Reduce maintenance costs from security logic that is interwoven with application logic. Apply a simple design that lets you plug in a role-based access control component.
It is common for many software professionals to see code snippets that check whether a user has an assigned value or a specific role enabling the user to access a screen, a report, or data. However, these types of checks are not application logic; rather, they are authorization requirements that must be kept independent of the application domain. Common issues that arise when designing an application include application security becoming interwoven with the application, lack of a single location where all of the application security can be maintained, and changing compliance needs such as Sarbanes-Oxley and other legal requirements and internal controls.
As a result of these problems, application code must be changed even if the application's functionality doesn't change, which results in an application's higher maintenance cost and no clear separation of application logic from security logic.
A solution to these problems requires some preplanning and a simple design that also serves as prevention in the future. The design is a role-based access control (RBAC) security component, which decouples security or authorization logic—note that security and authorization are used interchangeably here—(see the sidebar, "Security Domain Terminology").
This simple XML structure can be loaded during system startup and stored in the AccessPolicy class. AccessPolicy is a singleton class that can be instantiated either on first invocation or at system startup (see Figure 1). This class encapsulates the policy set. The isAuthorized() operation accepts parameters such as resource name (resource element from the XML), action, and the list of roles assigned to the user. This method returns one of the three AuthorizationConstants.
setRoleScope(): A method that allows the user to set a role scope at the beginning of the session. If this method is set, then all the authorization requests will be processed only for the set role(s); otherwise, setRoleScope() will automatically assume the combination of all roles assigned to the user.
assertAccess(): This method is invoked to obtain authorization. The assertAccess() method calls the AuthPolicy.isAuthorized() method to obtain the authorization. It returns void if the user is authorized to access the resource. If the user is not authorized, then a SecurityException() is thrown, which maps to a global error for security violation. If the isAuthorized() call returns conditional access, then it calls the assertAccessCondition() method.
getAuthorizations(): This method returns the authorization for all the child resources under a resource.
getUserInfo(): A private method that is used to retrieve user information, such as roles assigned to the user. This information could be obtained from an application table or from the corporate directory.
assertAccessConditions(): A private method invoked internally from the assertAccess() method. This method calls one of the many AccessCondition classes based on the resource or object from which accessCondition must be evaluated.
logAccess(): A method used to log the results of the assertAccess call. This information can be used later for audit purposes, including Sarbanes-Oxley compliance and access reviews required by legal or internal procedures.
IAcessCondition (see Figure 1) is an interface that defines the assertAccessCondition() method to be implemented by AccessCondition implementations. It accepts the AuthRequest and AccessConditionValues, which are represented by a collection. The method returns void when a user is authorized for access; otherwise, it throws security exception.
The AccessCondition class (see Figure 1) illustrates that one or more varieties of AccessCondition classes must be implemented to handle these scenarios for various groups of resources or objects. The AuthorizationFilter class (see Figure 1) calls the AuthService to obtain the authorization for a user. This process removes the need of an authorization check from within the application code.
This simple design will allow applications to have the capability to plug in a simple RBAC-based component into an existing application. The result of this design is a clean application that is code free of authorization calls intertwined with checks for access conditions, access control logging required by legal compliances, and a policy set that can be maintained as configuration without changing application code.
These functionalities are beyond the scope of this particular discussion; however, readers can easily extend or add them: a Web service providing remote access to the authorization service, a Web application built to centralize the policy structure into a database, support group structures by adding a few additional classes to the components, and load policy that is set dynamically upon change by building the mechanism. Note that an active user session might pose certain problems.
This design doesn't work with group structures and assumes that the group structure and roles available to the group membership are resolved before calling this service. Also, this design doesn't provide a centralized repository or user interface to maintain policy information. Note also that this design is specific to application architecture; therefore, the details of retrieving user roles and information from the database or the directory (LDAP) were not included.