Fine-grained Java EE authorization using Enum-based access control lists with EAz: Part 1: The problem space and EAz architecture

With the advent of the Java™ 5 ENumSet and Enum-based Authorization (EAz), it is now possible to implement an efficient and easy-to-maintain Java access control list framework for fine-grained control over application resources. This content is part of the IBM WebSphere Developer Technical Journal.

Robert Patt-Corner (rpatt-corner@proqual.net), Chief Technical Officer, Proqual-IT

Robert Patt-Corner architects, designs, and develops enterprise applications for business areas including healthcare, document management, alerting and workflow services, and predictive information gathering. He is Chief Technical Officer at ProQual-IT, focusing on service-oriented architectures. Previously, he was Chief Technical Architect at Noblis (formerly Mitretek Healthcare), leading the team that developed an automated, Web-based healthcare alert service. Robert's certifications include WebSphere Administration, Rational Application Development, and Lotus Administration and Development. You can contact Robert at rpc@dorsetwest.com or rpatt-corner@proqual.net, and you can view his Web site at www.dorsetwest.com or www.proqual.net.



Keys Botzum, Senior Technical Staff Member , IBM

Keys Botzum is a Senior Technical Staff Member with IBM Software Services for WebSphere. Mr. Botzum has over 10 years of experience in large scale distributed system design and additionally specializes in security. Mr. Botzum has worked with a variety of distributed technologies, including Sun RPC, DCE, CORBA, AFS, and DFS. Recently, he has been focusing on J2EE and related technologies. He holds a Masters degree in Computer Science from Stanford University and a B.S. in Applied Mathematics/Computer Science from Carnegie Mellon University. Mr. Botzum has published numerous papers on WebSphere and WebSphere security. Additional articles and presentations by Keys Botzum can be found at http://www.keysbotzum.com, as well as on IBM developerWorks WebSphere. He is also co-author of IBM WebSphere: Deployment and Advanced Configuration.



03 October 2007

Also available in Chinese

Introduction

Java developers attempting to exert fine-grained control over access to application resources quickly reach the limits of built-in Java Platform, Enterprise Edition (Java EE) declarative authorization. Developers typically attempt various solutions, including Java EE programmatic authorization, external frameworks like Acegi, various ad-hoc extensions to Java EE's built-in declarative authorization model, and implementations of binary access control lists (ACL).

Often the external frameworks lack sufficient capability or impose too heavily on the built-in Java EE authorization and authentication model, forcing developers to discard a great deal of the useful native functionality of Java EE. Until now, home-grown ACL frameworks required complex and potentially fragile or difficult-to-maintain binary arithmetic implementations in order to achieve sufficient efficiencies in a large scale implementations.

Java 5 introduced the Enum, EnumSet, and EnumMap entities, officially billed as a solution to a quite different problem: a way to work with bounded sets of discrete values. In the process, the developers of Java 5 implemented the Enum and EnumSet and map as an efficient binary entity. This makes it remarkably easy for developers to construct an easily understood and maintained implementation of the classic ACL using only Enums and their extensions.

The Enum-based Authorization (EAz, pronounced 'easy') series describes how to implement a complete ACL implementation in Java 5, including discrete binary permissions, arbitrary named sets of permissions, methods for applying and rapidly testing access control on protected resources, and a means to display the truth table of a particular access control using the JavaServer™ Faces (JSF) capabilities of IBM® WebSphere® Application Server V6.1 and IBM Rational® Application Developer 7.

This first of three articles describes the requirements and overall architecture of the EAz solution, including interfaces for authorization checks, context-aware access control mechanisms and an enum-based configuration class. The next article covers the code and implementation details of each class, and the final article illustrates display integration with existing Java EE authorization in a Web-based JSF application by implementing a JSF solution to the difficult problem of using a Web application to graphically represent which discrete permissions are available to a user or group in a Web framework.


Java EE authorization: What's available in the standard

The Java EE specification offers substantial declarative and programmatic authorization capabilities (see Resources). Let's begin by covering enough of Java EE authorization to understand the built-in capabilities of a Java EE 5 container, then examine several application areas where authorization requires capabilities beyond those in the standard.

The Java EE authorization structure manages relationships between secured application resources, like pages and methods, application-specific Java EE roles, and group memberships. Figure 1 outlines the key relationships, simplified mildly.

Figure 1. Java EE 5 authorization structure
Figure 1. Java EE 5 authorization structure

Starting from the bottom:

  1. Application resources like EJB methods and Web pages can be declared to require authorization for access by means of security constraints in deployment descriptors. (There's a layer of indirection between roles as used in code and roles as surfaced to deployment, but it's not germane to this discussion.) Security constraints are mapped to the Java EE roles needed to satisfy authorization in the descriptor itself.

  2. While the Java EE specification does not specify how, role memberships are typically mapped at deployment time to groups or users from some user registry. One role might be granted to multiple groups or users (principals); one group or principal may have multiple roles mapped to it. User principals also acquire role mappings by virtue of the groups to which they belong. The flexibility of role-to-group or principal mapping is a major strength of the Java EE authorization framework.

  3. When a principal attempts to access a resource protected by a security constraint, the Java EE security implementation examines the mapped roles one at a time, until either an available role grants access to the resource or all roles have been exhausted. The first role to permit access returns success; if no roles are allowed access, then Java EE authorization returns failure.

Moving beyond declarative authorization to Java EE programmatic authorization

Declarative authorization relies entirely on the deployment descriptor constraints for authorization policies. Descriptors such as the web.xml file of a Web application or the application.xml of an EJB component completely define the declared protected resources and roles authorized to access them.

Groups only come into play at actual application deployment; one site running the application might map one set of groups and named users to a role, and another site might map quite another set of groups and users.

Declarative authorization's up-front mapping makes it easy for administrators to understand, maintain, and modify security policies, but are too rigid for even simple scenarios where, for example, a section of code secured by one role contains an additional section that requires an additional role for authorization. It's certainly possible to factor code to adapt to this lack of fine control, but eventually the rigidity of declarative authorization will begin to warp and complicate the application's structure.

Java EE programmatic authorization partly alleviates the problem by introducing an element of dynamic authorization in code. Programmatic authorization makes use of the same security structures of roles, secured resources, groups, and named users as Java EE declarative security, but enables a programmer to check for access at run time in an application-specific manner. Java EE programmatic authorization is invoked directly from the application code by means of four security-aware method invocations -- two for use in Web applications and two for use in EJBs:

  • isCallerInRole (EJBContext)
  • getCallerPrincipal (EJBContext)
  • isUserInRole (HttpServletRequest)
  • getUserPrincipal (HttpServletRequest)

You will typically use programmatic authorization when multiple roles are allowed access to a declaratively secured resource like a page or a method, but only a subset of the roles should be allowed access to a specific subset of functionality.


Limitations of Java EE authorization

Given the substantial Java EE authorization capabilities outlined above, many developers manage perfectly well without the need for additional authorization capabilities. However, there are significant gaps in Java EE authorization that will require functionality beyond the Java EE security specification.

Fine-grained permission assignment

Let's use the term permission to mean the finest unit or grain of authorization we care about in a particular application; for example, the ability to assign a single stage in a complex workflow, or to read or edit a single field or group of fields. We'll use the term resource to refer to the finest grain of functionality that we can secure with a permission; for example, a Web page or EJB method call.

Java EE's finest unit of permission is the Java EE role. Each role must be defined in the deployment descriptor of each Web or enterprise application, and security constraints must be created to secure each authorization-controlled resource. In addition, an administrator (or application deployer) must assign each role to the appropriate groups and users at deployment time, and maintain the mapping as groups and user change over time.

Java EE's finest unit of authorized resource is the method or URI for declarative authorization, or the block of code for programmatic authorization. The problem is that a Java EE role or an ad-hoc programmatic test on a code block is not well suited for fine-grained permission control. Theoretically, there is no problem in using Java EE for fine grained permissions. The application developer must define a Java EE role for each permission in the application and maintain the roles in deployment descriptors over the life of the application, and/or test for application-specific role memberships in code blocks. Likewise, the deployers must maintain the individual permission-to-group or principal mappings over the life of the application.

As the overall enterprise application grows though, with more and more Web applications, and more and more EJBs (each of which requires individual deployment description and mapping), the maintenance effort and opportunity for error multiplies. Authorization becomes a thicket of XML statements and program code; opaque to the analyst and a stumbling block to reconfiguration and deployment. (Java EE 5 also permits annotations to declare security constraints in code, but this capability makes the situation more complex in some ways by eliminating a central repository for declarations.)

As a result, developers almost always map Java EE roles to bundles of related permissions at a coarser granularity. Web applications typically define single roles to grant access to bundles of Web pages represented as URI patterns. The security constraint in Listing 1, for example, secures all URLs beginning with "/reports/" with a single role called reportEnabled, which can be mapped to multiple groups at deployment.

Listing 1. Typical multiple URI mapping to a single role
<security-constraint>
<display-name>View Reports</display-name>
<web-resource-collection>
	<web-resource-name>All Reports</web-resource-name>
	<url-pattern>/reports/*</url-pattern>
	<http-method>GET</http-method>
	<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
	<role-name>reportEnabled</role-name>
</auth-constraint>
</security-constraint>

If you want different permissions to run different groups of reports according to differing roles, the declarative security would probably still be maintainable, but if an application required fine-grained access to individual reports, you would need to define and maintain one of these security constraints for each report element you needed to secure. The declarative aspects of Java EE will begin to break down under the weight of the complexity, particularly as developers and deployers are called on to maintain the application over time.

Java EE programmatic authorization to add granularity

Java EE programmatic authorization can add some granularity to Java EE-provided declarative authorization. Building on the reports example, it is possible to enable access to a reports page or method for all users in the reportEnabled Java EE role, then to authorize or de-authorize certain aspects of reporting depending on a second role based on programmatic role tests. Listing 2 shows how a method secured by the reportEnabled role prints some extra reports for principals with the extraReports role:

Listing 2. Extending reports with Java EE programmatic security
printUsualReports();	
    if (ctx.isCallerInRole("extraReports")) {
      printMoreReports();
    }

Programming hand-coded tests for role membership definitely adds some flexibility and granularity to your authorization capabilities.

Limits to programmatic authorization

The flexibility gained through programmatic Java EE authorization is limited in scope and bought at a high price. A developer can authorize or deny arbitrary functionality as needed, but the application's authorization policy is now partially or completely embedded in its code. An analyst will need to understand the initial declarative authorization, the extensions by programmatic authorization, and the interaction of the two. Understanding and maintaining the authorization policy will be an order of magnitude more difficult, even if the code is free from defects. Development techniques that carefully outline and control the places and ways that programmatic authorization is used can help control the complexity. However, the problem of complexity and consequent need for careful structure isn't unique to Java EE authorization; it is characteristic of programmatic authorization in general.

The larger problem is that Java EE programmatic authorization doesn't offer benefits commensurate with the complexity it introduces. Java EE programmatic authorization depends entirely on the same deployment roles as declarative authorization, and suffers from the same problems of granularity -- Java EE roles map well to groups of permissions but poorly to individual permissions. The other problem with Java EE roles is that the roles are intrinsically static structures, and cannot be adapted to application-specific contexts or relationships at run time.

On one hand, the static nature of Java EE roles is a good thing for maintainability. An application using only Java EE authorization has an authorization policy completely defined by its roles, authentication constraints, and programmatic authorization calls. But this also means that a given Java EE role maintains the same set of permissions for all principals associated with it regardless of external associations. An application cannot use a single "Report Reader" role to authorize users in one organizational unit to access Reports A, B, and C and users in another to have access to reports A, D, and F, even though the business meaning of the role is identical in both cases.

Instance versus class authorization

Most authorization systems, including Java EE authorization can secure access to an entire class of resources; for example, a group of Web URLs that match a pattern or all methods on an interface. Instance-based authorization is more difficult. For example, in Java EE, you can easily express whether a particular method on a particular EJB can be called or not, but what you can not express is whether a particular instance of that EJB can be used by a particular party. Imagine, for example, that there is an account EJB that has a transferFunds() method. Using typical Java EE declarative authorization, the constraint that says "you can only transfer funds from specific accounts" cannot be expressed unless you are willing to create a unique EJB type for each account. Clearly, this is not feasible. Often the instance that needs authorization isn't a Java EE artifact at all, but a domain object instance that is specific to the application, such as an individual bank account.

What you need is a more powerful model of authorization that is more expressive without being unduly complex.


Moving beyond Java EE authorization

Let's introduce an example application to illustrate an easy-to-understand authorization problem that requires more resources to solve than Java EE can offer. Imagine a "virtual campus" application, with several of levels of access:

  1. An overall campus and its public areas.
  2. A variety of buildings and open meeting spaces in the buildings of a campus.
  3. Private offices in the buildings.
  4. Special secure spaces in the buildings.

The authorization model for such an application is pretty much the same as for a physical set of buildings and rooms of a similar nature -- in fact, you could apply the authorization model to a physical campus by adding access cards and sensors.

Here is the authorization model for this virtual campus application:

  1. Users with entry level authorization on a given campus can enter the campus and perform some default operations in the public spaces in the campus.
  2. User with entry level authorization for a particular building may enter the specific building and the public meeting spaces in the building.
  3. Users with entry authorization on a particular private office may enter the office.
  4. Users with authorization to enter specialized secure spaces (for example, artificial life labs), may enter the spaces.

The example authorization model is very simple in that it only defines a single permission, the entry permission. Even so, it would be very difficult to implement this model in a maintainable way using only Java EE declarative and programmatic authorization. If you add even one more permission to the model (for example, the permission to grant others entry to a space), the model becomes far too difficult for Java EE authorization.

We'll use the requirements from this example as a conceptual benchmark to explore authorization capabilities. The general capabilities underlying this example model are:

  1. Manage permissions and groups of permissions at a finer grain than Java EE roles, and in a way that can be easily and safely configured, understood, and maintained by an administrator.
  2. Introduce a way to authorize resource access such that it can be directly driven by the application's domain model -- specifically by means of authorization for campuses, buildings, rooms, and so on.
  3. Scale to manage thousands of campuses, up to 100,000 named users, and hundreds of permissions.
  4. Coexist with and extend ordinary Java EE declarative and programmatic security so the existing authorization policies can continue to operate undisturbed where necessary.

It would not be practical to provide authorization for the virtual campus example using pure Java EE declarative and programmatic authorization; the code would rapidly descend into an unmaintainable mess of IF-THEN blocks and identity checks.

The Acegi solution

The closest we could find to a ready-made framework for fine-grained configurable Java EE authorization is a combination of Spring (see Resources) and Acegi; Spring providing the ability to treat authorization as an aspect , injecting security authorization into methods as required, and Acegi providing the actual authentication solution. In the words of its creator, Acegi is:

...a powerful, flexible security solution for enterprise software, with a particular emphasis on applications that use Spring. Using Acegi Security provides your applications with comprehensive authentication, authorization, instance-based access control, channel security and human user detection capabilities.

Two passes through the documentation and a little experimentation suggested Acegi was all it claimed to be. It looked like a viable solution for our generic requirements, but with two rather major exceptions:

  • Acegi doesn't really plug into or augment Java EE authorization; it replaces Java EE authorization, and Java EE authentication as well. In the interest of maintaining existing standard functionality, we don't think it wise to completely bypass the Java EE-based security mechanisms, particularly the authentication mechanisms which are unrelated to the authorization problem at hand.

  • Acegi had, at least at the time we reviewed it, some scalability limitations. Permissions were based on masking a 32-bit integer -- the example application and requirements would have needed at least 64 bits to handle the required atomic permissions.

The first issue -- completely replacing Java EE authorization and authentication -- was really the main sticking point. Authorization and authentication are fundamental mechanisms in an application, and discarding both for a custom implementation strikes us as a risky choice. First, there's the usual risk of continued support and development for a third party framework. But perhaps more important, there's the opportunity cost of missing enhancements down the road. Lost opportunities aren't just limited to Java EE changes in authentication and authorization; replacing Java EE security is likely to interfere with both Java EE and vendor extension enhancements that assume Java EE security as a foundation.

Custom solutions

The primary risk of developing a custom authorization solution is the reasonably high likelihood of creating an unmaintainable mess of spaghetti security code, worse than the original problem. It would be easy, for example, to proliferate roles that were essentially the same, or mix Java EE programmatic authorization tests with custom tests in code to obtain the desired authorization result in a particular case, but it would quickly become impossible to understand and manage the combined impact of all the cases.

Good practices help to mitigate the risk; for example, treating security as an aspect and isolating authorization decisions from application code. Good design can ensure that custom authorization behaves like a "black box," an opaque API that can be called on from application code when decisions need to be made that go beyond the capabilities of the Java EE programmatic authorization model. Designing extended authorization as an interface gives some independence from implementation as well. Requirements were developed to mitigate the risks involved in a custom authorization solution. Specifically, a custom solution must:

  1. Be addressed as a simple interface that is suitable for use as a cross-cutting aspect of an application.
  2. Leave existing Java EE declarative and programmatic authorization intact and operational.
  3. Be authentication-agnostic, adaptable to both standard Java EE authentication and custom authentication.
  4. Be able to authorize access to domain objects of an application, not just Java EE artifacts.
  5. Be scalable, easy to maintain, and -- especially -- very fast. Authorization is invoked frequently and across a wide footprint of code. Any bottleneck in authorization is likely to have a substantial and widely dispersed impact on system performance.
  6. Be general enough to have wide applicability to many application domains.

Access control lists and permission sets

There is one area where fine-grained authorization models are ubiquitous and well-understood: the operating system. Nearly every modern OS makes use of a bit-masking permission-based model that deals with the control of resources by principals, based on principals and groups. In one sense, Acegi is an attempt to apply OS-like access control lists to the Java EE environment.

Access control lists (ACLs) are objects with an owner and an indexed list, or hash, as illustrated in Figure 2.

Figure 2. Basic structure of an access control list
Figure 2. Basic structure of an access control list

An access control list is a list of permissions or sets of permissions securing a resource. Each entry in the ACL's list of permissions grants a set of permissions on the resource to a principal or group. Nearly all modern operating systems use ACLs to authorize access to resources. Figure 3 shows the graphical display for an ACL on a file managed by the Linux® operating system:

  • The ACL resource and owner is the file called dekihost.
  • The ACL's list contains three permission sets:
    1. The first entry is indexed by the principal "rpc," granting read and write permissions.
    2. The second entry is indexed by the group "admin," and grants all members of the group the same two read and write permissions.
    3. The third entry is indexed by a virtual Linux operating system group called "others." Any principal that is not rpc or a member of the admin group is considered a member of "others" and is granted the single read permission only.
Figure 3. Graphical representation of a Linux file-based ACL
Figure 3. Graphical representation of a Linux file-based ACL

Talking about ACLs in a precise way can be difficult. One reason is that the language surrounding the resource secured by an ACL tends to become associated with the ACL itself. In this file example, the owner of the file is the rpc principal. File ownership is a Linux operating system construct. However, the owner of the ACL is not rpc, but the file itself, dekihost, the resource that the ACL secures. For the purpose of this article, the "owner of an ACL" will refer exclusively to the resource that the ACL secures.

The scope of an ACL

An ACL grants sets of permissions to principals or groups -- but what are the targets or scope of the permissions? The scope of an ACL is at least the ACL owner. If rpc has write privileges, by virtue of being listed in the Figure 5 ACL, then it is the dekihost ACL owner to which he can write.

But scope doesn't necessarily stop at the ACL owner. ACL scope has some interesting and useful implications, even in the relatively simple and standard case of file systems. For example, an ACL for a folder is almost the same as one for a file. However, most operating systems implement a folder-related rule governing how a new file's ACL is created.

Figure 4 shows a folder ACL that gives the rpc principal the create file and delete file permissions, plus one additional permission: the ability to read files in the folder. This means that once rpc has created a file, he cannot modify it; the ACL implements a write-once rule for the folder. There are two important points to note about this example:

  • From an ACL perspective, there are four individual permissions in rpc's permission set, not one set of two folder permissions and another set containing one file permission, as the GUI implies. The Linux GUI merges file system concepts with ACL concepts, breaking up the folder permissions and the file access permissions in two graphical widgets, but the underlying structure of the ACL grants rpc four permissions: List files, create file, delete file, and read file.

  • The ACL knows nothing about the containment relationship between folders and files; the ACL simply lists who can do what within its scope. The containment relationship, and hence the scope, is implemented by the operating system. This last point is particularly important for ACLs in applications; it means that ACLs not only secure their resource, they can secure other objects related to their resource in an application-specific way.

Figure 4. ACL permissions set
Figure 4. ACL permissions set

To sum up, the scope of an ACL is at least the resource it secures, however the scope can be extended by an application in order to implement a designer's desired authorization model.

An illustration from the virtual campus

Let's illustrate ACL scope using the virtual campus application example. Recall that one of the authorization rules stated that a user authorized to enter a particular building on a campus was also authorized to enter any public space in the building. You can implement this rule with a single ACL on each authorization-controlled building, granting users and groups in the ACL the ENTER permission. You can then extend the scope of the building's ACL to include all public spaces associated with the building in the application. You can extend the scope of a building's ACL very efficiently, and without having to maintain ACLs for every public space in the building. Let's assume you write an authorization service interface that looks like this:

public boolean permit(Object permission, Object doorway);

In the virtual campus domain model, you associate every doorway with a building. In the permit() implementation, you look for an ACL for the doorway in question; in this case, a doorway to a public space in a building. The public door ACL does not exist, so you fall back on the ACL of the parent. You can do this hierarchy traversal recursively; that is, continue to look for a parent's ACL until you find one. Once you find an ACL, examine the current user's identity and groups, and see if they are listed in the ACL, and if their listing has the ENTRY permission. If found, grant authorization, otherwise deny.

Notice how powerful this example is: the recursive nature of the ACL lookup enables you to define broad areas of authorization with a single ACL, and define exceptions at any level of an unambiguous hierarchy. For example, you can have a public area containing a private area; a user with only building ENTRY will succeed on the public area based on the building ACL, but fail on the private area entry because the private area will override the parent ACL with its own more restrictive ACL.

First steps

Our initial ACL implementation approach took a path roughly similar to that of Acegi: we considered a collection of long variables for the bitmask instead of an integer in order to implement more permissions than Acegi allowed. There was no single disqualifying problem with the bit manipulation approach, but it did present challenges in a couple of areas:

  • Bitwise logic is complex for ordinary programmers and analysts. Therefore, the code that implemented the lower levels of the ACL implementation would require exceptionally careful and intricate testing and very good isolation from application programmers.

  • Storage and retrieval of the bitmaps was going to be complicated, and seemed likely to introduce database vendor dependencies in terms of storing and retrieving the bitmaps quickly and in a maintainable way.

Switching to enum-based authorization

While work proceeded on custom authorization, we were working independently with standard enums from Java 5. Enums are a generic concept, not tied to a particular language, and represent named constant values, usually as an enumeration of members of a discrete set. The Java examples use a playing card class as a primary illustration, creating enums for each suit and rank, with each card receiving a discrete, constant, named value from a Suit enum and a Rank enum:

Listing 3. Excerpt from a Java 5 enum example
public class Card {
    public enum Rank { DEUCE, THREE, FOUR, FIVE, SIX,
        SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE }
    public enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES }

    private final Rank rank;
    private final Suit suit;
    private Card(Rank rank, Suit suit) {
        this.rank = rank;
        this.suit = suit;
    } ...

Java 5 offers several collections classes for Enums, and one day a sentence in the EnumSet javadoc jumped out:

Enum sets are represented internally as bit vectors. This representation is extremely compact and efficient. The space and time performance of this class should be good enough to allow its use as a high-quality, typesafe alternative to traditional int-based "bit flags." Even bulk operations (such as containsAll and retainAll) should run very quickly if the specified collection is also an enum set.

The JavaDoc indicated that, in their substantial wisdom, the Java 5 implementors created EnumSets that could easily represent collections of discrete permissions, and that were implemented in exactly the high-performance bit vector format we were planning to construct and test by hand.

We discarded the hand-crafted implementation of permission set bitmaps in favor of EnumSets. Creating EAz using Enums and EnumSets in lieu of custom bit vectors turned out to be quite straightforward. The overall concepts, classes, service interface, and dynamic behaviour will be discussed in the remainder of this article, then move to implementation details in the next.


Introducing EAz

EAz is an Enum-based authorization extension for Java EE authorization. EAz relies on the bitmap-vector implementations of the Enum collections in Java 5 and beyond to implement a simple ACL-based approach to securing Java EE resources in situations where standard Java EE declarative and programmatic access are insufficient to meet an application's business requirements. EAz requires Java 5 or later to operate because of its reliance on the Java 5 Enum classes.

EAz concept of operations

EAz's main job is to define and implement an interface that any application's code can query to authorize access to application resources.

EAz requires an application to implement an ACLManager interface, which is responsible for retrieving a desired ACL for a secured resource from a persistent datastore or cache. The ACL should be persisted and retrieved using ordinary database types, such as varchar or string.

EAz implements ACLs as an ACL class, with permit() methods to check authorization. EAz sample code is available showing how to translate database strings to and from EAz enum format.

EAz models collections of permissions as a PermissionSet interface, an arbitrary collection of individual binary permissions. In the demonstration implementation, all permissions in a PermissionSet are required for successful authorization and there is no provision for deny-type permissions.

A Permissions class with a static initializer provides a single location for declaring and configuring available application permissions. The initializer assembles an AllPermissions EnumSet. AllPermissions contains each permission available in the application using EAz. The Permissions class also provides for an AllActions EnumSet which offers a mapping of application-specific actions to permission sets. This gives EAz the ability to supply "well known" permission sets to an application's service layers by action name.

An application implementing EAz typically uses Permissions, the ACLManager, and an ACL as follows:

  1. An application service wanting to access a resource obtains the resource's ACL from the ACLManager using the resource identifier as a key.
  2. The application service retrieves the action it intends to authorize from the Permissions AllActions EnumSet and uses a static Permissions.getPermissionSetForAction(theAction) method to retrieve the permission set of permissions needed to carry out the action in the application.
  3. The application invokes the ACL's permit() method to determine authorization, passing in identity information in the form of a group or a user identifier. A Subject can also be used as a default source for group and identity information. In this example, we use WebSphere Application Server APIs for obtaining group information, but the concept is generic and should be applicable to any application server.
  4. The ACL.permit() method returns silently on success or throws a PermissionViolationException on failure.

Figure 5 shows a sequence diagram of the typical EAz authorization process.

Figure 5. Typical EAz authorization sequence
Figure 5. Typical EAz authorization sequence

Finally, EAz provides a convenient AuthorizationManager interface to wrap the details of the typical interaction and provide a simpler interface to the application's service layer.

The ACLManager

Figure 6 shows EAz's ACLManager interface, consisting of a single public getACL() method.

Figure 6. ACLManager interface
Figure 6. ACLManager interface

Applications using EAz must implement ACLManager, retrieving the ACL that secures a resource from cache or persistent storage. You may recall that retrieving and persisting ACL bitmaps was an initial maintenance and database portability concern. The EAz sample implementation demonstrates how Enum-based access control lists can be persisted and reconstituted from simple strings, retaining all the performance benefits of bitmaps once the ACL is retrieved. In a production implementation, we recommend caching retrieved ACLs in a distributed cache to avoid any performance penalty in retrieval; this can be done through configuration if you use an Object-Relational Mapping (ORM) framework like Hibernate for your persistent storage access.

Figure 7 shows the critical portion of our example Data Access Object (DAO) code, demonstrating how to retrieve and reconstitute ACL information from strings stored in a properties file. We used a properties-based example to simplify the downloadable EAz code; you will want to implement more appropriate database table persistence in your own application.

Figure 7. Code segment illustrating how ACL's can be reconstructed from persisted strings
Figure 7. Code segment illustrating how ACL's can be reconstructed from persisted strings

The essence of the EAz persistence technique is in section 4, where the Enum valueOf() method turns a string into its Enum representation.

The ACL interface is shown in Figure 8. An ACL holds the permissions allowed to groups on a resource. It's main dynamic function is the permit(...) method, which determines whether authorization succeeds or fails based on whether the permissions requested by the application are available for the group provided to the ACL.

Figure 8. ACL interface
Figure 8. ACL interface

EAz internal structures and configuration

Before diving into code in the next article in this series, let's take a look at the structures of the PermissionSet interface and the configuration class Permissions. A consideration of PermissionSet and Permissions will lead us to look at the enum structures that underpin the EAz implementation.

Figure 9. The PermissionSet interface and the Permissions configuration class
Figure 9. The PermissionSet interface and the Permissions configuration class

The PermissionSet interface

The PermissionSet interface (Figure 9) is a collection of individual permissions. The interface defines methods to add permissions to an instance of the PermissionSet and query the set to see if it contains either a single individual permission or another PermissionSet as a subset.

Notice that the permissions referenced in the addPermission and hasPermission methods are members of the static AllPermissions enum type, maintained in the Permissions configuration class. AllActions and AllPermissions enums will be discussed as part of the Permissions configuration class discussion.

The Permissions configuration class

The Permissions configuration class functions as a container for static enum types that configure the permissions in a particular application using EAz. Permissions is never instantiated -- it features a static initializer that builds up the enum types that configure permissions for the application.

As Figure 9 shows, Permissions creates two static enum types: one called AllActions and the second called AllPermissions. AllActions is public, and defines the actions that can be authorized in the application. The enum constants in AllActions are effectively the names of a configured application's commands. Enum constants of AllActions can be referred to from anywhere in code, for example: Permissions.AllActions.ACTION_WHATEVER. Declaring an application's authorized actions in advance brings a measure of structure to an EAz-authorized application and improves its maintainability.

AllPermissions is another static enum type, but unlike AllActions, it is a protected internal member of the org.eazacl.acl package and is not visible at the EAz service layer or at the application level.

The enum constants in AllPermissions represent the unary permissions that actually control authorization. These will be referred to as the application's "permissions."

The Permissions class builds up static maps that relate each action to the PermissionSet that a principal must hold to access the action. In this way, the action enum constants in AllActions become both the names of application commands and enum constants used to locate the PermissionSets that the PermissionManager uses to authorize the commands.

EAz applications call the public static method Permissions.getPermissionSetForAction() to retrieve the PermissionSet required for any action in AllActions.

The AuthorizationManager interface

Figure 10 illustrates the AuthorizationManager interface. AuthorizationManager is a thin wrapper around the ACLManager and ACL to simplify application access to EAz authorization. Instead of manually querying the ACLManager for the ACL securing a resource and then querying the ACL for authorization, an application can simply use the AuthorizationManager's authorize() method to handle the details instead.

AuthorizationManager also provides a capability to query the authorization service with a canAuthorize() method to determine whether a given authorization request would succeed if it were made. This capability is useful in constructing user interfaces that display and manipulate details of an authorization implementation.

Figure 10. AuthorizationManager interface
Figure 10. AuthorizationManager interface

Identity, groups, and EAz

There are some limitations in the EAz implementation example, mainly for clarity and simplicity. The implementation handles only a single group, even though the interface is set up for multiple groups; the details of iterating over each supplied group until success or exhaustion were omitted.

The initial EAz implementation only provides authorization for groups, not for named individuals. Individual authorization can be implemented in exactly the same way as groups. Applications implement individual and group naming in different ways; all that is needed to implement ACLs containing permissionSets keyed to individual users is a convention or namespace to distinguish an individual from a group.

Using EAz in the virtual campus

Let's end by walking through how you would apply EAz to our virtual campus example. Assume the campus implementation shown in Figure 11.

Figure 11 Example virtual campus layout
Figure 11 Example virtual campus layout

The figure illustrates Campus A and Campus B. We'll focus on Campus A, but B is present to demonstrate that you can handle multiple campuses and authorize access for one campus only. Campus A shows two buildings, Engineering and Biology. Each building has a relationship to its campus. In this illustration, both buildings have a public space, a private office, and a secure lab. Every space has a name, and is associated with its building.

The table below defines the groups and individuals involved:

Table 1. Groups and individuals for virtual campus example
IdentifierTypeGroups
Campus A usersGroup---
Campus B usersGroup---
Campus A Engineers (Engineers)Group---
Campus A Biologists (Biologists)Group---
Campus A Cleaning staffGroup---
Jane LinnaeusIndividualCampus A users, Biologists
Jim FermiIndividualCampus A users, Engineers
Stan the YetiIndividualCampus A users, Biologists

Let's assume the following access rules:

  • Only Campus A users can enter Campus A; only Campus B users can enter Campus B.

And within the Campus A users:

  • Biologists and Engineers can enter their own buildings and public spaces in it; in addition, Biologists can enter the Engineering building.
  • Cleaning staff for Campus A can enter any room except the labs.
  • Jim Fermi is allowed to enter his office in the Engineering Building; Private Office 2.
  • Jim Fermi and Stan the Yeti are the only ones allowed to enter the Engineering Building Secure Lab 3.
  • Jane Linnaeus is allowed to enter her Biology Building Private Office 5,
  • Only Stan the Yeti can enter the Biology Building Secure Lab 6.

The campuses, buildings, and rooms can be secured as shown in Figure 12.

Figure 12. ACL structure for virtual campus example
Figure 12. ACL structure for virtual campus example

Notice that the public areas in each building need no ACL; you only need to create an ACL when the access rules for a resource deviate from that of the parent resource. For example, you could add a theatre available to all Campus A users and create no ACL for it.

Let's close with a brief, simplified example of how you might use the EAz interface in a real application. Imagine that you have implemented the virtual campus system and we are now writing the method that executes when a room is entered. Your code would look like this:

Listing x. Sample code
...
enterRoom(Room room) {
AuthorizationManager.authorize(Permissions.AllActions.ACTION_ENTER_ROOM, 
	WSSubject.getCallerSubject(), aclHolder);
...Do room stuff...
}

Conclusion

This article covered the problems that the enum-based authorization of EAz sets out to solve, the main public and internal structures of EAz, and the typical ways that an application uses EAz to make custom authorization decisions. Part 2 will examine EAz's initial code in detail to see how Java 5 enums implement the authorization functionality. In the final article, we'll look at using EAz's capabilities in describing its own authorization configuration in a JSF-based Web application that displays the configuration of EAz-based variable authorization rules.


More in this series

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=259211
ArticleTitle=Fine-grained Java EE authorization using Enum-based access control lists with EAz: Part 1: The problem space and EAz architecture
publish-date=10032007