Sunday, March 23, 2014

Implementing container authorization in Java EE with JACC

A while back we looked at how container authentication is done in Java EE by using the JASPIC API. In this article we'll take a look at its authorization counterpart; JACC/JSR 115.

JACC, which stands for Java Authorization Contract for Containers and for some reason also for Java Authorization Service Provider Contract for Containers is a specification that according to the official Java EE documentation "defines a contract between a Java EE application server and an authorization policy provider" and which "defines java.security.Permission classes that satisfy the Java EE authorization model.".

 

Public opinion

While JASPIC had only been added to Java EE as late as in Java EE 6, JACC has been part of Java EE since the dark old days of J2EE 1.4. Developers should thus have had plenty of time to get accustomed to JACC, but unfortunately this doesn't quite seem to be the case. While preparing for this article I talked to a few rather advanced Java EE developers (as in, those who literally wrote the book, worked or have worked on implementing various aspects of Java EE, etc). A few of their responses:

  • "I really have no idea whatsoever what JACC is supposed to do"
  • (After telling I'm working on a JACC article) "Wow! You really have guts!"
  • "[the situation surrounding JACC] really made me cry"
Online there are relatively few resources to be found about JACC. One that I did found said the following:
  • " [...] the PolicyConfiguration's (atrocious) contract [...] "
  • "More ammunition for my case that this particular Java EE contract is not worth the paper it's printed on."

 

What does JACC do?

The negativity aside, the prevailing emotion seems to be that it's just not clear what JACC brings to the table. While JASPIC is not widely used either people do tend to easily get what JASPIC primarily does; provide a standardized API for authentication modules, where those authentication modules are things that check credentials against LDAP servers, a database, a local file, etc and where those credentials are asked via mechanisms like an HTML form or HTTP BASIC etc.

But what does a "contract between an AS and an authorization policy provider" actually mean? In other words, what does JACC do?

In a very practical sense JACC offers the followings things:

  • Contextual objects: A number of convenient objects bound to thread local storage that can be obtained from "anywhere" like the current http request and the current Subject.
  • Authorization queries: A way for application code to ask the container authorization related things like: "What roles does the current user have?" and "Will this user have access to this URL?"
  • A hook into the authorization process: A way to register your own class that will:
    • receive all authorization constraints that have been put in web.xml, ejb-jar.xml and corresponding annotations
    • be consulted when the container makes an authentication decision, e.g. to determine if a user/caller has access to a URL
We'll take a more in-depth look at each of those things below.

 

Contextual objects

Just as the Servlet spec makes a number of (contextual) objects available as request parameters, and JSF does something like this via EL implicit objects, so does JACC makes a number of objects available.

While those objects seem to be primarily intended to be consumed by the special JACC policy providers (see below), they are in fact specified to work in the context of a "dispatched call" (the invocation of an actual Servlet + Filters or an EJB + Interceptors). As such they typically do work when called from user code inside e.g. a Servlet, but the spec could be a bit stricter here and specifically mandate this.

Contrary to Servlet's request/session/application attributes, you don't store instances of objects directly into the JACC policy context. Instead, just like can optionally be done for JNDI you store for each key a factory that knows how to produce an object corresponding to that key. E.g. in order to put a value "bar" in the context using the key "foo" you'd use the following code:

final String key = "foo";
            
PolicyContext.registerHandler(key, 
    new PolicyContextHandler() {
        @Override
        public Object getContext(String key, Object data) throws PolicyContextException {
            return "bar";
        }
        
        @Override
        public boolean supports(String key) throws PolicyContextException {
            return key.equals(key);
        }
        
        @Override
        public String[] getKeys() throws PolicyContextException {
            String[] keys = { key };
            return keys;
        }
    }, 
    true
);

// result will be "bar"
String result = PolicyContext.getContext("foo");
For general usage it's bit unwieldy that the factory (called handler) must known its own key(s). Most containers first get the factory by looking it up in a table using the key it was registered with and then ask the factory if it indeed supports that key. Since a factory has to be registered individually for each key it supports anyway it's debatable whether the responsibility for ensuring the correct keys are used shouldn't be with the one doing the registration. That way the factory interface could have been made a bit simpler.

By default the following keys are defined:

  • javax.security.auth.Subject.container - Returns the current Subject or null if user not authenticated.
  • javax.servlet.http.HttpServletRequest - Returns the current request when requested from a Servlet.
  • javax.xml.soap.SOAPMessage - Returns the SOAP message when requested from an EJB when JAX-WS is used.
  • javax.ejb.EnterpriseBean - Returns the EJB 1.0 "EnterpriseBean" interface. It's best forgotten that this exists.
  • javax.ejb.arguments - Returns the method arguments of the last call to an EJB that's on the stack.
  • java.security.Policy.supportsReuse - Indicates that a container (server) supports caching of an authorization outcome.

Getting the Subject is especially crucial here, since it's pretty much the only way in Java EE to get a hold of this, and we need this for our authorization queries (see below).

For general usage the other objects have the most value for actual policy providers, as in e.g. JSF there are already ways to get the current request from TLS. In practice however semi-proprietary JAAS login modules also not rarely use the PolicyContext to get access to the http request. It should be noted that it's not entirely clear what the "current" request is, especially when servlet filters are used that do request wrapping. Doing an authorization query first (see below) may force the container to set the current request to the one that's really current.

 

Authorization queries

JACC provides an API and a means to ask the container several authorization related things. Just like JASPIC, JACC provides an API that's rather abstract and which seems to be infinitely flexible. Unfortunately this also means it's infinitely difficult for users to find out how to perform certain common tasks. So, while JACC enables us to ask the aforementioned things like "What roles does the current user have?" and "Will this user have access to this URL?", there aren't any convenience methods such as "List<String> getAllUserRoles();" or "boolean hasAccess(String url);". Below we'll show how these things are being doing in JACC.

Get all users roles

We'll start with obtaining the Subject instance corresponding to the current user. For simplicity we assume the user is indeed logged-in here.
Subject subject = (Subject) PolicyContext.getContext("javax.security.auth.Subject.container");
After that we'll get the so-called permission collection from the container that corresponds to this Subject:
PermissionCollection permissionCollection = Policy.getPolicy().getPermissions(
    new ProtectionDomain(
        new CodeSource(null, (Certificate[]) null), 
        null, null, 
        subject.getPrincipals().toArray(new Principal[subject.getPrincipals().size()])
    )
);
Most types shown here originate from the original Java SE security system (pre-JAAS even). Their usage is relatively rare in Java EE so we'll give a quick primer here. A more thorough explanation can be found in books like e.g. this one.


CodeSource

A code source indicates from which URL a class was loaded and with which certificates (if any) it was signed. This class was used in the original security model to protect your system from classes that were loaded from websites on the Internet; i.e. when Applets were the main focus area of Java. The roles associated with a user in Java EE do not depend in any way on the location from which the class asking this was loaded (they are so-called Principal-based permissions), so we provide a null for both the URL and the certificates here.

ProtectionDomain

A protection domain is primarily a grouping of a code source, and a set of static permissions that apply for the given Principals. In the original Java SE security model this type was introduced to be associated with a collection of classes, where each class part of the domain holds a reference to this type. In this case we're not using the protection domain in that way, but merely use it as input to ask which permissions the current Subject has. As such, the code source, static permissions and class loader (third parameter) are totally irrelevant. The only thing that matters is the Subject, and specifically its principals.

The reason why we pass in a code source with all its fields set to null instead of just a null directly is that the getPermissions() method of well known Policy implementations like the PolicyFile from the Oracle JDK will call methods on it without checking if the entire thing isn't null. The bulky code to transform the Principals into an array is an unfortunate mismatch between the design of the Subject class (which uses a Set) and the ProtectionDomain (which uses a native array). It's extra unfortunate since the ProtectionDomain's constructor specifies it will copy the array again, so two copies of our original Set will be made here.

Policy

A policy is the main mechanism in Java SE that originally encapsulated the mapping between code sources and a global set of permissions that were given to that code source. Only much later in Java 1.4 was the ability added to get the permissions for a protection domain. Its JavaDoc has the somewhat disheartening warning that applications should not actually call the method. In case of JACC we can probably ignore this warning (application server implementations call this method too).

PermissionCollection

At first glance a PermissionCollection is exactly what its name implies (no pun intended): a collection of permissions. So why does the JDK have a special type for this and doesn't it just use a Collection<Permission> or a List<Permission>? Maybe part of the answer is that PermissionCollection was created before the Collection Framework in Java was introduced.

But this may be only part of the answer. A PermissionCollection and its most important subclass Permissions make a distinction between homogenous and heterogenous Permissions. Adding an arbitrary Permission to a Permissions class is supposed to not just add it randomly in sequence, but to add it internally to a special "bucket" . This bucket is another PermissionCollection that stores permissions of the same type. It's typically implemented as a Class to PermissionCollection Map. This somewhat complex mechanism is used to optimize checking for a permission; iterating over every individual permission would not be ideal. We at least should be able to go right away to the right type of permission. E.g. when checking for permission to access a file, it's useless to ask every socket permission whether we have access to that.


After this we call the implies() method on the collection:

permissionCollection.implies(new WebRoleRefPermission("", "nothing"));
This is small trick, hack if you will, to get rid of a special type of permission that might be in the collection; the UnresolvedPermission. This is a special type of permission that may be used when permissions are read from a file. Such file then typically contains the fully qualified name of a class that represents a specific permission. If this class hasn't been loaded yet or has been loaded by another class loader than the one from which the file is read, a UnresolvedPermission will be created that just contains this fully qualified class name as a String. The implies() method checks if the given permission is implied by the collection and therefor forces the actual loading of at least the WebRoleRefPermission class. This class is the standard permission type that corresponds to the non-standard representation of the group/roles inside the collection of principals that we're after.

Finally we iterate over the permission collection and collect all role names from the WebRoleRefPermission:

Set<String> roles = new HashSet<>();
for (Permission permission : list(permissionCollection.elements())) {
   if (permission instanceof WebRoleRefPermission) {
       String role = permission.getActions();
      
       if (!roles.contains(role) && request.isUserInRole(role)) {
           roles.add(role);
       }
   }
}
A thing to note here is that there's no such thing as a WebRolePermission, but only a WebRoleRefPermission. In the Servlet spec a role ref is the thing that you use when inside a specific Servlet a role name is used that's different from the role names in the rest of your application. In theory this could be handy for secured Servlets from a library that you include in your application. Role refs are fully optional and when you don't use them you can simply use the application wide role names directly.

In JACC however there are only role refs. When a role ref is not explicitly defined then they are simply defaulted to the application role names. Since a role ref is per servlet, the number of WebRoleRefPermission instances that will be created is *at least* the number of roles in the application plus one (for the '**' role), times the number of servlets in the application (typically plus three for the default and JSP servlet, and an extra one for the so-called unmapped context). So given an application with two roles "foo" and "bar" and two Servlets named "servlet1" and "servlet2", the WebRoleRefPermission instances that will be created is as follows:

  1. servlet1 - foo
  2. servlet1 - bar
  3. servlet1 - **
  4. servlet2 - foo
  5. servlet2 - bar
  6. servlet2 - **
  7. default - foo
  8. default - bar
  9. default - **
  10. jsp - foo
  11. jsp - bar
  12. jsp - **
  13. "" - foo
  14. "" - bar
  15. "" - **
In order to filter out the duplicates the above code uses a Set and not e.g. a List. Additionally, to filter out any role refs other than those for the current Servlet from which we are calling the code we additionally do the request.isUserInRole(role) check. Alternatively we could have checked the name attribute of each WebRoleRefPermission as that one corresponds to the name of the current Servlet, which can be obtained via GenericServlet#getServletName. If we're sure that there aren't any role references being used in the application, or if we explicitly want all global roles, the following code can be used instead of the last fragment given above:
Set<String> roles = new HashSet<>();
for (Permission permission : list(permissionCollection.elements())) {
   if (permission instanceof WebRoleRefPermission && permission.getName().isEmpty()) {
       roles.add(permission.getActions());
   }
}

Typically JACC providers will create the total list of WebRoleRefPermission instances when an application is deployed and then return a sub-selection based on the Principals that we (indirectly) passed in our call to Policy#getPermissions. This however requires that all roles are statically and upfront declared. But a JASPIC auth module can dynamically return any amount of roles to the container and via HttpServletRequest#isUserInRole() an application can dynamically query for any such role without anything needing to be declared. Unfortunately such dynamic role usage typically doesn't work when JACC is used (the Java EE specification also forbids this, but on servers like JBoss it works anyway).

All in all the above shown query needs a lot of code and a lot of useless types and parameters for which nulls have to be passed. This could have been a lot simpler by any of the following means:

  • Availability of a "List<String> getAllUserRoles();" method
  • Standardization of the group/role Principals inside a subject (see JAAS in Java EE is not the universal standard you may think it is)
  • A convenience method to get permissions based on just a Subject or Principal collection, e.g. PermissionCollection getPermissions(Subject subject); or PermissionCollection getPermissions(Collection<Principals> principals);

This same technique with slightly different code is also explained here: Using JACC to determine a caller's roles

Has access

Asking whether a user has permission to access a given resource (e.g. a Servlet) is luckily a bit smaller:
Subject subject = (Subject) PolicyContext.getContext("javax.security.auth.Subject.container");

boolean hasAccess = Policy.getPolicy().implies(
    new ProtectionDomain(
        new CodeSource(null, (Certificate[]) null),
        null, null, 
        subject.getPrincipals().toArray(new Principal[subject.getPrincipals().size()])
    ),
    new WebResourcePermission("/protected/Servlet", "GET"))
;
We first get the Subject and create a ProtectionDomain in the same way as we did before. This time around we don't need to get the permission collection, but can make use of a small shortcut in the API. Calling implies on the Policy instance effectively invokes it on the permission collection that this instance maintains. Besides being ever so slightly shorter in code, it's presumably more efficient as well.

The second parameter that we pass in is the actual query; via a WebResourcePermission instance we can ask whether the resource "/protected/Servlet" can be accessed via a GET request. Both parameters support patterns and wildcards (see the JavaDoc). It's important to note that a WebResourcePermission only checks permission for the resource name and the HTTP method. There's a third aspect for checking access to a resource and that boils down to the URI scheme that's used (http vs https) and which corresponds to the <transport-guarantee> in a <user-data-constraint> element in web.xml. In JACC this information is NOT included in the WebResourcePermission, but a separate permission has been invented for that; the WebUserDataPermission, which can be individually queried.

Although this query requires less code than the previous one, it's probably still more verbose than many users would want to cope with. Creating the ProtectionDomain remains a cumbersome affair and getting the Subject via a String that needs to be remembered and requiring a cast unfortunately all add to the feeling of JACC being an arcane API. Here too, things could be made a lot simpler by:

  • Availability of boolean hasAccess(String resource);" and boolean hasAccess(String resource, String metbod);" methods
  • A convenience method to do a permission check based on the current user, e.g. boolean hasUserPermission(Permission permission);
  • A convenience method to do a permission check based on just a Subject or Principal collection, e.g. boolean hasPermission(Subject subject, Permission permission); or boolean hasPermission(Collection principals, Permission permission);
As it appears, several JACC implementations in fact internally have methods not quite unlike these.

 

A hook into the authorization process

The most central feature of JACC is that it allows a class to be registered that hooks into the authorization process. "A class" is actually not entirely correct as typically 3 distinct classes are needed; a factory (there's of course always a factory), a "configuration" and the actual class (the "policy") that's called by the container (together the provider classes).

The factory and the policy have to be set individually via system properties (e.g. -D on the commandline). The configuration doesn't have to be set this way since it's created by the factory.

The following table gives an overview of these:

Class System property Description Origin
javax.security.jacc.PolicyConfigurationFactory javax.security.jacc.PolicyConfigurationFactory.provider Creates and stores instances of PolicyConfiguration JACC
javax.security.jacc.PolicyConfiguration - Receives Permission instances corresponding to authorization constraints and can configure a Policy instance JACC
java.security.Policy javax.security.jacc.policy.provider Called by the container for authorization decisions Java SE

 

Complexity

While JACC is not the only specification that requires the registration of a factory (e.g. JSF has a similar requirement for setting a custom external context) its perhaps debatable whether the requirement to implement 3 classes and set 2 system properties isn't yet another reason why JACC is perceived as arcane. Indeed, when looking at a typical implementation of the PolicyConfigurationFactory it doesn't seem it does anything that the container wouldn't be able to do itself.

Likewise, the need to have a separate "configuration" and policy object isn't entirely clear either. Although just an extra class, such seemingly simple requirement does greatly add to the (perceived) complexity. This is extra problematic in the case of JACC since the "configuration" has an extremely heavyweight interface with an associated requirement to implement a state machine that controls the life cycle in which its methods can be called. Reading it one can only wonder why the container doesn't implement this required state machine and just lets the user implement the bare essentials. And if this all isn't enough the spec also hints at users having to make sure their "configuration" implementation is thread safe, again a task which one would think a container would be able to do.

Of course we have to realize that JACC originates from J2EE 1.4, where a simple "hello, world" EJB 2 required implementing many useless (for most users) container interfaces, the use of custom tools and verbose registrations in XML deployment descriptors, and where a custom component in JSF required the implementation and registration of many such moving parts as well. Eventually the EJB bean was simplified to just a single POJO with a single annotation, and while the JSF custom component didn't became a POJO it did became a single class with a single annotation as well.

JACC had some maintenance revisions, but never got a really major revision for ease of use. It thus still strongly reflects the J2EE 1.4 era thinking where everything had to be ultimately flexible, abstract and under the control of the user. Seemingly admirable goals, but unfortunately in practice leading to a level of complexity very few users are willing to cope with.

Registration

The fact that the mentioned provider classes can only be registered via system properties is problematic as well. In a way it's maybe better than having no standardized registration method at all and leaving it completely up to the discretion of an application server vendor, but having it as the only option means the JACC provider classes can only be registered globally for the entire server. In particular it thus means that all applications on a server have to share the same JACC provider even though authorization is not rarely quite specific to an individual application.

At any length not having a way to register the required classes from within an application archive seriously impedes testing, makes tutorials and example applications harder and doesn't really work well with cloud deployments either. Interestingly JASPIC which was released much later dished the system properties method again and left a server wide registration method unspecified, but did specify a way to register its artifacts from within an application archive.

Default implementation

Arguably one of the biggest issues with JACC is that the spec doesn't mandate a default implementation of a JACC provider to be present. This has two very serious consequences. First of all users wanting to use JACC for authorization queries can not just do this, since there might not be a default JACC provider available for their server, and if there is it might not be activated.

Maybe even worse is that users wanting to provide extended authorization behavior have to implement the entire default behavior from scratch. This is bad. Really bad. Even JSF 1.0 which definitely had its own share of issues allowed users to provide extensions by overriding only what they wanted to be different from whatever a default implementation provided. JACC is complex enough as it is. Not providing a default implementation is making the complexity barrier literally go through the roof.

Role mapping

While we have seen some pretty big issues with JACC already, by far the biggest issue and a complete hindrance to any form of portability of JACC policy providers is the fact that there's a huge gaping hole in the specification; the crucial concept of "role mapping" is not specified. The spec mentions it, but then says it's the provider's responsibility.

The problem is two fold; many containers (but not all) have a mechanism in place where the role names that are returned by a (JASPIC) authentication module (typically called "groups" at this point) are mapped to the role names that are used in the application. This can be a one to one mapping, e.g. "admin" is mapped to "administrator", but can also be a many to many mapping. For example when "admin and "super" both map to "super-users", but "admin" is also mapped to "administrator". JACC has no standard API available to access this mapping, yet it can't work without this knowledge if the application server indeed uses it.

In practice JACC providers thus have to be paired with a factory to obtain a custom role mapper. This role mapper has to be implemented again for every application server that uses role mapping and every JACC provider. Especially for the somewhat lesser known application servers and/or closed source ones it can be rather obscure to find out how to implement a role mapper for it. And the task is different for every other JACC policy provider as well, and some JACC policy providers may not have a factory or interface for it.

Even if we ignore role mapping all together (e.g. assume role "admin" returned by an auth module is the same "admin" used by the application, which is not at all uncommon), there is another crucial task that's typically attributed to the role mapper which we're missing; the ability to identify which Principals are in fact roles. As we've seen above in the section that explained the authorization queries we're passing in the Principals of a Subject. But as we've explained in a previous article this collection contains all sorts of principals and there's no standard way to identify which of those represent roles.

So the somewhat shockingly conclusion is that given the current Java EE specification it's not possible to write a portable and actual working JACC policy provider. At some point we just need the roles, but there's no way to get to them via any Java EE API, SPI, convention or otherwise. In sample code below we've implemented a crude hack where we hardcoded the way to get to the roles for a small group of known servers. This is of course far from ideal.

Sample code

The code below shows how to implement a simple as can be JACC module that implements the default behavior for Servlet and EJB containers. This thus doesn't show how to provide specialized behavior, but should give some idea what's needed. The required state machine was left out as well as the article was already getting way too long without it.

The factory
We start with creating the factory. This is the class that we primarily register with the container. It stores instances of the configuration class TestPolicyConfiguration in a static concurrent map. The code is more or less thread-safe with respect to creating a configuration. In case of a race we'll create an instance for nothing, but since it's not a heavy instance there won't be much harm done. For simplicity's sake we won't be paying much if any attention to thread-safety after this. The getPolicyConfiguration() will be called by the container whenever it needs the instance corresponding to the "contextID", which is in its simplest form the (web) application for which we are doing authorization.
import static javax.security.jacc.PolicyContext.getContextID;

// other imports omitted for brevity

public class TestPolicyConfigurationFactory extends PolicyConfigurationFactory {
    
    private static final ConcurrentMap<String, TestPolicyConfiguration> configurations = new ConcurrentHashMap<>();

    @Override
    public PolicyConfiguration getPolicyConfiguration(String contextID, boolean remove) throws PolicyContextException {
        
        if (!configurations.containsKey(contextID)) {
            configurations.putIfAbsent(contextID, new TestPolicyConfiguration(contextID));
        }
        
        if (remove) {
            configurations.clear();
        }
        
        return configurations.get(contextID);
    }
    
    @Override
    public boolean inService(String contextID) throws PolicyContextException {
        return getPolicyConfiguration(contextID, false).inService();
    }
    
    public static TestPolicyConfiguration getCurrentPolicyConfiguration() {
        return configurations.get(getContextID());
    }
    
}
The configuration
The configuration class has a ton of methods to implement and additionally according to its JavaDocs a state machine has to be implemented as well.

The methods that need to be implemented can be placed into a few groups. We'll first show the implementation of each group of methods and then after that show the entire class.

The first is simply about the identity of the configuration. It contains the method getContextID that returns the ID for the module for which the configuration is created. Although there's no guidance where this ID has to come from, a logical way seems to pass it from the factory via the constructor:

    private final String contextID;
    
    public TestPolicyConfiguration(String contextID) {
        this.contextID = contextID;
    }
    
    @Override
    public String getContextID() throws PolicyContextException {
        return contextID;
    }
The second group concerns the methods that receive from the container all permissions applicable to the application module, namely the excluded, unchecked and per role permissions. No less than 6 methods are demanded to be implemented. 3 of them each take a single permission of the aforementioned types, while the other 3 take a collection (PermissionCollection) of these permissions. Having these two sub-groups of methods seems rather unnecessary. Maybe there was a deeper reason once, but of the several existing implementations I studied all just iterated over the collection and added each individual permission separately to an internal collection.

At any length, we just collect each permission that we receive in the most straightforward way possible.


    private Permissions excludedPermissions = new Permissions();
    private Permissions uncheckedPermissions = new Permissions();
    private Map<String, Permissions> perRolePermissions = new HashMap<String, Permissions>();

    // Group 2a: collect single permission

    @Override
    public void addToExcludedPolicy(Permission permission) throws PolicyContextException {
        excludedPermissions.add(permission);
    }

    @Override
    public void addToUncheckedPolicy(Permission permission) throws PolicyContextException {
        uncheckedPermissions.add(permission);
    }

    @Override
    public void addToRole(String roleName, Permission permission) throws PolicyContextException {
        Permissions permissions = perRolePermissions.get(roleName);
        if (permissions == null) {
            permissions = new Permissions();
            perRolePermissions.put(roleName, permissions);
        }
        
        permissions.add(permission);
    }

    // Group 2b: collect multiple permissions
    
    @Override
    public void addToExcludedPolicy(PermissionCollection permissions) throws PolicyContextException {
        for (Permission permission : list(permissions.elements())) {
            addToExcludedPolicy(permission);
        }
    }
    
    @Override
    public void addToUncheckedPolicy(PermissionCollection permissions) throws PolicyContextException {
        for (Permission permission : list(permissions.elements())) {
            addToUncheckedPolicy(permission);
        }
    }
    
    @Override
    public void addToRole(String roleName, PermissionCollection permissions) throws PolicyContextException {
        for (Permission permission : list(permissions.elements())) {
            addToRole(roleName, permission);
        }
    }

The third group are life-cycle methods, partly part of the above mentioned state machine. Much of the complexity here is caused by the fact that multi-module applications have to share some authorization data but also still need to have their own, and that some modules have to be available before others. The commit methods signals that the last permission has been given to the configuration class. Depending on the exact strategy used, the configuration class could now e.g. start building up some specialized data structure, write the collected permissions to a standard policy file on disk, etc.

In our case we have nothing special to do. For this simple example we only implement the most basic requirement of the aforementioned state machine and that's making sure the inService method returns true when we're done. Containers may check for this via the previously shown factory, and will otherwise not hand over the permissions to our configuration class or keep doing that forever.

    @Override
    public void linkConfiguration(PolicyConfiguration link) throws PolicyContextException {
    }
    
    boolean inservice;
    @Override
    public void commit() throws PolicyContextException {
        inservice = true;
    }

    @Override
    public boolean inService() throws PolicyContextException {
        return inservice;
    }

The fourth group concern methods that ask for the deletion of previously given permissions. Here too we see some redundant overlap; there's 1 method that deletes all permissions and 1 method for each type. These methods are supposedly needed because programmatic registration of Servlets can change the authorization data during container startup and every time(?) that this happens all previously collected permissions would have to be deleted. Apparently the container can't delay the moment of handing permissions to the configuration class since during the time when it's legal to register Servlets a call can be made to another module (e.g. a ServletContextListener could call an EJB in a separate EJB module). Still, it's questionable whether it's really needed to have a kind of back-tracking permission collector in place and whether part of this burden should really be placed on the user implementing an authorization extension.

In our case the implementations are pretty straightforward. The Permissions type doesn't have a clear() method so we'll replace it by a new instance. For the Map we can call a clear() method, but there's a special protocol to be executed concerning the "*" role; if the collection explicitly contains a role name "*" remove it, otherwise clear the entire collection.

    @Override
    public void delete() throws PolicyContextException {
        removeExcludedPolicy();
        removeUncheckedPolicy();
        perRolePermissions.clear();
    }

    @Override
    public void removeExcludedPolicy() throws PolicyContextException {
        excludedPermissions = new Permissions();
    }

    @Override
    public void removeRole(String roleName) throws PolicyContextException {
        if (perRolePermissions.containsKey(roleName)) {
            perRolePermissions.remove(roleName);
        } else if ("*".equals(roleName)) {
            perRolePermissions.clear();
        }
    }

Finally we added 3 getters ourselves to give access to the 3 collections of permissions that we have been building up. We'll use this below to give the Policy instance access to the permissions.

    public Permissions getExcludedPermissions() {
        return excludedPermissions;
    }

    public Permissions getUncheckedPermissions() {
        return uncheckedPermissions;
    }

    public Map<String, Permissions> getPerRolePermissions() {
        return perRolePermissions;
    }

Although initially unwieldy looking, the PolicyConfiguration in our case becomes just a data structure to add, get and remove three types of different but related groups of objects. All together it becomes this:

import static java.util.Collections.list;

// other imports omitted for brevity

public class TestPolicyConfiguration implements PolicyConfiguration {
    
    private final String contextID;
    
    private Permissions excludedPermissions = new Permissions();
    private Permissions uncheckedPermissions = new Permissions();
    private Map<String, Permissions> perRolePermissions = new HashMap<String, Permissions>();

    // Group 1: identity    

    public TestPolicyConfiguration(String contextID) {
        this.contextID = contextID;
    }
    
    @Override
    public String getContextID() throws PolicyContextException {
        return contextID;
    }


    // Group 2: collect permissions from container

    // Group 2a: collect single permission

    @Override
    public void addToExcludedPolicy(Permission permission) throws PolicyContextException {
        excludedPermissions.add(permission);
    }

    @Override
    public void addToUncheckedPolicy(Permission permission) throws PolicyContextException {
        uncheckedPermissions.add(permission);
    }

    @Override
    public void addToRole(String roleName, Permission permission) throws PolicyContextException {
        Permissions permissions = perRolePermissions.get(roleName);
        if (permissions == null) {
            permissions = new Permissions();
            perRolePermissions.put(roleName, permissions);
        }
        
        permissions.add(permission);
    }

    // Group 2b: collect multiple permissions
    
    @Override
    public void addToExcludedPolicy(PermissionCollection permissions) throws PolicyContextException {
        for (Permission permission : list(permissions.elements())) {
            addToExcludedPolicy(permission);
        }
    }
    
    @Override
    public void addToUncheckedPolicy(PermissionCollection permissions) throws PolicyContextException {
        for (Permission permission : list(permissions.elements())) {
            addToUncheckedPolicy(permission);
        }
    }
    
    @Override
    public void addToRole(String roleName, PermissionCollection permissions) throws PolicyContextException {
        for (Permission permission : list(permissions.elements())) {
            addToRole(roleName, permission);
        }
    }

    // Group 3: life-cycle methods

    @Override
    public void linkConfiguration(PolicyConfiguration link) throws PolicyContextException {
    }
    
    boolean inservice;
    @Override
    public void commit() throws PolicyContextException {
        inservice = true;
    }

    @Override
    public boolean inService() throws PolicyContextException {
        return inservice;
    }

    // Group 4: removing all or specific collection types again

    @Override
    public void delete() throws PolicyContextException {
        removeExcludedPolicy();
        removeUncheckedPolicy();
        perRolePermissions.clear();
    }

    @Override
    public void removeExcludedPolicy() throws PolicyContextException {
        excludedPermissions = new Permissions();
    }

    @Override
    public void removeRole(String roleName) throws PolicyContextException {
        if (perRolePermissions.containsKey(roleName)) {
            perRolePermissions.remove(roleName);
        } else if ("*".equals(roleName)) {
            perRolePermissions.clear();
        }
    }

    @Override
    public void removeUncheckedPolicy() throws PolicyContextException {
        uncheckedPermissions = new Permissions();
    }

    // Group 5: extra methods
    
    public Permissions getExcludedPermissions() {
        return excludedPermissions;
    }

    public Permissions getUncheckedPermissions() {
        return uncheckedPermissions;
    }

    public Map<String, Permissions> getPerRolePermissions() {
        return perRolePermissions;
    }
}
The policy
To implement the policy we inherit from the Java SE policy class and override the implies and getPermissions methods. Where the PolicyConfiguration is the "dumb" data structure that just contains the collections of different permissions, the Policy implements the rules that operate on this data.

The policy instance is per the JDK rules a single instance for the entire JVM, so central to its implementation in Java EE is that it first has to obtain the correct data corresponding to the "current" Java EE application or module within such application. The key to obtaining this data is the Context ID that is set in thread local storage by the container prior to calling the policy. The factory that we showed earlier stored the configuration instance under this key in a concurrent map and we use the extra method that we added to the factory here to conveniently retrieve it again.

The policy implementation also contains the hacky method that we referred to earlier for extracting the roles from a collection of principals. It just iterates over the collection and matches the principals against the known group names for each server. Of course this is a brittle and incomplete technique. Newer versions of servers can change the class names and we have not covered all servers; e.g. WebSphere is missing since via the custom method to obtain a Subject we saw earlier that the roles were stored in the credentials (we may need to study this further).

package test;

import static java.util.Collections.list;
import static test.TestPolicyConfigurationFactory.getCurrentPolicyConfiguration;

// other imports omitted for brevity

public class TestPolicy extends Policy {
    
    private Policy previousPolicy = Policy.getPolicy();
    
    @Override
    public boolean implies(ProtectionDomain domain, Permission permission) {
            
        TestPolicyConfiguration policyConfiguration = getCurrentPolicyConfiguration();
    
        if (isExcluded(policyConfiguration.getExcludedPermissions(), permission)) {
            // Excluded permissions cannot be accessed by anyone
            return false;
        }
        
        if (isUnchecked(policyConfiguration.getUncheckedPermissions(), permission)) {
            // Unchecked permissions are free to be accessed by everyone
            return true;
        }
        
        if (hasAccessViaRole(policyConfiguration.getPerRolePermissions(), getRoles(domain.getPrincipals()), permission)) {
            // Access is granted via role. Note that if this returns false it doesn't mean the permission is not
            // granted. A role can only grant, not take away permissions.
            return true;
        }
        
        if (previousPolicy != null) {
            return previousPolicy.implies(domain, permission);
        }
        
        return false;
    }

    @Override
    public PermissionCollection getPermissions(ProtectionDomain domain) {

        Permissions permissions = new Permissions();
        
        TestPolicyConfiguration policyConfiguration = getCurrentPolicyConfiguration();
        Permissions excludedPermissions = policyConfiguration.getExcludedPermissions();

        // First get all permissions from the previous (original) policy
        if (previousPolicy != null) {
            collectPermissions(previousPolicy.getPermissions(domain), permissions, excludedPermissions);
        }

        // If there are any static permissions, add those next
        if (domain.getPermissions() != null) {
            collectPermissions(domain.getPermissions(), permissions, excludedPermissions);
        }

        // Thirdly, get all unchecked permissions
        collectPermissions(policyConfiguration.getUncheckedPermissions(), permissions, excludedPermissions);

        // Finally get the permissions for each role *that the current user has*
        Map<String, Permissions> perRolePermissions = policyConfiguration.getPerRolePermissions();
        for (String role : getRoles(domain.getPrincipals())) {
            if (perRolePermissions.containsKey(role)) {
                collectPermissions(perRolePermissions.get(role), permissions, excludedPermissions);
            }
        }

        return permissions;
    }
    
    @Override
    public PermissionCollection getPermissions(CodeSource codesource) {

        Permissions permissions = new Permissions();
        
        TestPolicyConfiguration policyConfiguration = getCurrentPolicyConfiguration();
        Permissions excludedPermissions = policyConfiguration.getExcludedPermissions();

        // First get all permissions from the previous (original) policy
        if (previousPolicy != null) {
            collectPermissions(previousPolicy.getPermissions(codesource), permissions, excludedPermissions);
        }

        // Secondly get the static permissions. Note that there are only two sources possible here, without
        // knowing the roles of the current user we can't check the per role permissions.
        collectPermissions(policyConfiguration.getUncheckedPermissions(), permissions, excludedPermissions);

        return permissions;
    }
    
    private boolean isExcluded(Permissions excludedPermissions, Permission permission) {
        if (excludedPermissions.implies(permission)) {
            return true;
        }
        
        for (Permission excludedPermission : list(excludedPermissions.elements())) {
            if (permission.implies(excludedPermission)) {
                return true;
            }
        }
        
        return false;
    }
    
    private boolean isUnchecked(Permissions uncheckedPermissions, Permission permission) {
        return uncheckedPermissions.implies(permission);
    }
    
    private boolean hasAccessViaRole(Map<String, Permissions> perRolePermissions, List<String> roles, Permission permission) {
        for (String role : roles) {
            if (perRolePermissions.containsKey(role) && perRolePermissions.get(role).implies(permission)) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Copies permissions from a source into a target skipping any permission that's excluded.
     * 
     * @param sourcePermissions
     * @param targetPermissions
     * @param excludedPermissions
     */
    private void collectPermissions(PermissionCollection sourcePermissions, PermissionCollection targetPermissions, Permissions excludedPermissions) {
        
        boolean hasExcludedPermissions = excludedPermissions.elements().hasMoreElements();
        
        for (Permission permission : list(sourcePermissions.elements())) {
            if (!hasExcludedPermissions || !isExcluded(excludedPermissions, permission)) {
                targetPermissions.add(permission);
            }
        }
    }
    
    /**
     * Extracts the roles from the vendor specific principals. SAD that this is needed :(
     * @param principals
     * @return
     */
    private List<String> getRoles(Principal[] principals) {
        List<String> roles = new ArrayList<>();
        
        for (Principal principal : principals) {
            switch (principal.getClass().getName()) {
            
                case "org.glassfish.security.common.Group": // GlassFish
                case "org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal": // Geronimo
                case "weblogic.security.principal.WLSGroupImpl": // WebLogic
                case "jeus.security.resource.GroupPrincipalImpl": // JEUS
                    roles.add(principal.getName());
                    break;
                
                case "org.jboss.security.SimpleGroup": // JBoss
                    if (principal.getName().equals("Roles") && principal instanceof Group) {
                        Group rolesGroup = (Group) principal;
                        for (Principal groupPrincipal : list(rolesGroup.members())) {
                            roles.add(groupPrincipal.getName());
                        }
                        
                        // Should only be one group holding the roles, so can exit the loop
                        // early
                        return roles;
                    }
            }
        }
        
        return roles;
    }
    
}

 

Summary of JACC's problems

Unfortunately we've seen that JACC has a few problems. It has a somewhat arcane and verbose API which easily puts (new) users off. We've also seen that it puts too much responsibilities on the user, especially when it comes to customizing the authorization system.

A fatal flaw in JACC is that it didn't specify how to access roles from a collection of Principals. Since authorization is primarily role based in Java EE this makes it downright impossible to create portable JACC policy providers and much harder than necessary to create vendor specific ones.

Another fatal flaw of JACC is that there's not necessarily a default implementation of JACC active at runtime. This means that general Java EE applications cannot just use JACC; they would have to instruct their users to make sure JACC is activated for their server. Since not all servers ship with a simple default implementation this would not even work for all servers.

On the one hand it's impressive that JACC has managed to integrate so well with Java SE security. On the other hand it's debatable whether this has been really useful in practice. Java EE containers can now consult the Java SE Policy for authorization, but when a security manager is installed Java SE code will consult the very same one.

Java SE permissions are mostly about what code coming from some code location (e.g. a specific library directory on the severer) is allowed to do, while Java EE permissions are about the things an external user logged-in to the server is allowed to do. With the current setup the JACC policy replaces the low level Java SE one and thus all Java SE security checks will go through the JACC policy as well. This may be tens of checks per second or even more. All that a JACC policy can really do is delegate these to the default Java SE policy.

 

Conclusion

The concept of having a central repository holding all authorization rules is by itself a powerful addition to the Java EE security system. It allows one to query the repository and thus programmatically check if a resource will be accessible. This is particularly useful for URL based resources; if a user would not have access we can omit rendering a link to that resource, or perhaps render it with a different color, with a lock icon, etc.

Although not explicitly demonstrated in this article it shouldn't require too much imagination to see that the default implementation that we showed can easily do something else instead and thus tailor the authorization system to the specific needs of an application.

As shown it might not be that difficult to reduce the verbosity of the JACC API by introducing a couple of convenience methods for common functionality. Specifying which principals corresponds to roles would be the most straightforward solution for the roles problem, but a container provided role mapper instance that returns a list of group/role principals given a collection of strings representing application roles and the other way around might be a workable solution as well.

While tedious for users to implement, container vendors should not have much difficulties implementing a default JACC policy provider and activating it by default. Actually mandating providers to USE JACC themselves for their Servlet and EJB authorization decisions may be another story though. A couple of vendors (specifically Oracle themselves for WebLogic) claim performance and flexibility issues with JACC and actually more or less encourage users not to use it. In case of WebLogic this advice probably stems from the BEA days, but it's still in the current WebLogic documentation. With Oracle being the steward of JACC it's remarkable that they effectively suggest their own users not to use it, although GlassFish which is also from Oracle is one of the few or perhaps only server that in fact uses JACC internally itself.

As it stands JACC is not used a lot and not universally loved, but a few relatively small changes may be all that's needed to make it much more accessible and easy to use.

Arjan Tijms

Thursday, February 20, 2014

JAAS in Java EE is not the universal standard you may think it is

The Java EE security landscape is complex and fragmented. There is the original Java SE security model, there is JAAS, there is JASPIC, there is JACC and there are the various specs like Servlet, EJB and JAX-RS that each have their own sections on security.

For some reason or the other it's too often thought that there's only a single all encompassing security framework in Java EE, and that the name of that framework is JAAS. This is however not the case. Far from it. Raymond K. NG has explained this a long time ago in the following articles:

In short, Java EE has its own security model and over the years has tried to bridge this to the existing Java SE model, but this has never worked flawlessly. One particular issue that we'll be taking a more in-depth look at in this article concerns the JAAS Subject.

The JAAS Subject represent an entity like a person and is implemented as a so-called "bag of Principals". This means it holds a collection of arbitrary Principals, where a Principal can be anything that identifies the entity, like an email, telephone number or an identification number. This gives it great flexibility to work with, but since none of those Principals are in any way standardized also makes it difficult and open to interpretation. In a way one might almost just pass a plain HashMap along, which after all is very flexible too.

For Java EE most of the flexibility that the Subject offers is lost however. The most predominant APIs only deal with 2 specific principals; the caller/user principal and the group/role principal.

An enormous amount of complexity and rather arcane API workarounds have spun around a simple but very unfortunate fact; those two core principals have not been standardized in any way in Java EE. This means that a JAAS login module, which populates a Subject with principals when it commits, can never be used directly with an arbitrary Java EE server as it has no idea how a particular Java EE server represents the caller/user and group/role principals.

As explained in a previous article JASPIC has introduced a workaround for this, where a so-called CallerPrincipalCallback and GroupPrincipalCallback do represent the data for those two principals in a standardized way. The idea is that a container can read this data from those callbacks in this standardized way and then construct container specific principals which are subsequently stored in a container specific way into the Subject. This works to some degree, but it remains a crude workaround.

The problem is that the JASPIC CallbackHandler to which these Callbacks are passed is under no obligation to just read the data and directly store it into the Subject without any further side effects. In practice, this is thus indeed not what happens. Handlers will set global security contexts, call into services to execute all kinds of login logic and what have you. Furthermore, there is nothing available to directly read those Principals back from a given Subject. Indirectly you can get the user/caller principal back via e.g. the Servlet API's HttpServletRequest#getUserPrincipal, but this only works for the current authenticated user and not for an arbitrary Subject that you may have lying around. Likewise, the role/group principals can be obtained via the JACC API. Unfortunately JACC itself is a rather obscure API that while being part of Java EE does not necessarily actually has to be active at runtime by default, and in fact doesn't even have to work out of the box since Java EE implementations are not mandated to ship with an actual default JACC provider (they only have to provide the SPI to plug such provider into the container).

The big question is then of course why these two core principals have never been standardized. Do containers use such different representations that standardization is not feasible, or did it just never occurred to anyone in all those years that it may be handy to standardize these?

In order to see if vastly different representations might be the issue I took a look at how actual containers exactly store the Principals inside a Subject. This was primarily done by examining an instance of a Subject via a debugger, but I also took a quick look at some example JAAS modules for a couple of servers. After all, at some point the JAAS module has to add the Principals to the given Subject and the example may indicate how this is done for a specific server.

Examining an instance of a Subject via a debugger


For authentication I used the JASPIC auth module that was introduced in step 5 of the aforementioned previous article and the same test application. In order to retrieve the Subject I used the following JACC code in the Servlet:
Subject subject = (Subject) PolicyContext.getContext("javax.security.auth.Subject.container");

For JBoss EAP 6.2, GlassFish 4 and Geronimo 3.0.1 this worked perfectly. There were however issues with WebLogic 12.1.2, WebSphere 8.5 and JEUS 8.

In WebLogic 12.1.2 JACC is not enabled by default. You have to explicitly enable it by starting the container with a huge string of command line options, including one that references the directory where WebLogic is installed:

./startWebLogic.sh -Djava.security.manager -Djavax.security.jacc.policy.provider=weblogic.security.jacc.simpleprovider.SimpleJACCPolicy -Djavax.security.jacc.PolicyConfigurationFactory.provider=weblogic.security.jacc.simpleprovider.PolicyConfigurationFactoryImpl -Dweblogic.security.jacc.RoleMapperFactory.provider=weblogic.security.jacc.simpleprovider.RoleMapperFactoryImpl -Djava.security.policy==/opt/weblogic12.1.2/wls12120/wlserver/server/lib/weblogic.policy

A particular nasty requirement here is that WebLogic requires the Java SE security manager to be activated. The Java SE security manager is used for code level protection, which is a level of protection that is rarely if ever needed in Java EE as it's extremely rare that a server will run untrusted code (like e.g. a browser which runs an untrusted Applet from the Internet). Activating the security manager can have a huge performance impact and this is typically not recommended for application servers. Yet, WebLogic for some reason as the only server in the string of servers that I tested requires this.

After starting WebLogic this way it crashed immediately with an exception about not having access to read some internal file. To quickly remedy this I took the extreme measure of just granting anything to everything, which kinda beats using the security manager in the first place and which more or less proves the unnecessity of the WebLogic security manager requirement. To do this I put the following in the referenced policy file:

grant { 
    permission java.security.AllPermission;
};

Needless to say this is for testing only and should not be used for a production environment where code level security really is required. After this workaround WebLogic booted, but then started to throw out of memory exceptions. These appeared to be caused by a sudden lack of memory in the permanent generation. After I boosted this from 256MB to 512MB WebLogic was stable again.

WebSphere 8.5 proved to be even worse. Here too JACC is not enabled by default, but it can be enabled via the admin console in the security section. However WebSphere does not seem to ship with any default JACC provider. There's only a provider available that's a client for something called the Tivoli Access Manager, which after some research appeared to be some kind of authorization server that needed to be installed separately. For a moment I pondered whether I should try to install this, but the WebSphere installation had taken a horrendous amount of time already and it would be over the top and an immense overkill to have an external authorization server running just to get the thread local Subject inside a Servlet.

"Luckily" there's also an IBM proprietary way to get the Subject, so I used that instead:

Subject subject = com.ibm.websphere.security.auth.WSSubject.getCallerSubject();

Finally in JEUS 8 too JACC is not enabled by default. It can be enabled by commenting out or removing the existing repository-service and custom-authorization-service elements in the authorization section of [jeus install dir]/domains/jeus_domain/config/domain.xml and adding the empty jacc-service element. It then becomes just this:

<authorization>
    <jacc-service/>
</authorization>

Although there's no security manager used here and thus no overhead from that, TMaxSoft warns in its JEUS 7 security manual that the default JACC provider that will be activated by the above shown configuration is mainly for testing and doesn't advise to use it for real production usage. Curiously though, I found after some experimentation that the non-JACC default authorization provider in JEUS is pretty much just like JACC but with some small differences. This may be an interesting thing to take a deeper look at in a future article.

Additionally JEUS 8 also offered a proprietary way to get hold of the Subject when JACC is not enabled:

Subject subject = jeus.security.impl.login.CommonLoginService.doGetCurrentSubject().toJAASSubject();

Sadly JEUS 8 is the only application server that despite being Java EE 7 certified doesn't implement JASPIC in such a way that it actually works; an authentication module can be installed and it's correctly called for each request, but then the container just does nothing with the user/caller and group/roles that are passed to the handler. Because of this a native login module had to be used for JEUS 8 (the default file based username/password module).

The following shows the result of inspecting the Subject on every server:

JBoss EAP 6.2

Principals
    org.jboss.security.SimpleGroup (name=CallerPrincipal)
        members
            org.jboss.security.SimplePrincipal (name=test)

    org.jboss.security.SimpleGroup (name=Roles)
        members
            org.jboss.security.SimplePrincipal (name=architect)

GlassFish 4.0

Principals
    org.glassfish.security.common.PrincipalImpl (name = test)
    org.glassfish.security.common.Group (name = architect)

Geronimo 3.0.1

Pricipals
    org.apache.geronimo.security.realm.providers.GeronimoUserPrincipal (name="test")
    org.apache.geronimo.security.realm.providers.GeronimoGroupPrincipal (name="architect")
    org.apache.geronimo.security.IdentificationPrincipal
        (name = org.apache.geronimo.security.IdentificationPrincipal[[1392054106031:0x0f9d4c68befbfbee189e0ca0dadd3757d577da8b]]
        (id = SubjectId (name = [1392054106031:0x0f9d4c68befbfbee189e0ca0dadd3757d577da8b], subjectId = 1392054106031))

WebLogic 12.1.2

Principals
    weblogic.security.principal.WLSUserImpl (name = test)
    weblogic.security.principal.WLSGroupImpl (name = architect)

WebSphere 8.5

Principals
    com.ibm.ws.security.common.auth.WSPrincipalImpl (username = test, fullname = defaultWIMFileBasedRealm/test)

PublicCredentials
    com.ibm.ws.security.auth.WSCredentialImpl
        (accessId = user:defaultWIMFileBasedRealm/uid=test,o=defaultWIMFileBasedRealm
         groupIds
            group:defaultWIMFileBasedRealm/cn=architect,o=defaultWIMFileBasedRealm
         realmuniqueusername = defaultWIMFileBasedRealm/uid=test,o=defaultWIMFileBasedRealm
         uniqueusername = uid=test,o=defaultWIMFileBasedRealm
         username = test)

JEUS 8

Principals
    jeus.security.resource.PrincipalImpl (name = test)
    jeus.security.resource.GroupPrincipalImpl 
        (name = architect, 
         description =, 
         individuals = {Principal test=Principal test} (Hashtable of jeus.security.resource.PrincipalImpl to jeus.security.resource.PrincipalImpl), 
         subgroups = [] (empty Vector))
    jeus.security.base.Subject$SerializedSubjectPrincipal (bytes = …)

PrivateCredentials
    jeus.security.resource.Password (algorithm = "base64", password = "YWRtaW4wMDc=", plainPassword = "admin007")
    jeus.security.resource.SystemPassword (algorithm = null, password = "globalpass", plainPassword = "globalpass")

So there you have it; 6 different servers implementing pretty much the same thing in 6 different ways.

What we see is that there are several methods to distinguish between a Principal that represents a user/caller name and one that represents a group/role name. JBoss EAP 6.2 as the only tested server uses a named Group for this. The name of the Group indicated the type of the Principal. Not entirely consistently this is "CallerPrincipal" for the user/caller Principal and "Roles" for the Group/Roles Principals. Pretty much every other server uses the class type for this type of distinction. Of course every server uses its own type. Things like org.glassfish.security.common.PrincipalImpl, org.apache.geronimo.security.realm.providers.GeronimoUserPrincipal, weblogic.security.principal.WLSUserImpl and jeus.security.resource.PrincipalImpl are completely identical; a simple Principal implementation with a single String attribute called "name".

WebSphere 8.5 does do things a little different. It only models the user/caller name as a Principal and when doing that for some reason needs an additional "fullname" attribute, but otherwise this is still pretty much the same thing as what the other servers use. Things are more different when it comes to the group/roles. As the only server tested WebSphere doesn't put these in the Principals collection, but uses the private credentials one for this. This collection is of the general type Object, and thus can hold other things than just Principals. It can be argued whether this is more correct or not. Principals are supposed to identify an entity, but does a group/role identify an entity, or is it more a security related attribute? At any length, WebSphere is the only one that thinks the latter.

We also see that two servers store extra internal data into the Subject. Geronimo for some reason needs a org.apache.geronimo.security.IdentificationPrincipal, while JEUS needs an extra jeus.security.base.Subject$SerializedSubjectPrincipal. In case of JEUS the extra Principal seems to be used to convert from its own proprietary Subject type to the standard JAAS Subject type and back again. It looks like JEUS is the only server that thinks it absolutely needs its very own Subject type. From a quick glance at the Geronimo code it wasn't clear why Geronimo needs the IdentificationPrincipal, but I'm sure there is a solid reason for that.

Finally the group/role Principal in JEUS 8 is special in that it holds a reference to the collection of all individuals who are in that group/role. It may be that this specific linkage is only done when using a small build-in user repository like the local file based ones. In case of an external provider like Facebook it would of course really not be feasible. Also note that the credentials are there in JEUS 8 since we really presented a password when we used the server provided login module. In all other cases we used JASPIC and didn't provide a password.

Looking at example JAAS login modules

As mentioned above, JAAS example login modules are another source that's worth looking at. Specifically interesting is the commit method, where the module is supposed to do the transfer from the user/caller name and group/role names into the Subject.

Geronimo 3.0.1 comes with several example modules. Of those the commit methods are conceptually identical. Given below is the method from the PropertiesFileLoginModule:

    public boolean commit() throws LoginException {
        if(loginSucceeded) {
            if(username != null) {
                allPrincipals.add(new GeronimoUserPrincipal(username));
            }
            for (Map.Entry<String, Set<String>> entry : groups.entrySet()) {
                String groupName = entry.getKey();
                Set<String> users = entry.getValue();
                for (String user : users) {
                    if (username.equals(user)) {
                        allPrincipals.add(new GeronimoGroupPrincipal(groupName));
                        break;
                    }
                }
            }
            subject.getPrincipals().addAll(allPrincipals);
        }
        // Clear out the private state
        username = null;
        password = null;

        return loginSucceeded;
    }

In the security manual of JEUS 7 a couple of JAAS login modules are given as well. Given below is the commit method for the DBRealmLoginModule (there is an English version available as PDF, but it's often moved around and requires a login and is thus unfortunately very hard to link to)

    public boolean commit() throws LoginException {
        if (succeeded == false) {
            return false;
        } else {
            userPrincipal = new PrincipalImpl(username);
            if (!subject.getPrincipals().contains(userPrincipal))
                subject.getPrincipals().add(userPrincipal);

            ArrayList roles = getRoleSets();
            for (Iterator i = roles.iterator(); i.hasNext();) {
                String roleName = (String) i.next();
                logger.debug("Adding role to subject : username = " + username +
                        ", roleName = " + roleName);
                subject.getPrincipals().add(new RolePrincipalImpl(roleName));
            }

            userCredential = new Password(password);
            subject.getPrivateCredentials().add(userCredential);

            username = null;
            password = null;
            domain = null;
            commitSucceeded = true;
            return true;
        }
    }

JBoss ships with a number of login modules, which are essentially JAAS login modules as well. They have abstracted the common commit method to the base class AbstractServerLoginModule, which is shown below:

    public boolean commit() throws LoginException {
        PicketBoxLogger.LOGGER.traceBeginCommit(loginOk);
        if (loginOk == false)
            return false;

        Set<Principal> principals = subject.getPrincipals();
        Principal identity = getIdentity();
        principals.add(identity);
        
        // add role groups returned by getRoleSets.
        Group[] roleSets = getRoleSets();
        for (int g = 0; g < roleSets.length; g++) {
            Group group = roleSets[g];
            String name = group.getName();
            Group subjectGroup = createGroup(name, principals);
            if (subjectGroup instanceof NestableGroup) {
                /*
                 * A NestableGroup only allows Groups to be added to it so we need to add a SimpleGroup to subjectRoles to contain the roles
                 */
                SimpleGroup tmp = new SimpleGroup("Roles");
                subjectGroup.addMember(tmp);
                subjectGroup = tmp;
            }
            // Copy the group members to the Subject group
            Enumeration<? extends Principal> members = group.members();
            while (members.hasMoreElements()) {
                Principal role = (Principal) members.nextElement();
                subjectGroup.addMember(role);
            }
        }
        
        // add the CallerPrincipal group if none has been added in getRoleSets
        Group callerGroup = getCallerPrincipalGroup(principals);
        if (callerGroup == null) {
            callerGroup = new SimpleGroup(SecurityConstants.CALLER_PRINCIPAL_GROUP);
            callerGroup.addMember(identity);
            principals.add(callerGroup);
        }

        return true;
    }

As can be seen the JAAS modules all use the server specific way to store the Principals inside the Subject and this is done in pretty much the same way as the JASPIC container code did. A remarkable difference is that the JBoss JAAS module inserts the caller/user Principal twice; once directly in the root of the principals set and once in a named group, while the JASPIC auth code only used the named group. In the example DBRealmLoginModule of JEUS there's no trace of a reference to all users in a group/role. So this is either just an extra thing, or perhaps JEUS adds this information to the Subject after the JAAS login module has committed. For this article I did not investigate that further.

Conclusion

We looked at a relatively large number of servers here, nearly all the ones that implement the full Java EE profile (and thus support both JASPIC and JACC where the Subject type comes into play). There are more servers out there like Tomcat, TomEE, Jetty and Resin, but I did not look into them. From previous experience I know that Tomcat doesn't use the Subject type internally and only has support to read from a Subject via its JAAS realm. There are a small number of rather obscure other full Java EE implementations like the Hitachi and NEC offerings which I might investigate in a future article.

For the servers tested it seems that most servers could simply use two standardized Principals for the caller/user and group/role Principal. E.g.

  • javax.security.CallerPrincipal
  • javax.security.GroupPrincipal

Each principal would only need a single attribute name of type String. GlassFish, Geronimo and WebLogic could use such types as a direct replacement. JBoss would have to switch from looking at a group node to the class type of the Principal. JEUS could directly use the standard type for the caller/user principal, but may have to stuff the extra information it now puts in to the group/role principal somewhere else (assuming that it indeed really needs this info and we did not just observed some coincidental side-effect of the particular login-module that we used). The only server that really does things differently is WebSphere. Its caller/user principal is mostly equivalent to those of the other servers, but the group/role one is radically different.

To accommodate the extra info a few servers may need to store in the Principal, it might be feasible to define an extra Map<String, Object> attribute on the standardized Principals where this extra server specific info could be stored.

All in all it thus seems this standardization should be possible without too many problems. The introduction of these two small types would likely simplify the far too abstract and complex Java EE security landscape a lot. The question remains though why this wasn't done long ago.

Arjan Tijms

Friday, October 18, 2013

JEUS application server: The story continues

In a previous blog post I wrote about JEUS, a Java EE 7 certified application server that is popular in Korea but pretty much unknown elsewhere. In this follow up I'll report about the latest findings and take a look at how to deploy an application to JEUS and do some debugging.

The previous installment was quite a story, but there's more. The us domain that was previously just gone briefly flickered into existence again, and it indeed offered the much sought after JEUS 7 download link; jeus70_linux_x86.bin. In order to get this I once again had to register a new account, and this time the signup process limited my password to 12 characters and did not allow capitals and non-alphanumerics (the 4 other signup processes that I went through did allow this). Unfortunately after a few hours the site suddenly featured completely different content. It's now largely, but not entirely, identical to the main corporate site, and doesn't feature the JEUS 7 download link anymore.

The tech site appeared to have a forum "hidden" behind the Q & A link. With some 5 to 10 posts per day it's clear that JEUS is definitely very much alive in Korea. (Google translate unfortunately stops working after you click on a post, so the forum is best viewed directly using the Chrome auto-translation).

Just as the entire site, the forum posts are mainly about JEUS 5 (J2EE 1.4, 2003) and JEUS 6 (Java EE 5, 2006). Apparently Java EE 6 (2009) hasn't fully penetrated the Korean market yet, although occasionally Korean users too wonder why JEUS 7 is not out there in the open. After asking on the forum the kind people from TmaxSoft where friendly enough to upload a JEUS 7 trial version again, this time on the English variant of the tech site ((which is rather empty otherwise, but now does feature this unique download)

There appears to be an Eclipse plug-in available after all on the download page, called JMaker, which in the typical ancient versions trend is of course for Eclipse 3.2 (2006) and supports JEUS 5. Next to the download page there's also a "resource room", which has a download page as well that has another Eclipse plug-in called the Jeus Bridge. This one supports Eclipse 3.5 (2009), which is slowly creeping towards the modern world, but unfortunately it too only supports JEUS 5. I got in touch with JEUS' lead developer Yoon Kyung Koo and he let me know they're attempting to open up the plug-in, which I take as meaning it will eventually appear in the Eclipse marketplace and such.

Downloads

So far I've found the following download links:

JEUS binary Size Initial release refJava EE versionDocumentation Dev preview Public download Site
jeus50-unix-generic.bin 265 MB May 2005 J2EE 1.4 JEUS 5 X X Korean tech site
jeus50-win.exe 205 MB X X
jeus60-winx86-en.exe 147 MB June, 2007 Java EE 5 JEUS v6.0 Fix8 Online Manual (11/2011) X X
jeus60-winx64-ko.exe 190 MB X X
jeus60_unix_generic_ko.bin 268 MB X X
jeus70_linux_x86.bin 133 MB June, 2012 Java EE 6 JEUS v7.0 Fix#1 Korean Online Manual (04/2013)

JEUS v7.0 Fix#1 English PDF set (08/2013)
V X US corporate site (dead link, no other source)
jeus70_unix_generic.bin 546 MB X X English tech site
jeus70_win_x86.exe 441 MB X X
jeus70_win_x64.exe 445 MB X X
jeus80_unix_generic_ko.bin 165 MB Est. Q4 2014 Java EE 7 [no documentation yet] V V Int. corporate site

 

Deploying to JEUS 8

Just as GlassFish, WebLogic and Geronimo among others, it seems it's only possible to deploy an application to JEUS via a running server. The simplicity that JBoss, TomEE and Tomcat offer by simply copying an archive to a special directory is unfortunately not universally embraced by Java EE implementations.

In order to deploy an app to JEUS 8 we first start it again:

./bin/startDomainAdminServer -domain jeus_domain -u administrator -p admin007
After it's started we connect to the running instance via the jeusadmin command:
./bin/jeusadmin -u administrator -p admin007
In my home directory I have a small war that I normally use for quick JASPIC tests. In order to deploy this we first have to install it via the admin console that we just opened:
[DAS]jeus_domain.adminServer>installapp /home/arjan/abc.war
Successfully installed the application [abc_war].
This command will copy the .war to [jeus install dir]/domains/jeus_domain/.uploaded and [jeus install dir]/domains/jeus_domain/.applications. After this kind of pre-staging step we can do the actual deploy:
[DAS]jeus_domain.adminServer>deploy abc_war -all
deploy the application for the application [abc_war] succeeded.
Note that the first argument for the deploy command is "abc_war". This is the application ID that is assigned to the application when it was installed and defaults to the full archive name with all periods replaced by underscores. There is more information available for each command via the help command, e.g. help deploy will list all arguments with some short explanation.

The deploy command will add a new entry to [jeus install dir]/domains/jeus_domain/config/domain.xml:

<deployed-application>
    <id>abc_war</id>
    <path>/home/arjan/jeus8/domains/jeus_domain/.applications/abc_war/abc.war</path>
    <type>WAR</type>
    <targets>
        <all-target/>
    </targets>
    <options>
        <classloading>ISOLATED</classloading>
        <fast-deploy>false</fast-deploy>
        <keep-generated>false</keep-generated>
        <shared>false</shared>
        <security-domain-name>SYSTEM_DOMAIN</security-domain-name>
    </options>
</deployed-application>

Requesting http://localhost:8808/abc/index.jsp?doLogin=true in a browser did load the correct test page, but JASPIC itself didn't work as expected. We should see the authenticated user's name and role, but this didn't happen:

This is not entirely unexpected as most servers need a special configuration (typically a vendor specific group-to-role mapping) before JASPIC starts working and I haven't added any such configuration for JASPIC yet.

Debugging JEUS 8

In order to find out where things go wrong it's always handy to do a little debugging. Maybe it's not the configuration at all, but perhaps the @WebListener isn't called that installs the SAM, or maybe it is, but then the SAM isn't called, etc. But without any tooling support, how do we debug an application running on JEUS? Using good-old System.out debugging gave me some hints. The SAM is not called for public pages (it should be) but it is indeed called for protected pages. Unfortunately despite being called for a protected page there still wasn't any sight of the authenticated name nor roles. Obviously we need some -real- debugging.

The most straightforward method to do that in this case is via remote debugging, which means we need to add a special agentlib parameter when starting up JEUS. [jeus install dir]/bin/startDomainAdminServer seemed like an obvious place as this starts a Java application that looks to be the server. Unfortunately it appeared to be only a launcher that launches a new process representing the real server and then exits. After some digging I found out that additional parameters for this "real server" can be specified in the same [jeus install dir]/domains/jeus_domain/config/domain.xml file that we saw before via the jvm-option element:


    
        adminServer
        
        
            
                -Xmx1024m -XX:MaxPermSize=128m 
                -agentlib:jdwp=transport=dt_socket,address=1044,server=y,suspend=y
            
        
        
    

Starting JEUS 8 again will cause it to halt and wait for a debug connection:

[launcher-1] [Launcher-0012] Starting the server [adminServer] with the command
 /opt/jdk1.7.0_40/jre/bin/java -DadminServer -Xmx1024m -XX:MaxPermSize=128m -agentlib:jdwp=transport=dt_socket,address=1044,server=y,suspend=y -server -Xbootclasspath/p:/home/arjan/jeus8/lib/system/extension.jar -classpath /home/arjan/jeus8/lib/system/bootstrap.jar -Djava.security.policy=/home/arjan/jeus8/domains/jeus_domain/config/security/policy -Djava.library.path=/home/arjan/jeus8/lib/system -Djava.endorsed.dirs=/home/arjan/jeus8/lib/endorsed -Djeus.properties.replicate=jeus,sun.rmi,java.util,java.net -Djeus.jvm.version=hotspot -Djava.util.logging.config.file=/home/arjan/jeus8/bin/logging.properties -Dsun.rmi.dgc.server.gcInterval=3600000 -Djava.util.logging.manager=jeus.util.logging.JeusLogManager -Djeus.home=/home/arjan/jeus8 -Djava.net.preferIPv4Stack=true -Djeus.tm.checkReg=true -Dsun.rmi.dgc.client.gcInterval=3600000 -Djeus.domain.name=jeus_domain -Djava.naming.factory.initial=jeus.jndi.JNSContextFactory -Djava.naming.factory.url.pkgs=jeus.jndi.jns.url -Djeus.server.protectmode=false -XX:+UnlockDiagnosticVMOptions -XX:+LogVMOutput -XX:LogFile=/home/arjan/jeus8/domains/jeus_domain/servers/adminServer/logs/jvm.log jeus.server.admin.DomainAdminServerBootstrapper -domain jeus_domain -u administrator -server adminServer .
[2013.10.18 23:36:56][2] [launcher-1] [Launcher-0014] The server[adminServer] is being started ...
Listening for transport dt_socket at address: 1044

After this point we can attach to it via Eclipse:

If everything went well, you'll see JEUS 8's thread appearing in the debug view:

Setting a break-point inside the SAM and requesting the protected page again indeed breaks us into the debugger:

Without diving much further in the details it appears that despite passing the JASPIC TCK, the handler (shown in the image above as JeusCallbackHandler) does exactly *nothing*, so a standard JASPIC module can never really work. I confirmed that this is indeed the case with JEUS' lead developer. Remember though that the JEUS version being tested here is just a developer preview and the final version will most likely implement the required behavior correctly.

Interestingly, after playing around with JEUS' authentication some more it appeared that JEUS uses JASPIC auth modules to implement the build-in Servlet authentication methods. This is almost certainly also without using the Callback handler (as it does absolutely nothing, what would be the point?) so it's not in a fully JASPIC compliant way, but it's nevertheless really interesting. All other Java EE servers that I looked at don't actually use Java EE authentication as their native authentication system, but just wrap the JASPIC interfaces on top of whatever else they're using natively. JEUS has an exclusive here by basing their authentication directly on JASPIC. If they only can make it fully compliant JEUS will be one of the most interesting servers for JASPIC our there.

Redeploying

Deploying a single war once that's already in your home is one thing, but how to easily see your changes after editing in your IDE? Unfortunately there's no real easy solution for this without tooling support, but I cooked up the following admittedly very crude script that when executed from the (Maven) project folder somewhat gets the job done:

#!/bin/sh

WAR_HOME=/home/arjan/eclipse43ee/workspace/auth/target/

# Rebuild project
mvn package
cp ${WAR_HOME}/auth-0.0.1-SNAPSHOT.war ${WAR_HOME}/auth.war

# Redeploy to JEUS
~/jeus8/bin/jeusadmin -u administrator -p admin007 "undeploy auth_war"
~/jeus8/bin/jeusadmin -u administrator -p admin007 "uninstall auth_war"
~/jeus8/bin/jeusadmin -u administrator -p admin007 "installapp ${WAR_HOME}/auth.war"
~/jeus8/bin/jeusadmin -u administrator -p admin007 "deploy auth_war -all"

Meanwhile I have a tail on the log in another console:

tail -f domains/jeus_domain/servers/adminServer/logs/JeusServer.log

It works, but it's not exactly the experience you'd normally expect when working with an IDE.

Finally, we stop the server again via the following command:

./bin/stopServer -host localhost -u administrator -p admin007

Conclusion

In this article we haven't really extensively tested JEUS, but we just went through the steps of finding the download links, installing it (see previous article), deploying a really small application, and doing some debugging on the Java EE 7 developer preview. I didn't yet test a bigger application such as the OmniFaces showcase, something I still plan to do for JEUS 7 (the production ready Java EE 6 implementation).

By putting up English documentation and downloadable trial versions of JEUS 7 as well as a developer preview of JEUS 8 TmaxSoft has made some small but important steps to get JEUS out of international obscurity. Still they have some way to go. While we saw that deployment and debugging can be done without tool support, very few developers would consider this a viable option and because of that may not be really eager to try out JEUS.

An important next step for TmaxSoft will be to indeed make the required tooling easily and freely available.

Arjan Tijms