Saturday, July 23, 2016

Simplified custom authorization rules in Java EE

In a previous article we looked at implementing a Java EE authorization module using the JACC specification. This module implemented the default authorization rules as specified by the JACC-, Servlet- and EJB specifications. In this article we go beyond that default algorithm and take a look at providing our own custom authorization rules.

In order to implement custom rules, one would traditionally ship an entire JACC provider with factory, configuration and policy. Not only is this a lot of code (JACC doesn't have any code that can be reused, for the smallest change everything needs to be implemented from scratch), it's also problematic that a JACC provider is global for the entire application server, while authorization rules are almost always specific for an individual application. Even when you adhere to the best practice of using one application per server, it's still quite a hassle to re-install the JACC provider after every little change separately from the application.

For this article we're therefor going to employ a different approach; use CDI to delegate from a single JACC provider that's installed once, to an application specific CDI bean. Incidentally this is largely the same thing Soteria/JSR 375 is doing for authentication mechanisms, which the key difference that the Java EE authentication SPI -does- allow authentication modules to be installed per application.

For the actual custom authorization rule we took inspiration from a rather interesting question and answer on StackOverflow:

Is it possible to determine group membership of a user on demand instead of when logging in via a ServerAuthModule (JASPIC)?

As it appears, the answer to this question is yes ;)

In order to come to a solution for the above stated problem we first took the original JACC provider from the previous article and refactored it a little. The permissions that were previously put in the intermediate base class TestPolicyConfigurationPermissions were factored out to a separate struct like class:

public class SecurityConstraints {

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

    // + getters/setters
Furthermore a new class was introduced that holds the caller (name) principal, the (mapped) roles and the raw set of unmapped principals (which are often server specific):
public class Caller {

    private Principal callerPrincipal;
    private List<String> roles;
    private List<Principal> unmappedPrincipals;

    // + getters/setters
Next we define an interface for the application to implement. Our JACC provider will call this at certain points during the authorization process:
public interface AuthorizationMechanism {
    
    default Boolean preAuthenticatePreAuthorize(Permission requestedPermission, SecurityConstraints securityConstraints) {
        return null;
    }
    
    default Boolean preAuthenticatePreAuthorizeByRole(Permission requestedPermission, SecurityConstraints securityConstraints) {
        return null;
    }

    default Boolean postAuthenticatePreAuthorize(Permission requestedPermission, Caller caller, SecurityConstraints securityConstraints) {
        return null;
    }
    
    default Boolean postAuthenticatePreAuthorizeByRole(Permission requestedPermission, Caller caller, SecurityConstraints securityConstraints) {
        return null;
    }
}
As can be seen we distinguish for authorization decisions before authentication has taken place and thereafter, and for those at the start of an authorization decision and right before it comes time to check for role based permissions. The difference between those last two moments is that for the latter the tests for excluded permissions (those granted to no one) and unchecked (permissions granted to everyone) have already been performed. (Note that methods have been used for now, but perhaps events are the better solution here)

With these artefacts in place we can now modify the JACC Policy class to request a bean via CDI that implements the AuthorizationMechanism interface, and if it exists call one of the appropriate methods. The following shows an excerpt of this:

boolean postAuthenticate = domain.getPrincipals().length > 0;

AuthorizationMechanism mechanism = getBeanReferenceExtra(AuthorizationMechanism.class);
Caller caller = null;

if (postAuthenticate) {
    caller = new Caller(
        roleMapper.getCallerPrincipalFromPrincipals(currentUserPrincipals),
        roleMapper.getMappedRolesFromPrincipals(currentUserPrincipals),
        currentUserPrincipals);
}

if (mechanism != null) {
    Boolean authorizationOutcome = postAuthenticate? 
        mechanism.postAuthenticatePreAuthorize(requestedPermission, caller, securityConstraints) :
        mechanism.preAuthenticatePreAuthorize(requestedPermission, securityConstraints);
    
    if (authorizationOutcome != null) {
        return authorizationOutcome;
    }
}

In the code above getBeanReferenceExtra uses CDI to fetch a bean of type AuthorizationMechanism. This method works around a bug in Payara where the so-called component namespaces aren't being set. This bug is pretty much the same as also existed for calling a JASPIC SAM. If the mechanism is not null, then depending on whether authentication has already happened or not either the postAuthenticatePreAuthorize or the preAuthenticatePreAuthorize is called.

The JACC Policy doesn't tell us explicitly if the call is before or after authentication, but we can deduct this by looking at the passed-in principals. The assumption is that before authentication there are no principals at all, and after authentication there's at least one (if there was no actual authentication being done, the so-called "unauthenticated caller principal" is added).

For further details see the full source code.

With the JACC provider in place we can now create a Java EE web application that takes advantage of it. We'll start with a custom JSR 375 IdentityStore that only authenticates a single user named "test" and doesn't return any groups:

@RequestScoped
public class CustomIdentityStore implements IdentityStore {
      
    public CredentialValidationResult validate(UsernamePasswordCredential usernamePasswordCredential) {
        
        if (usernamePasswordCredential.getCaller().equals("test") && 
            usernamePasswordCredential.getPassword().compareTo("secret1")) {
            
            return new CredentialValidationResult(
                new CallerPrincipal("test"), 
                null // no static groups, dynamically handled via authorization mechanism 
            );
        }
        
        return INVALID_RESULT;
    }
    
}
We also add a custom JSR 375 HttpAuthenticationMechanism, one that's just used for testing purposes:
@RequestScoped
public class CustomAuthenticationMechanism implements HttpAuthenticationMechanism {
    
    @Inject
    private IdentityStore identityStore;

    @Override
    public AuthStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthException {

        if (request.getParameter("name") != null && request.getParameter("password") != null) {

            CredentialValidationResult result = identityStore.validate(
                new UsernamePasswordCredential(request.getParameter("name"), request.getParameter("password")));

            if (result.getStatus() == VALID) {
                return httpMessageContext.notifyContainerAboutLogin(
                    result.getCallerPrincipal(), result.getCallerGroups());
            } else {
                throw new AuthException("Login failed");
            }
        } 

        return httpMessageContext.doNothing();
    }
    
}
Note that we could have omitted both the custom identity store and custom authentication mechanism and instead configured the default embedded store and the default BASIC authentication mechanism, but now we more clearly see what's exactly happening in the authentication process. Also note that the HttpAuthenticationMechanism is a CDI based HTTP specific variant of the ServerAuthModule that was mentioned in the SO question cited above.

Let's now finally look at our custom authorization rule, which basically looks as follows:

@ApplicationScoped
public class CustomAuthorizationMechanism implements AuthorizationMechanism {
   
    @Override
    public Boolean postAuthenticatePreAuthorizeByRole(Permission requestedPermission, Caller caller, SecurityConstraints securityConstraints) {
        
        return getRequiredRoles(securityConstraints.getPerRolePermissions(), requestedPermission)
                .stream()
                .anyMatch(role -> isInRole(caller.getCallerPrincipal().getName(), role));
    }
}
What's happening here is that our custom code is being asked to authorize the caller for some requested permission. Such permission can e.g. be a WebResourcePermission for a given protected path like /foo/bar.

In order to do the on demand membership check as was asked for in the SO question we start with using the getRequiredRoles method and the collected SecurityConstraints to obtain a list of roles that are required for the requested permission. We then look if the current caller is in any of those roles using the isInRole method. This method can do whatever backend lookup is needed, but for an example application we'll mock it using a simple map where we put caller "test" in roles "foo", "bar" and "kaz".

For completeness, the getRequiredRoles method gets the roles that are required for a requested permission as follows:

return perRolePermissions
            .entrySet().stream()
            .filter(entry -> entry.getValue().implies(requestedPermission))
            .map(e -> e.getKey())
            .collect(toList());
In order to test if the authorization rule works we add a protected Servlet as follows:
@DeclareRoles({ "foo", "bar", "kaz" })
@WebServlet("/protected/servlet")
@ServletSecurity(@HttpConstraint(rolesAllowed = "foo"))
public class ProtectedServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        response.getWriter().write("This is a protected servlet \n");

        String webName = null;
        if (request.getUserPrincipal() != null) {
            webName = request.getUserPrincipal().getName();
        }

        response.getWriter().write("web username: " + webName + "\n");

        response.getWriter().write("web user has role \"foo\": " + request.isUserInRole("foo") + "\n");
        response.getWriter().write("web user has role \"bar\": " + request.isUserInRole("bar") + "\n");
        response.getWriter().write("web user has role \"kaz\": " + request.isUserInRole("kaz") + "\n");
    }

}
The Servlet shown here is protected by the role "foo", so with the above given IdentityStore which doesn't return any groups/roles for our caller "test", we would normally not be able to access this Servlet. Yet, with the custom authorization rule in place we're able to access this Servlet just fine when authenticating as user "test".

Next we added a preAuthenticatePreAuthorize method to our authorization mechanism with some logging and also added logging to our existing postAuthenticatePreAuthorizeByRole method. We then deployed the application as "custom-authorization" to Payara 4.1.1.162 and requested http://localhost:8080/custom-authorization/protected/servlet?name=test&password=secret1. This resulted in the following log entries:

preAuthenticatePreAuthorize called. Requested permission: ("javax.security.jacc.WebUserDataPermission" "/protected/servlet" "GET")
preAuthenticatePreAuthorize called. Requested permission: ("javax.security.jacc.WebResourcePermission" "/protected/servlet" "GET")

validateRequest called. Authentication mandatory: true

postAuthenticatePreAuthorizeByRole called. Requested permission: ("javax.security.jacc.WebResourcePermission" "/protected/servlet" "GET")

postAuthenticatePreAuthorizeByRole called. Requested permission: ("javax.security.jacc.WebRoleRefPermission" "org.omnifaces.authorization.ProtectedServlet" "foo")
postAuthenticatePreAuthorizeByRole called. Requested permission: ("javax.security.jacc.WebRoleRefPermission" "org.omnifaces.authorization.ProtectedServlet" "bar")
postAuthenticatePreAuthorizeByRole called. Requested permission: ("javax.security.jacc.WebRoleRefPermission" "org.omnifaces.authorization.ProtectedServlet" "kaz")
What we see happening here is that our authorization mechanism is initially called twice before authentication. First to see if the request can be aborted early by checking if the protocol (http/https) is allowed, and secondly to see if the resource is allowed to be accessed publicly. If the resource is allowed to be accessed publicly, authentication is still being asked for, but it's not mandatory then.

For the request to "/protected/servlet" authentication is mandatory as shown above. Since authentication is thus mandatory, our authorization mechanism is called again after authentication for the same requested WebResourcePermission permission, but this time the caller data corresponding to the authenticated identity is also available. Our method will return "true" here, since ("javax.security.jacc.WebResourcePermission" "/protected/servlet" "GET") will turn out to require the role "foo", and our mock "isInRole" method will return "true" for caller "test" and role "foo".

We therefor continue to our requested resource, which means the servlet will be invoked.

Interesting to note here is that HttpServletRequest#isUserInRole is not implemented internally by checking a simple list of roles, but by calling our authorization module with a requested permission ("javax.security.jacc.WebRoleRefPermission" "org.omnifaces.authorization.ProtectedServlet" "[role name]"). The three isUserInRole calls that the servlet shown above does therefor show up in the log as three calls to our authorization mechanism, each for a different role.

Also interesting to note is that our authorization mechanism handles a requested permission to access "/protected/servlet" and a requested permission for isUserInRole("foo") completely identical. If necessary the authorization mechanism can of course inspect the requested permission, but for this use case that wasn't necessary.

To contrast the call to the protected servlet, let's also take a quick look at the log entries resulting from a call to a public servlet that's otherwise identical to the protected one:

preAuthenticatePreAuthorize called. Requested permission: ("javax.security.jacc.WebUserDataPermission" "/public/servlet" "GET")
preAuthenticatePreAuthorize called. Requested permission: ("javax.security.jacc.WebResourcePermission" "/public/servlet" "GET")

validateRequest called. Authentication mandatory: false

postAuthenticatePreAuthorizeByRole called. Requested permission: ("javax.security.jacc.WebRoleRefPermission" "org.omnifaces.authorization.PublicServlet" "foo")
postAuthenticatePreAuthorizeByRole called. Requested permission: ("javax.security.jacc.WebRoleRefPermission" "org.omnifaces.authorization.PublicServlet" "bar")
postAuthenticatePreAuthorizeByRole called. Requested permission: ("javax.security.jacc.WebRoleRefPermission" "org.omnifaces.authorization.PublicServlet" "kaz")
What we see here is that our authorization mechanism is initially called twice again, but since the default authorization algorithm will grant access to the anonymous caller the subsequent authentication is not mandatory and our authorization mechanism will also not be called a second time for the same resource permission.

For more details see the full source code of the example application.

Conclusion

We've seen that custom authorization rules allow us to do things that are normally not thought possible using Java EE security. Java EE's security model is clearly much more advanced than just the basic ability to check for roles, but much of its power has likely been untapped by many.

The reason for this is the relative obscurity of the JACC specification, the difficulty to install a provider and the very large amount of work required for even the smallest amount of custom authorization logic. The approach presented in this article allows us to re-use an existing JACC provider and provide custom authorization logic with a minimal amount of code.

What we presented here is only a prototype, but it may serve as a basis for authorization in the Java EE Security API (JSR 375).

Arjan Tijms

Friday, June 3, 2016

The state of portable authentication in Java EE, mid 2016 update

In the beginning of this year and two months prior to that we looked at how well modern Java EE servers supported portable authentication (JASPIC) in Java EE. In this article we look at the current state of the union.

Originally the situation didn't looked that rosy, but since then things have been steadily improving. Since last time new versions of Payara, WildFly and Liberty have been released, while TomEE now also supports JASPIC. No new versions of Oracle's WebLogic and GlassFish were released. New tests have been added for registering a session with a custom principal and EJB propagation after register session.

The results of running the latest series of JASPIC tests are shown below:

Running the Java EE 7 samples JASPIC tests
Module Test Payara 4.1.1.163 snapshot WildFly 10.0.0 Liberty 9 beta 2016.5 TomEE 7.0.0
async-authentication testBasicAsync
Passed
Passed
Passed
Passed
basic-authentication testProtectedPageNotLoggedin
Passed
Passed
Passed
Passed
basic-authentication testProtectedPageLoggedin
Passed
Passed
Passed
Passed
basic-authentication testPublicPageLoggedin
Passed
Passed
Passed
Passed
basic-authentication testPublicPageNotLoggedin
Passed
Passed
Passed
Passed
basic-authentication testPublicAccessIsStateless
Passed
Passed
Passed
Passed
basic-authentication testProtectedAccessIsStateless
Passed
Passed
Passed
Passed
basic-authentication testProtectedAccessIsStateless2
Passed
Passed
Passed
Passed
basic-authentication testProtectedThenPublicAccessIsStateless
Passed
Passed
Passed
Passed
custom-principal testProtectedPageLoggedin
Passed
Passed
Passed
Passed
custom-principal testPublicPageLoggedin
Passed
Passed
Passed
Passed
custom-principal testPublicAccessIsStateless
Passed
Passed
Passed
Passed
custom-principal testProtectedAccessIsStateless
Passed
Passed
Passed
Passed
custom-principal testProtectedAccessIsStateless2
Passed
Passed
Passed
Passed
custom-principal testProtectedThenPublicAccessIsStateless
Passed
Passed
Passed
Passed
dispatching testBasicForwardViaProtectedResource
Passed
Passed
Passed
Passed
dispatching testBasicForwardViaPublicResource
Passed
Passed
Passed
Passed
dispatching testBasicIncludeViaPublicResource
Passed
Passed
Passed
Passed
dispatching-jsf-cdi testCDIForwardViaProtectedResource
Passed
Passed
Passed
Passed
dispatching-jsf-cdi testCDIForwardViaPublicResource
Passed
Passed
Passed
Passed
dispatching-jsf-cdi testCDIIncludeViaPublicResource
Passed
Passed
Passed
Passed
dispatching-jsf-cdi testJSFwithCDIForwardViaPublicResource
Passed
Passed
Passed
Passed
dispatching-jsf-cdi testJSFwithCDIForwardViaProtectedResource
Passed
Passed
Passed
Passed
dispatching-jsf-cdi testJSFwithCDIIncludeViaPublicResource
Failure
Failure
Failure
Passed
dispatching-jsf-cdi testJSFForwardViaPublicResource
Passed
Passed
Passed
Passed
dispatching-jsf-cdi testJSFForwardViaProtectedResource
Passed
Passed
Passed
Passed
dispatching-jsf-cdi testJSFIncludeViaPublicResource
Failure
Failure
Failure
Passed
ejb-propagation publicServletCallingProtectedEJB
Passed
Passed
Passed
Passed
ejb-propagation protectedServletCallingProtectedEJB
Passed
Passed
Passed
Passed
ejb-propagation publicServletCallingPublicEJBThenLogout
Passed
Passed
Passed
Passed
ejb-propagation protectedServletCallingPublicEJB
Passed
Passed
Passed
Passed
invoke-ejb-cdi protectedInvokeCDIFromSecureResponse
Passed
Passed
Failure
Passed
invoke-ejb-cdi protectedInvokeCDIFromCleanSubject
Passed
Passed
Passed
Passed
invoke-ejb-cdi protectedInvokeCDIFromValidateRequest
Passed
Passed
Failure
Passed
invoke-ejb-cdi publicInvokeCDIFromSecureResponse
Passed
Passed
Failure
Passed
invoke-ejb-cdi publicInvokeCDIFromValidateRequest
Passed
Passed
Failure
Passed
invoke-ejb-cdi publicInvokeCDIFromCleanSubject
Passed
Passed
Passed
Passed
invoke-ejb-cdi protectedInvokeEJBFromSecureResponse
Passed
Passed
Passed
Passed
invoke-ejb-cdi protectedInvokeEJBFromCleanSubject
Passed
Passed
Passed
Passed
invoke-ejb-cdi protectedInvokeEJBFromValidateRequest
Passed
Passed
Passed
Passed
invoke-ejb-cdi publicInvokeEJBFromSecureResponse
Passed
Passed
Passed
Passed
invoke-ejb-cdi publicInvokeEJBFromValidateRequest
Passed
Passed
Passed
Passed
invoke-ejb-cdi publicInvokeEJBFromCleanSubject
Passed
Passed
Passed
Passed
jacc-propagation callingJACCWhenAuthenticated
Passed
Failure
Failure
Failure
jacc-propagation callingJACCWhenAuthenticated
Passed
Failure
Failure
Failure
jacc-propagation callingJACCWhenNotAuthenticated
Passed
Passed
Failure
Failure
lifecycle testBasicSAMMethodsCalled
Passed
Passed
Passed
Passed
lifecycle testLogout
Passed
Passed
Passed
Passed
register-session testRemembersSessionEJB
Passed
Failure
Passed
Passed
register-session testJoinSessionIsOptional
Passed
Passed
Failure
Passed
register-session testRemembersSession
Passed
Passed
Passed
Passed
register-session testRemembersSessionEJB
Passed
Failure
Passed
Passed
register-session testJoinSessionIsOptional
Passed
Passed
Failure
Passed
register-session testRemembersSession
Passed
Passed
Passed
Passed
status-codes test404inResponse
Passed
Passed
Passed
Passed
status-codes test404inResponse
Passed
Passed
Passed
Passed
wrapping testResponseWrapping
Passed
Passed
Passed
Passed
wrapping testRequestWrapping
Passed
Passed
Passed
Passed

Not directly shown in the table, but one of the biggest changes is that Liberty 9 beta 2016.5 no longer needs JASPIC to be activated in a problematic and proprietary way. Historically Liberty was one of the most problematic servers in this area. It originally required all users and groups to be made known to a Liberty specific artefact called a user registry (a kind of identity store). This requirement was later relaxed such that only groups had to be defined, but since group to role mapping was also mandatory, this stil meant defining the same group at least 3 times if no actual group mapping was needed.

In Liberty 9 beta 2016.5 all this overhead is now completely gone, and just like GlassFish and WebLogic, JASPIC just works without any activation whatsoever (with a caveat for GlassFish that by default it does require group to role mapping). One thing to watch out for is that if you do need group to role mapping, you seemingly can't just use Liberty's role mapping file (ibm-application-bnd.xml), but also need to define the above mentioned user registry again. All other servers (that feature a role mapper) allow the mapping of groups set by a JASPIC SAM via only a single XML file. When asked about this, the Liberty lead mentioned that this is due to backwards compatibility, which does make sense. It's also not such a bad thing; if you want to use the proprietary group to role mapping feature it's only a relatively small extra step to also activate a proprietary artefact such as the user registry.

As far as JASPIC functionality goes, Liberty keeps failing the CDI related tests and the JACC ones. The JACC ones are a clear spec violation (confirmed by the JACC and JASPIC spec lead), but since it concerns a requirement that was never enforced (namely, JACC has to be available and used by default) it's likely far from trivial to just fix this. Liberty also fails a test where the authenticated user is changed during the same HTTP session. This however clashes with Liberty's protection against a session fixation attack, which for instance happens when a user logs out and without resetting the http session (specifically changing its ID) a new user logs in again. This protection can be disabled by putting <httpSession securityIntegrationEnabled="false"/> in server.xml.

As mentioned above, TomEE is new to JASPIC scene, but what an entrance it makes! In it's very first release it passed all JASPIC tests, safe the JACC ones. TomEE (as not a full Java EE 7 certified server) is however not required to support JACC at all, let alone out of the box. Since TomEE does actually support JACC it may be possible to have it pass these tests as well. Right from the start TomEE also doesn't require any explicit activation of JASPIC. It just works out of the box.

JBoss/WildFly, while still having a very good JASPIC implementation fell a tiny bit behind. It still doesn't pass all JACC tests, even though JACC is in fact available by default (a bug prevents roles to be propagated correctly). Additionally, a new bug was found that prevents roles being propagated to EJB beans when the remember session feature is used. JBoss also still requires the dummy-like marker file (jboss-web.xml) in an archive to activate JASPIC. Even though such marker file is only a small nuisance compared to having to modify the server itself, it does make JBoss the sole server now that requires such activation at all (but as mentioned above, GlassFish/Payara requires a proprietary group to role mapping file to be present).

Payara also suffered from a newly discovered bug, this one involving the combination of the remember session feature and a custom principal. This was however quickly fixed in the 4.1.1.163 branch and Payara remains one of the strongest JASPIC implementations. There's essentially only one minor failure involving a not too common case of including a JSF resource from within a SAM.

Conclusion

7 years after portable authentication was introduced in Java EE 6 and 3 years after its revision in Java EE 7, JASPIC implementations are now finally getting really compliant. There's a residu of bugs in the various implementations that still need to be fixed, and a few inconveniences to take away in some servers (JBoss' jboss-web.xml and GlassFish/Payara's glassfish-web.xml), but overal things are starting to look very good.

The one big exception remains WebLogic 12.2.1, which currently doesn't work at all after a major bug was introduced in that version that prevents basic authentication to work correctly (effectively a blocker bug that makes running all other tests rather moot)

Arjan Tijms

Wednesday, May 4, 2016

Java EE's mysterious message policy

Users of Java EE authentication (JASPIC) may have noticed that the initialize method of a SAM takes two parameters of type MessagePolicy. But what are these parameters used for? In this article we'll take a somewhat deeper look.

In practice, the overwhelming majority of SAMs only seem to use this MessagePolicy in one way; completely ignore it. As such, there aren't many if any examples available that demonstrate how these passed in policies should actually be enforced.

The JASPIC spec isn't quite clear about this either. It does seem to say in a somewhat cryptic way that the isMandatory method is an alias for the "javax.security.auth.message.MessagePolicy.isMandatory" entry in the MessageInfo map, and that the ProtectionPolicy of the TargetPolicy of the MessagePolicy must be ProtectionPolicy.AUTHENTICATE_SENDER and/or ProtectionPolicy.AUTHENTICATE_CONTENT.

Pretty much the only example we have in code that at least references the MessagePolicy type is within the Java EE reference implementation; GlassFish. Although GlassFish doesn't use JASPIC for the Servlet defined authentication mechanisms (FORM, BASIC, ...), it does uses JASPIC for the authentication mechanism protecting its build-in admin console. This is configured in domain.xml as follows:

<message-security-config auth-layer="HttpServlet">
  <provider-config provider-type="server" 
    provider-id="GFConsoleAuthModule"
    class-name="org.glassfish.admingui.common.security.AdminConsoleAuthModule">
    <request-policy auth-source="sender"/>
    <response-policy/>
    <property name="loginPage" value="/login.jsf"/>
    <property name="loginErrorPage" value="/loginError.jsf"/>
  </provider-config>
</message-security-config>

Unfortunately, despite a minimal request policy being configured here, the actual implementation of AdminConsoleAuthModule does the same thing that basically all other SAMs do: ignore it.

For another potential hint, let's see what the RI actually does internally with the message policy before it passes it to a SAM. For this I started at the entry point of a SAM, when the runtime calls the validateRequest method of the encapsulating context. In order to make things readable for this article, I removed all alternative branches in the code (mostly permutations of client/server modules and new/old modules (new = jaspic, old = the GlassFish proprietary predecessor of JASPIC) and Servlet/SOAP). With those branches removed and flattening the code found in many helper methods and contained objects, it looks as follows:

// Check if the resource is secured and put as string in Map
isMandatory = !webSecMgr.permitAll(req);
if (isMandatory || calledFromAuthenticate) {
   messageInfo.getMap().put(IS_MANDATORY, TRUE.toString());
}

// Get “true” / “false” string from map, convert it to Boolean and then to string again
String isMandatoryStr = messageInfo.getMap().get(IS_MANDATORY);
String authContextID = Boolean.valueOf(isMandatoryStr).toString();

// Convert once again to Boolean, and set to MANDATORY or OPTIONAL policy
messagePolicy = Boolean.valueOf(authContextID)? 
    new MessagePolicy[] { MANDATORY_POLICY, null } : // response policy always null
    new MessagePolicy[] { OPTIONAL_POLICY, null }

IDEntry idEntry = configMap.get(“HttpServlet”).idMap.get(providerID);

// Set the definite request policy, but messagePolicy[0] is never null here
// for the “HttpServlet” layer
MessagePolicy requestPolicy = (messagePolicy[0] != null || messagePolicy[1] != null)?
    messagePolicy[0] :      // will always be chosen here 
    idEntry.requestPolicy;  // the policy as parsed from domain.xml, always unused 
   
Entry entry = new Entry(idEntry.moduleClassName, requestPolicy, idEntry.options);

// Pass the message policies into the SAM instance
ServerAuthModule sam = entry.newInstance();
sam.initialize(
    entry.getRequestPolicy(),
    entry.getResponsePolicy(), handler, map);

ModuleInfo moduleInfo = ModuleInfo(sam, map);

ServerAuthModule moduleObj = moduleInfo.getModule();
Map moduleMap = moduleInfo.getMap();
    
ServerAuthContext serverAuthContext = new GFServerAuthContext(moduleObj, moduleMap);

// Invoke the context, which on its turn will invoke the SAM we just initialized 
AuthStatus authStatus = serverAuthContext.validateRequest(messageInfo, subject, null);

As can be seen, despite there being a path for setting the parsed request policy from domain.xml (the idEntry.requestPolicy), the code always chooses between one of two fixed policies; MANDATORY_POLICY or OPTIONAL_POLICY, depending on the same IS_MANDATORY value ("javax.security.auth.message.MessagePolicy.isMandatory") that is put in the message info map.

Those two fixed policies are set as static final variables as follows:

private static final MessagePolicy MANDATORY_POLICY =
    getMessagePolicy(true);
    
private static final MessagePolicy OPTIONAL_POLICY =
    getMessagePolicy(false);
Where the getMessagePolicy() method has the following relevant content (code branches that were never taken have been cut out again):
public static MessagePolicy getMessagePolicy (boolean mandatory) {
        
    List<TargetPolicy> targetPolicies = new ArrayList<TargetPolicy>();
       
    targetPolicies.add(new TargetPolicy(
        null, // No Target
        new ProtectionPolicy() {
            public String getID() {
                return ProtectionPolicy.AUTHENTICATE_SENDER;
            } 
        })
    );
         
    return new MessagePolicy(
        targetPolicies.toArray(new TargetPolicy[targetPolicies.size()]),
        mandatory);
}

Conclusion

The conclusion seems to be, assuming that the RI code is as spec compliant as we can get, that MessagePolicy#isMandatory() is just an alternative for checking the message info map, while the TargetPolicy, Target and ProtectionPolicy are essentially useless for a Servlet Container Profile SAM, since at least for the RI they always have the same constant value. It has to be noted that if a SAM is registered programmatically, the entire initialization of the SAM is under the application's control and the behavior of the server doesn't apply.

Speculating, it might be the case that AUTHENTICATE_SENDER simply means "do what a SAM is supposed to do in validateRequest()" (e.g. authenticating callers), while the potential alternative or additional AUTHENTICATE_CONTENT means "do what a SAM is supposed to do in secureResponse()" (e.g. encrypting the response). If that interpretation would indeed be correct, one may see these message policies as standard switches (properties) for these two methods. E.g. a message policy with AUTHENTICATE_CONTENT and isMandatory() returning true would then mean that the SAM *must* encrypt the response. This latter thing however seems to be just as rare in practice as actually looking at the message policy is.

A quick glimpse at the SOAP Profile, and some of the implementation code in the RI, revealed that the entire message policy concept may have some more use over there. As JASPIC was originally designed with many potential other profiles in mind (e.g. for EJB, JMS, etc), it could also well be that the concept was designed for a future that just never came to be.

Arjan Tijms

Saturday, April 2, 2016

Servlet 4.0's mapping API previewed in Tomcat 9.0 m4

Without doubt one of the most important Servlet implementations is done by Tomcat. Tomcat serves, or has served, as the base for Servlet functionality in a number of Java EE application servers and is one of the most frequently used standalone Servlet containers.

Contrary to what is often thought, Tomcat is not the reference implementation for Servlet; the build-in Servlet container of GlassFish is. Even though important parts of that GlassFish Servlet implementation are in fact based on Tomcat it's a different code base. Normally GlassFish being the RI implements new spec level functionality first, but unfortunately during the Java EE 8/Servlet 4.0 cycle the GlassFish team is working on other assignments and hasn't worked out a schedule to incorporate this new functionality.

Luckily Tomcat has taken the initiative and in Tomcat 9.0.0.M4 the proposed Servlet 4.0 Mapping API has been implemented.

This API allows Servlet users to find out via which mapping a Servlet was being called. E.g. a Servlet can be mapped by a user to both "/foo/*" and "*.bar" among others. Especially for frameworks it can be important to know what the mapping was, since not rarely something (typically a template file) has to be loaded based on what the * from the above example was. And not only that, if links have to be generated they often need to use the same mapping that was used to call the Servlet.

The current way to do this is a little hairy and therefor quite error prone; it requires checking against the many components of the request URI. The new proposed API greatly simplifies this via the new HttpServletRequest#getMapping() method:

public default Mapping getMapping() {
    // ...
}
The new Mapping type that can be seen in the signature of the above method looks as follows:
/**
 * Represents how the request from which this object was obtained was mapped to
 * the associated servlet.
 *
 * @since 4.0
 */
public interface Mapping {

    /**
     * @return The value that was matched or the empty String if not known.
     */
    String getMatchValue();

    /**
     * @return The {@code url-pattern} that matched this request or the empty
     *         String if not known.
     */
    String getPattern();

    /**
     * @return The type of match ({@link MappingMatch#UNKNOWN} if not known)
     */
    MappingMatch getMatchType();
}
The MappingMatch is an enumeration of the different types of possible mappings as shown below:
/**
 * Represents the ways that a request can be mapped to a servlet
 *
 * @since 4.0
 */
public enum MappingMatch {

    CONTEXT_ROOT,
    DEFAULT,
    EXACT,
    EXTENSION,
    IMPLICIT,
    PATH,
    UNKNOWN
}
To test out the new functionality the following test Servlet was used:
@WebServlet({"/path/*", "*.ext", "", "/", "/exact"})
public class Servlet extends HttpServlet {
    
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        
        Mapping mapping = request.getMapping();
        
        response.getWriter()
                .append("Mapping match:")
                .append(mapping.getMatchType().name())
                .append("\n")
                .append("Match value:")
                .append(mapping.getMatchValue())
                .append("\n")
                .append("Pattern:")
                .append(mapping.getPattern());
    }

}

As can be seen this Servlet is mapped via a path mapping ("/path/*"), extension mapping ("*.ext"), context root mapping (""), default mapping ("/") and a single exact mapping ("/exact"). We deployed this Servlet via an application called "servlet4" to a Tomcat 9.0 M4 instance and subjected it to a couple of requests via a browser. The results are shown below:

Path mapping
http://localhost:8080/servlet4/path/foo

Mapping match:PATH
Match value:/foo
Pattern:/path/*

Extension mapping
http://localhost:8080/servlet4/foo.ext

Mapping match:EXTENSION
Match value:/foo
Pattern:*.ext

Context root mapping
http://localhost:8080/servlet4

Mapping match:CONTEXT_ROOT
Match value:
Pattern:

Default (fallback) mapping
http://localhost:8080/servlet4/doesnotexist

Mapping match:DEFAULT
Match value:/
Pattern:/

Exact mapping
http://localhost:8080/servlet4/exact

Mapping match:EXACT
Match value:/exact
Pattern:/exact
To test the implicit mapping (as per Servlet spec 12.2.1), the following JSP file was used:
<%
Mapping mapping = request.getMapping();

response.getWriter()
        .append("Mapping match:")
        .append(mapping.getMatchType().name())
        .append("\n")
        .append("Match value:")
        .append(mapping.getMatchValue())
        .append("\n")
        .append("Pattern:")
        .append(mapping.getPattern());
%>
This was however seen by Tomcat as a regular extension mapping:

Implicit mapping
http://localhost:8080/servlet4/page.jsp

Mapping match:EXTENSION 
Match value:/page 
Pattern:*.jsp
Implicit mappings are a little bit difficult to represent. Are they a mapping themselves, or is it just extra information? I.e. can you speak of an "implicit extension mapping" and an "implicit path mapping"?

Conclusion

At the moment it's unfortunately difficult to commit code to the Servlet RI (GlassFish), but Tomcat 9.0 M4 can be used to get an early glimpse of one of the new Servlet APIs. As the examples have shown, the new Mapping API now makes it trivial to find out via which mapping a Servlet was selected. The "implicit" mapping type however may still need some discussion.

Arjan Tijms

Monday, January 18, 2016

Java EE 7 server Liberty 9 beta 2016.1 tested for JASPIC support

IBM recently released the latest monthly beta of their modern and light weight Java EE 7 server; Liberty 9 beta 2016.1. Previous beta releases of Liberty 9 already performed quite well when it came to Java EE's portable authentication (JASPIC), but weren't perfect yet.

In this article we take a look to see if JASPIC support has improved in the latest release. To find out we executed the JASPIC tests against this latest release. For comparison the previous Liberty beta as well as the latest (snapshots) of Payara and WildFly are shown.

One thing to note is that previous downloads of recent Liberty betas were always for a full Java EE 7 server. For some inexplainable reason this month's beta is "only" a Java EE 7 web profile. Possibly this is a bug on the download page, as the size that as stated (116 mb) is not the same as the actual archive that's downloaded (94 mb).

One of Liberty's unique features is that it has a very elaborate and smooth system to install new components and their dependencies. In a way it's a bit like Maven dependency management but for the AS. With the help of this system the mysteriously missing Java EE 7 components could be installed after unpacking Liberty with the following command:

bin/installUtility install javaee-7.0
Additionally the so-called local connector was needed to run the tests. Previous betas included this as well, but it now had to be installed separately too:
bin/installUtility install localConnector-1.0

After this we could run the tests. The results are shown in the table below:

Running the Java EE 7 samples JASPIC tests
Module Test Payara 4.1.1.161-pre WildFly 10rc5 Liberty 9 beta 2016.1 Liberty 9 beta 2015.11
async-authentication testBasicAsync
Passed
Passed
Passed
Passed
basic-authentication testProtectedPageNotLoggedin
Passed
Passed
Passed
Passed
basic-authentication testProtectedPageLoggedin
Passed
Passed
Passed
Passed
basic-authentication testPublicPageLoggedin
Passed
Passed
Passed
Passed
basic-authentication testPublicPageNotLoggedin
Passed
Passed
Passed
Passed
basic-authentication testPublicAccessIsStateless
Passed
Passed
Passed
Passed
basic-authentication testProtectedAccessIsStateless
Passed
Passed
Passed
Passed
basic-authentication testProtectedAccessIsStateless2
Passed
Passed
Passed
Passed
basic-authentication testProtectedThenPublicAccessIsStateless
Passed
Passed
Passed
Passed
custom-principal testProtectedPageLoggedin
Passed
Passed
Passed
Passed
custom-principal testPublicPageLoggedin
Passed
Passed
Passed
Passed
custom-principal testPublicAccessIsStateless
Passed
Passed
Passed
Passed
custom-principal testProtectedAccessIsStateless
Passed
Passed
Passed
Passed
custom-principal testProtectedAccessIsStateless2
Passed
Passed
Passed
Passed
custom-principal testProtectedThenPublicAccessIsStateless
Passed
Passed
Passed
Passed
dispatching testBasicForwardViaProtectedResource
Passed
Passed
Passed
Passed
dispatching testBasicForwardViaPublicResource
Passed
Passed
Passed
Passed
dispatching testBasicIncludeViaPublicResource
Passed
Passed
Passed
Failure
dispatching-jsf-cdi testCDIForwardViaProtectedResource
Passed
Passed
Passed
Passed
dispatching-jsf-cdi testCDIForwardViaPublicResource
Passed
Passed
Passed
Passed
dispatching-jsf-cdi testCDIIncludeViaPublicResource
Passed
Passed
Passed
Failure
dispatching-jsf-cdi testJSFwithCDIForwardViaPublicResource
Passed
Passed
Passed
Passed
dispatching-jsf-cdi testJSFwithCDIForwardViaProtectedResource
Passed
Passed
Passed
Passed
dispatching-jsf-cdi testJSFwithCDIIncludeViaPublicResource
Failure
Failure
Failure
Failure
dispatching-jsf-cdi testJSFForwardViaPublicResource
Passed
Passed
Passed
Passed
dispatching-jsf-cdi testJSFForwardViaProtectedResource
Passed
Passed
Passed
Passed
dispatching-jsf-cdi testJSFIncludeViaPublicResource
Failure
Failure
Failure
Failure
ejb-propagation publicServletCallingProtectedEJB
Passed
Passed
Passed
Passed
ejb-propagation protectedServletCallingProtectedEJB
Passed
Passed
Passed
Passed
ejb-propagation publicServletCallingPublicEJBThenLogout
Passed
Passed
Passed
Passed
ejb-propagation protectedServletCallingPublicEJB
Passed
Passed
Passed
Passed
invoke-ejb-cdi protectedInvokeCDIFromSecureResponse
Passed
Passed
Failure
Failure
invoke-ejb-cdi protectedInvokeCDIFromCleanSubject
Passed
Passed
Passed
Passed
invoke-ejb-cdi protectedInvokeCDIFromValidateRequest
Passed
Passed
Failure
Failure
invoke-ejb-cdi publicInvokeCDIFromSecureResponse
Passed
Passed
Failure
Failure
invoke-ejb-cdi publicInvokeCDIFromValidateRequest
Passed
Passed
Failure
Failure
invoke-ejb-cdi publicInvokeCDIFromCleanSubject
Passed
Passed
Passed
Failure
invoke-ejb-cdi protectedInvokeEJBFromSecureResponse
Passed
Passed
Passed
Passed
invoke-ejb-cdi protectedInvokeEJBFromCleanSubject
Passed
Passed
Passed
Passed
invoke-ejb-cdi protectedInvokeEJBFromValidateRequest
Passed
Passed
Passed
Passed
invoke-ejb-cdi publicInvokeEJBFromSecureResponse
Passed
Passed
Passed
Passed
invoke-ejb-cdi publicInvokeEJBFromValidateRequest
Passed
Passed
Passed
Passed
invoke-ejb-cdi publicInvokeEJBFromCleanSubject
Passed
Passed
Passed
Passed
jacc-propagation callingJACCWhenAuthenticated
Passed
Failure
Failure
Failure
jacc-propagation callingJACCWhenAuthenticated
Passed
Failure
Failure
Failure
jacc-propagation callingJACCWhenNotAuthenticated
Passed
Passed
Failure
Failure
lifecycle testBasicSAMMethodsCalled
Passed
Passed
Passed
Passed
lifecycle testLogout
Passed
Passed
Passed
Passed
register-session testJoinSessionIsOptional
Passed
Passed
Passed
Passed
register-session testRemembersSession
Passed
Passed
Passed
Passed
status-codes test404inResponse
Passed
Passed
Passed
Passed
status-codes test404inResponse
Passed
Passed
Passed
Passed
wrapping testResponseWrapping
Passed
Passed
Passed
Passed
wrapping testRequestWrapping
Passed
Passed
Passed
Passed

As can be seen Liberty's JASPIC support has again improved. Including a resource (e.g. Servlet or JSP) into the response now generally works again. Only JSF based includes are still broken, but this is likely not a Liberty problem but a JSF one.

Additionally one CDI problem was fixed; obtaining and invoking a CDI bean from a SAM's cleanSubject method. This already worked on previous betas when the request was to a protected resource, but mysteriously failed for public resources. The cleanSubject method is generally somewhat easier to support, as this method is called in response to HttpServletRequest#logout and thus happens during the so-called resource invocation (i.e. from the context of a Servlet where CDI already is mandated to be available).

The real challenge for JASPIC implementors is to make sure that CDI works before and after this resource invocation. Payara, GlassFish and JBoss/WildFly have succeeded in supporting this, but Liberty not yet. This support is particularly important since the upcoming Java EE Security API (JSR 375) completely depends on the ability to obtain and invoke CDI beans from the validateRequest and secureResponse methods. Unfortunately early versions of the JSR 375 API can now not be tested on Liberty.

Conclusion

Liberty is improving rapidly and already very useful to deploy portable Java EE 7 authentication modules on. Hopefully it will soon take one the last hurdles and provide full support for CDI as well.

Arjan Tijms