This document explains the requirements and the design of security in PhotoXChange.
The security framework provides the means to authenticate users and control access to restricted resources based on user roles. Here, the authorization rules extend beyond those provided by the J2EE platform. Also, it should be possible to implement security aware functionality without being dependent on J2EE specific APIs. Hence a security framework is provided.
We assume that the application server and database environments are such that information cannot be altered by third parties.
To cover the authentication requirements the following design principles are used:
The authentication packages are org.wamblee.security.servlet and org.wamblee.security.tapestry. The first package contains general security related functionality for use in web applications. The tapestry package contains specific security related support for the Tapestry web application. An authorization framework is contained in the org.wamblee.security.authorization package. Security depends on user management.
The AuthenticationFilter is a servlet filter that takes care of executing requests as an authenticated Subject using Subject.doAs() when a Subject is available. The Subject is made available in the application server as part of the FORM based authentication. To retrieve the authenticated subject, the SubjectLocator is used. This interface has one implementation for JBoss that retrieves the Subject from the JBoss security manager. This retrieval is done using OGNL (Object Graph Navigation Language) to avoid dependencies on JBoss-specific classes in the code.
A subclass of BasePage called ProtectedPage is provided that represents pages that require an authenticated used. This class implements the PageValidateListener interface from Tapestry so that its pageValidate() method is called before the page is rendered. The pageValidate() method checks whether the user is logged in and if not throws an exception. This design is possible since J2EE declarative security is used in such a way that this situation can only occur in case of programming and/or configuration errors. .
There is no special class to represent a resource. The resource on which some operation is requested (or a representation of this resource) is used instead. The framework defines four operations for reading, writing, creation, and deletion, as well as one operation type which groups these four.
The result of an authorization is defined as an enumerated type with the values:
The basic ingredient for the authorization framework is a single authorization rule. To check whether an operation is allowed on a resource, a number of rules are consulted in a specified order using their isAllowed() method. A rule also provides an interface for getting the supported resource types to which it applies. In case a rule applies it returns GRANTED or DENIED, otherwise, it returns UNDECIDED or UNSUPPORTED_RESOURCE. An authorization rule must be persistent.
The UrlAuthorizationRule is a fully parametrized abstract base class for other concrete rule implementations. It is constructed with a number of rules to determine if a rule matches, as well as a result to return in case the rule matches. Specifically, for a rule to apply, the following conditions must hold:
To use the UrlAuthorizationRule, a subclass must be implemented which implements the getResourcePath() method to return the path for a given resource.
Two types of user conditions are implemented: AnyUserCondition which matches any user and GroupUserCondition which matches in case the user is part of a specified group.
Only one type of OperationCondition is implemented which checks if the operation is a subclass of a given operation type.
The authorization service provides the interface for authorization (isAllowed()) as well as methods for managing the authorization rules. The service works by checking each authorization rule in turn until one rule returns GRANTED or DENIED. If none of the rules match, then the service returns the result DENIED. The service therefore never returns any other result than GRANTED or DENIED.
The implementation of an authorization service that uses persistent storage for storing the rules is done in two steps:
This design separates the logic for implementation of the authorization rules from that of persistence and caching.
The authentication filter intercepts all requests to the web application. In case no user identity is available the request is simply forwarded. Otherwise, the user is logged in using JAAS and the request is forwarded using Subject.doAs().
The sequence diagram shows a scenario where an attempt is made to access a protected page. It obtains the Subject using JAAS API calls and if there is no subject, an exception is thrown.
To check whether an operation is allowed on a resource, the authorization service checks each rule in order until one rule returns GRANTED or DENIED. Thus the first rule that applies determines the result. If no rules match, DENIED is returned.