Friday, August 21, 2015

Activating JASPIC in JBoss WildFly

JBoss WildFly has a rather good implementation of JASPIC, the Java EE standard API to build authentication modules.

Unfortunately there's one big hurdle for using JASPIC on JBoss WildFly; it has to be activated. This activation is somewhat of a hack itself, and is done by putting the following XML in a file called standalone.xml that resides with the installed server:

<security-domain name="jaspitest" cache-type="default">
    <authentication-jaspi>
        <login-module-stack name="dummy">
            <login-module code="Dummy" flag="optional"/>
        </login-module-stack>
        <auth-module code="Dummy"/>
    </authentication-jaspi>
</security-domain>

UPDATE Dec 23, 2015: Since WildFly 10rc5 the "jaspitest" domain is included by default in standalone.xml and no longer has to be added manually. For JBoss EAP 7 beta1 and earlier this is still needed as it just predates the moment this domain was added.

Subsequently in the application a file called WEB-INF/jboss-web.xml needs to be created that references this (dummy) domain:

<?xml version="1.0"?>
<jboss-web>
    <security-domain>jaspitest</security-domain>
</jboss-web>

UPDATE Dec 23, 2015: For WildFly 10rc5 and later the below is not longer relevant. Only the jboss-web.xml file as shown above has to be added to activate JASPIC

While this works it requires the installed server to be modified. For a universal Java EE application that has to run on multiple servers this is a troublesome requirement. While not difficult, it's something that's frequently forgotten and can take weeks if not months to resolve. And when it finally is resolved the entire process of getting someone to add the above XML fragment may have to be repeated all over again when a new version of JBoss is installed.

Clearly having to activate JASPIC using a server configuration file is less than ideal. The best solution would be to not require any kind of activation at all (like is the case for e.g. GlassFish, Geronimo and WebLogic). But this is currently not implemented for JBoss WildFly.

The next best thing is doing this activation from within the application. As it appears this is indeed possible using some reflective magic and the usage of JBoss (Undertow) internal APIs. Here's where the OmniSecurity JASPIC Undertow project comes in. With this project JASPIC can be activated by putting the following in the pom.xml of a Maven project:

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces-security-jaspic-undertow</artifactId>
    <version>1.0</version>
</dependency>

The above causes JBoss WildFly/Undertow to load an extension that uses a number of internal APIs. It's not entirely clear why, but some of those are directly available, while other ones have to be declared as available. Luckily this can be done from within the application as well by creating a META-INF/jboss-deployment-structure.xml file with the following content:

<?xml version='1.0' encoding='UTF-8'?>
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
    <deployment>
        <dependencies>
            <module name="org.wildfly.extension.undertow" services="export" export="true" />
        </dependencies>
    </deployment>
</jboss-deployment-structure>

So how does the extension exactly work?

The most important code consists out of two parts. A reflective part to retrieve what JBoss calls the "security domain" (the default is "other") and another part that uses the Undertow internal APIs to activate JASPIC. This is basically the same code Undertow would execute if the dummy domain is put in standalone.xml.

For completeness, the reflective part to retrieve the domain is:

String securityDomain = "other";

IdentityManager identityManager = deploymentInfo.getIdentityManager();
if (identityManager instanceof JAASIdentityManagerImpl) {
 try {
  Field securityDomainContextField = JAASIdentityManagerImpl.class.getDeclaredField("securityDomainContext");
  securityDomainContextField.setAccessible(true);
  SecurityDomainContext securityDomainContext = (SecurityDomainContext) securityDomainContextField.get(identityManager);

  securityDomain = securityDomainContext.getAuthenticationManager().getSecurityDomain();

 } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
  logger.log(Level.SEVERE, "Can't obtain name of security domain, using 'other' now", e);
 }
}

The part that uses Undertow APIs to activate JASPIC is:

ApplicationPolicy applicationPolicy = new ApplicationPolicy(securityDomain);
JASPIAuthenticationInfo authenticationInfo = new JASPIAuthenticationInfo(securityDomain);
applicationPolicy.setAuthenticationInfo(authenticationInfo);
SecurityConfiguration.addApplicationPolicy(applicationPolicy);

deploymentInfo.setJaspiAuthenticationMechanism(new JASPIAuthenticationMechanism(securityDomain, null));
deploymentInfo.setSecurityContextFactory(new JASPICSecurityContextFactory(securityDomain));
The full source can be found on GitHub.

Conclusion

For JBoss WildFly it's needed to activate JASPIC. There are two hacks available to do this. One requires a modification to standalone.xml and a jboss-web.xml, while the other requires a jar on the classpath of the application and a jboss-deployment-structure.xml file.

It would be best if such activation was not required at all. Hopefully this will indeed be the case in a future JBoss.

Arjan Tijms