Web and Enterprise Architecture Design Patterns for J2EE, Part 2by Ganesh Prasad, Rajat Taneja and Vikrant Todankar
Editor's Note: This is the second article in a series in which the authors propose patterns in the following five categories: Partitioning, Scope, Security, Navigation, and Data Volume Control. Part 1 contained descriptions of the first two categories. This week, the authors discuss Security, Navigation, and Data Volume Control.
Most non-trivial web and enterprise applications have security requirements, and these fall into the following areas:
- Privacy/Encryption: a means to prevent unauthorized viewing of information.
- Authentication: a means to verify that a user is who they say they are.
- Access Control/Authorization/Entitlements: a means to check if a user is allowed to perform an operation or access a resource.
- Message Integrity: a means to ensure that data is not tampered with in transit.
- Non-repudiation (a corollary of Authentication and Message Integrity): a means to ensure that the user cannot plausibly deny having sent a message.
The security subsystems of most applications will need to provide more than one of the above mechanisms, and frequently all of them. Web applications do not generally implement encryption and message integrity mechanisms themselves, because the HTTPS/SSL technology incorporated into browsers and web servers is sufficient to provide these.
Most of the time, developers spend time and effort building authentication and access control subsystems, even though these features are ostensibly part of the J2EE specification. The reason for this wheel reinvention is that the standard J2EE security mechanisms are often inadequate for the purposes of many applications.
Standard authentication schemes typically return Boolean values to signify permission/denial, whereas an application may require further information, such as whether this is a first-time user, or whether the user's password has expired and must be changed. Similarly, authorization tags in EJB deployment descriptors control access to components, but are not fine-grained enough to enforce, for example, monetary limits on transactions, an essential requirement of many financial applications.
In other words, most attempts to reinvent the security wheel at the application level are aimed at going beyond the coarse-grained, black-or-white logic provided by the J2EE container.
1. Access Context/Nuanced Response
To provide the fine-grained and nuanced security advice required by real-world applications, which J2EE does not offer.
Problem and Solution
Given the need for an arbitrarily fine-grained, multi-valued response to authentication, session validation and access control requests, we propose a special design pattern for such situations. In this pattern, any request to a subsystem providing such services will be accompanied by a single parameter -- an "Access Context." The Access Context object will, broadly speaking, hold information about the user, the function they are attempting to perform, and an optional Value Object representing the parameters to that function.
The subsystem that is called will then analyze these parameters and return a nuanced response that contains more information than just "yes" or "no."
This Nuanced Response wrapper object may contain a status code, an optional qualifier, and an optional set of descriptions. The status code could be an unqualified yes. It could also be one of several values representing an unqualified no, or it could be one of several values representing a qualified yes. If the response is a qualified yes, the associated qualifier object will contain more details, and the client is assumed to know how to interpret these. The accompanying descriptions may similarly represent the set of all status messages that led to this final status. Exact implementations may vary in detail, but the outline solution is for all such calls to have a similar signature, i.e., a single Access Context parameter and a returned value that is a Nuanced Response.
The positive consequence of the Access Context/Nuanced Response pattern is a standard and straightforward mechanism that can be used without great effort once set up. It is capable of delivering adequately fine-grained authentication, session validation and access control for most applications.
The negative consequence of this pattern is the increased initial complexity of the solution. This complexity can be mitigated by having experienced developers code the initial implementations of its use, so that less-experienced developers can use them as templates.
To encapsulate the logic required to answer complex security and state-management queries.
Problem and Solution
An Advisor is our term for the component that accepts an Access Context and returns a Nuanced Response. The Advisor pattern, as we mentioned before, may be used for authentication, session validation, access control, and other similar functions.
For example, a simple way to implement access control functionality would be to delegate it to a specialized Advisor component that determines if a proposed action is permissible or not. The pattern is called Advisor because it does not enforce rules; it merely indicates what should be done by returning an arbitrarily fine-grained Nuanced Response. It is up to the calling component to act on the advice given. Trivial or non-critical business functions may perform their logic without calling an access control Advisor, for example.
The major positive consequence of the Advisor design pattern stems from its encapsulation of all verification logic in a single component or subsystem, which makes it easier to maintain. User roles, for example, can be managed entirely within an access control Advisor without having to be exposed to any other part of the application. An Advisor also acts as a facade for any rules engine it may use, allowing the application to start with a set of simple rules and expand it into a more intelligent mechanism as time goes by.
The negative consequence of this design pattern is that the application's rules are only enforced if components call the Advisor and act upon its advice. If a developer neglects to code a call to an access control Advisor at the start of a business function, for example, then the function becomes freely accessible.
To enforce the application of security and state management rules.
Problem and Solution
The Interceptor pattern is meant to address the drawback of the Advisor pattern by taking away the discretion of calling components to check and enforce rules. It uses a reliable mechanism to always perform such checks before control is passed to such a component. It is not a substitute for the Advisor pattern, but merely an add-on to ensure that the advice of the Advisor is binding.
The new, red-hot technology known as Aspect-Oriented Programming (AOP) is an extremely elegant way to implement the Interceptor pattern.
Obviously, the main positive consequence of the Interceptor design pattern is the increased assurance that rules will be complied with. It also takes away responsibility for rule enforcement from business components, making them simpler.
The negative consequences of the pattern may stem from the need to master new technologies that implement it well (such as AOP), although Interceptors can be coded using regular Java programming techniques, as well. Another consequence is that discretionary bypassing of access control checks (which may be justified in less critical cases, for performance reasons) may be harder to implement.
4. Need to Know
To pre-emptively block access to unauthorized business functions.
Problem and Solution
There is a design requirement often referred to as "pre-emptive access control." The idea is that if the user is not permitted to perform a certain action, the application should not provide them with the button or menu item to trigger it in the first place. They should only see what they can access.
The Need to Know design pattern is a kind of server-modified Client pattern, in which the server determines the functions that the user may perform and only generates the front-end components corresponding to those functions. (The process of determining which functions are allowed may, in turn, use Advisor and Access Context/Nuanced Response patterns.)
Sometimes, perhaps for marketing reasons, it may be required to display even functions that are not accessible, albeit in a disabled form. (This is a way of saying, "See what extra goodies you can access if you sign up for our Platinum membership?") The Need to Know pattern can support such a requirement as well, when used in conjunction with Advisor and Access Context/Nuanced Response.
The positive consequence of the Need to Know design pattern is a more natural and intuitive application. It locks the stable door before the security horse has bolted.
There is no real negative consequence to the pattern, unless the extra processing required to suppress unauthorized navigation capability is deemed wasteful!
To provide a convenient facade for a set of cooperating security components.
Problem and Solution
The Gatekeeper is a complex Security pattern, which is why we have saved it for last. It uses the Advisor, Access Context/Nuanced Response, and (optionally) Stateless Channel patterns to create a comprehensive security framework for an application. It is a specialized form of the GoF (the "Gang of Four": Gamma, Helms, Johnson, and Vlissides, authors of Design Patterns) Facade design pattern that is most useful for channel-independent business logic, such as that implemented by EJB.
The basic idea is that the entire logic of the application tier is hidden behind a Gatekeeper facade, which performs some coordination functions to simplify the design of other components.
The first contact of a client with the server occurs in the form of an authentication request to the Gatekeeper, which is the only externally visible component on the server. The authentication request contains a single Access Context parameter. The Gatekeeper consults an authentication Advisor, passing it the Access Context, and receives a Nuanced Response in return. The login is either allowed or rejected in an unqualified manner, or it is allowed in a qualified way (e.g., "First login -- change password", "Password expired -- force change", etc.).
If the response is an unqualified yes, the Gatekeeper then activates a State Management component and gets it to generate a session token, which it then passes back to the client as part of the Nuanced Response. Otherwise, it merely returns the original Nuanced Response. Whenever it is called, the State Management component records the session and its expiry time.
(Note that the session token can be managed using the Stateless Channel pattern from this point on.)
Subsequent accesses from the client are to the Gatekeeper as before, but they now consist of requests to business functions. All of the associated Access Context objects must now contain the session token, in addition to the Value Object that holds the parameters to the business function. The Gatekeeper consults a session validation Advisor using this Access Context, and the Advisor returns a Nuanced Response specifying whether the session is valid, invalid, or expired. The session validation Advisor is just an interface implemented by the State Management component. If the session is valid, the State Management component resets the expiry time of the session to keep it alive.
If the session is valid, the Gatekeeper presses ahead with the business function; otherwise, it returns the Nuanced Response forthwith to the client.
It is possible to implement an Interceptor pattern at this stage by having the Gatekeeper consult an access control Advisor before deciding whether to call the business function. If the Advisor says no, the Gatekeeper returns the Nuanced Response to the client and doesn't call the business function at all.
Another option would be to call the business function straightaway and let it decide whether to make the call to the Advisor or not.
Either way, the Gatekeeper acts as the go-between for the client and the business function, and the access control Advisor is called some time before the business logic is executed.
Note that the business functions can themselves implement the Advisor pattern, in that they can advise the Gatekeeper (through a Nuanced Response) on whether they succeeded or not, and if not, what kind of error or warning condition they encountered. The Gatekeeper will then return the Nuanced Response object returned by the business function. This is, in fact, the most natural way to code the application's business functions, once the Advisor/Access Context/Nuanced Response framework is in place.
In the J2EE context, the Gatekeeper and all Advisors are best implemented as Stateless Session Beans. The Gatekeeper is the only EJB that will have a remote interface. Other EJBs (security-related as well as business-related) will only have local interfaces. This ensures that external clients can only see the Gatekeeper. They can never directly access any other component. The security tags in the deployment descriptor can be used to enforce the rule that all access to other components can only occur through the Gatekeeper. The additional checks performed by the Gatekeeper will provide further security around the application's business functions. This combination of container-level and application-level security can be very effective.
We can see that interposing a Gatekeeper facade between clients (or channels) and application logic makes the design much more modular by clearly delineating responsibility for various security and business functions, and imposing a standard and consistent calling convention. It is highly secure, and caters to all of the major security concerns outlined at the beginning of this section (assuming that SSL is used to ensure privacy and data integrity). This is the major positive consequence of the Gatekeeper pattern.
The major negative consequence is that the relative complexity of this set of cooperating components makes it overkill for simpler applications. But if an open source implementation is readily available (we hope to provide one), it should be relatively simple to incorporate it into any application and enjoy its substantial benefits.
Pages: 1, 2