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>

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>

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

Friday, July 17, 2015

JSF 2.3 new feature: registrable DataModels

Iterating components in JSF such as h:dataTable and ui:repeat have the DataModel class as their native input type. Other datatypes such as List are supported, but these are handled by build-in wrappers; e.g. an application provided List is wrapped into a ListDataModel.

While JSF has steadily expanded the number of build-in wrappers and JSF 2.3 has provided new ones for Map and Iterable, a long standing request is for users (or libraries) to be able to register their own wrappers.

JSF 2.3 will now (finally) let users do this. The way this is done is by creating a wrapper DataModel for a specific type, just as one may have done years ago when returning data from a backing bean, and then annotating it with the new @FacesDataModel annotation. A “forClass” attribute has to be specified on this annotation that designates the type this wrapper is able to handle.

The following gives an abbreviated example of this:

@FacesDataModel(forClass = MyCollection.class)
public class MyCollectionModel<E> extends DataModel<E> {
    
    @Override
    public E getRowData() {
        // access MyCollection here
    }

    @Override
    public void setWrappedData(Object myCollection) {
        // likely just store myCollection
    }

    // Other methods omitted for brevity
}

Note that there are two types involved here. The “forClass” attribute is the collection or container type that the DataModel wraps, while the generic parameter E concerns the data this collection contains. E.g. Suppose we have a MyCollection<User>, then “forClass” would correspond to MyCollection, and E would correspond to User. If set/getWrappedData was generic the “forClass” attribute may not have been needed, as generic parameters can be read from class definitions, but alas.

With a class definition as given above present, a backing bean can now return a MyCollection as in the following example:

@Named
public class MyBacking {
    public MyCollection<User> getUsers() {
        // return myCollection
    }
}
h:dataTable will be able to work with this directly, as shown in the example below:
<h:dataTable value="#{myBacking.users}" var="user">
    <h:column>#{user.name}</h:column>
</h:dataTable>

There are a few things noteworthy here.

Traditionally JSF artefacts like e.g. ViewHandlers are registered using a JSF specific mechanism, kept internally in a JSF data structure and are looked up using a JSF factory. @FacesDataModel however has none of this and instead fully delegates to CDI for all these concerns. The registration is done automatically by CDI by the simple fact that @FacesDataModel is a CDI qualifier, and lookup happens via the CDI BeanManager (although with a small catch, as explained below).

This is a new direction that JSF is going in. It has already effectively deprecated its own managed bean facility in favour of CDI named beans, but is now also favouring CDI for registration and lookup of the pluggable artefacts it supports. New artefacts will henceforth very likely exclusively use CDI for this, while some existing ones are retrofitted (like e.g. Converters and Validators). Because of the large number of artefacts involved and the subtle changes in behaviour that can occur, not all existing JSF artefacts will however change overnight to registration/lookup via CDI.

Another thing to note concerns the small catch with the CDI lookup that was mentioned above. The thing is that with a direct lookup using the BeanManager we’d get a very specific wrapper type. E.g. suppose there was no build-in wrapper for List and one was provided via @FacesDataModel. Now also suppose the actual data type encountered at runtime is an ArrayList. Clearly, a direct lookup for ArrayList will do us no good as there’s no wrapper available for exactly this type.

This problem is handled via a CDI extension that observes all definitions of @FacesDataModel that are found by CDI during startup and stores the types they handle in a collection. This is afterwards sorted such that for any 2 classes X and Y from this collection, if an object of X is an instanceof an object of Y, X appears in the collection before Y. The collection's sorting is otherwise arbitrary.

With this collection available, the logic behind @FacesDataModel scans this collection of types from beginning to end to find the first match which is assignable from the type that we encountered at runtime. Although it’s an implementation detail, the following shows an example of how the RI implements this:

getDataModelClassesMap(cdi).entrySet().stream()
    .filter(e -> e.getKey().isAssignableFrom(forClass))
    .findFirst()                
    .ifPresent(
        e -> dataModel.add(
            cdi.select(
                e.getValue(),
                new FacesDataModelAnnotationLiteral(e.getKey())
             ).get())
        );

In effect this means we either lookup the wrapper for our exact runtime type, or the closest super type. I.e. following the example above, the wrapper for List is found and used when the runtime type is ArrayList.

Before JSF 2.3 is finalised there are a couple of things that may still change. For instance, Map and Iterable have been added earlier as build-in wrappers, but could be refactored to be based on @FacesDataModel as well. The advantage is be that the runtime would be a client of the new API as well, which on its turn means its easier for the user to comprehend and override.

A more difficult and controversial change is to allow @FacesDataModel wrappers to override build-in wrappers. Currently it’s not possible to provide one own’s List wrapper, since List is build in and takes precedence. If @FacesDataModel would take precedence, then a user or library would be able to override this. This by itself is not that bad, since JSF lives and breathes by its ability to let users or libraries override or extend core functionality. However, the fear is that via this particular way of overriding a user may update one if its libraries that happens to ship with an @FacesDataModel implementation for List, which would then take that user by surprise.

Things get even more complicated when both the new Iterable and Map would be implemented as @FacesDataModel AND @FacesDataModel would take precedence over the build-in types. In that case the Iterable wrapper would always match before the build-in List wrapper, making the latter unreachable. Now logically this would not matter as Iterable handles lists just as well, but in practice this may be a problem for applications that in some subtle way depend on the specific behaviour of a given List wrapper (in all honestly, such applications will likely fail too when switching JSF implementations).

Finally, doing totally away with the build-in wrappers and depending solely on @FacesDataModel is arguably the best option, but problematic too for reasons of backwards compatibility. This thus poses an interesting challenge between two opposite concerns: “Nothing can ever change, ever” and “Modernise to stay relevant and competitive”.

Conclusion

With @FacesDataModel custom DataModel wrappers can be registered, but those wrappers can not (yet) override any of the build-in types.

Arjan Tijms

Wednesday, June 3, 2015

OmniFaces 2.1 released!

We're proud to announce that today we've released OmniFaces 2.1. OmniFaces is a utility library for JSF that provides a lot of utilities to make working with JSF much easier.

OmniFaces 2.1 is the second release that will depend on JSF 2.2 and CDI 1.1 from Java EE 7. Since Java EE 7 availability remains somewhat scarce, we maintain a no-frills 1.x branch for JSF 2.0 (without CDI) as well.

The easiest way to use OmniFaces 2.1 is via Maven by adding the following to pom.xml:

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>2.1</version>
</dependency>

Alternatively the jars files can be downloaded directly.

A complete overview of all that's new can be found on the what's new page, and some more details can be found in BalusC's blogpost about this release.

As usual the release contains an assortment of new features, some changes and a bunch of fixes. One particular fix that took some time to get right is getting a CDI availability check to work correctly with Tomcat + OpenWebBeans (OWB). After a long discussion we finally got this to work, with special thanks to Mark Struberg and Ludovic PĂ©net.

One point worth noting is that since we joined the JSF EG, our time has to be shared between that and working on OmniFaces. In addition some code that's now in OmniFaces might move to JSF core (such as already happened for the IterableDataModel in order to support the Iterable interface in UIData and UIRepeat). For the OmniFaces 2.x line this will have no effect though, but for OmniFaces 3.x (which will focus on JSF 2.3) it may.

We will start planning soon for OmniFaces 2.2. Feature requests are always welcome ;)

Arjan Tijms

Tuesday, May 12, 2015

NEC's WebOTX - a commercial GlassFish derivative

In a previous article we took a look at an obscure Java EE application server that's only known in Korea and virtually unknown everywhere else. Korea is not the only country that has a national application server though. Japan is the other country. In fact, it has not one, but three obscure application servers.

These Japanese servers, the so-called obscure 3, are so unknown outside of Japan that major news events like a Java EE 7 certification simply just does not make it out here.

Those servers are the following:

  1. NEC WebOTX
  2. Hitachi Application Server
  3. Fujitsu Interstage AS

In this article we're going to take a quick look at the first one of this list: NEC WebOTX.

While NEC does have an international English page where a trial can be downloaded it only contains a very old version of WebOTX; 8.4, which implements Java EE 5. This file is called otx84_win32bitE.exe and is about 92MB in size.

As with pretty much all of the Asian application servers, the native language pages contain much more and much newer versions. In this case the Japanese page contains a recent version of WebOTX; 9.2, which implements Java EE 6. This file is called OTXEXP92.exe and is about 111MB in size. A bit of research revealed that a OTXEXP91.exe also once existed, but no other versions were found.

The file is a Windows installer, that presents several dialogs in Japanese. If you can't read Japanese it's a bit difficult to follow. Luckily, there are English instructions for the older WebOTX 8.4 available that still apply to the WebOTX 9.2 installer process as well. Installation takes a while and several scripts seem to start running, and it even wants to reboot the computer (a far cry from download & unzip, start server), but after a while WebOTX was installed in e:\webotx.

Jar and file comparison

One of the first things I often do after installing a new server is browse a little through the folders of the installation. This gives me some general idea about how the server is structured, and quite often will reveal what implementation components a particular server is using.

Surprisingly, the folder structure somewhat resembled that of GlassFish, but with some extra directories. E.g.

GlassFish 3.1.2.2 main dirWebOTX 9.2 main dir

 

Looking at the modules directory in fact did make it clear that WebOTX is in fact strongly based on GlassFish:

GlassFish 3.1.2.2 modules dirWebOTX 9.2 modules dir

 

The jar files are largely identical in the part shown, although WebOTX does have the extra jar here and there. It's a somewhat different story when it comes to the glassfish-* and gf-* jars. None of these are present in WebOTX, although for many similar ones are present but just prefixed by webotx- as shown below:

glassfish- prefixed jarswebotx- prefixed jars

 

When actually looking inside one of the jars with a matching name except for the prefix e.g. glassfish.jar vs webotx.jar, then it becomes clear that at least the file names are largely the same again, except for the package being renamed. See below:

glassfish.jarwebotx.jar

 

Curiously a few jars with similar names have internally renamed package names. This is for instance the case for the well known Jersey (JAX-RS) jar, but for some reason not for Mojarra (JSF). See below:

glassfish jersey-core.jarwebotx jersey-core.jar

 

Besides the differences shown above, name changes occur at a number of other places. For instance well known GlassFish environment variables have been renamed to corresponding WebOTX ones, and pom.xml as well as MANIFEST.FM files in jar files have some renamed elements as well. For instance, the embedded pom.xml for the mojarra jar contains this:

<project>
    <modelVersion>4.0.0</modelVersion>
    <!-- upds start 20121122 org.glassfish to com.nec.webotx.as -->
    <groupId>com.nec.webotx.as</groupId>
    <!-- upds end   20121122 org.glassfish to com.nec.webotx.as -->
    <artifactId>javax.faces</artifactId>
    <version>9.2.1</version>
    <packaging>jar</packaging>
    <name>
        Oracle's implementation of the JSF 2.1 specification.
    </name>
    <description>
        This is the master POM file for Oracle's Implementation of the JSF 2.1 Specification.
    </description>
With the MANIFEST.FM containing this:
Implementation-Title: Mojarra
Implementation-Version: 9.2.1
Tool: Bnd-0.0.249
DSTAMP: 20131217
TODAY: December 17 2013
Bundle-Name: Mojarra JSF Implementation 9.2.1 (20131217-1350) https://
 swf0200036.swf.nec.co.jp/app/svn/WebOTX-SWFactory/dev/mojarra/branche
 s/mojarra2.1.26@96979
TSTAMP: 1350
DocName: Mojarra Implementation Javadoc
Implementation-Vendor: Oracle America, Inc.

 

Trying out the server

Rather peculiar to say the least for a workstation is that WebOTX is automatically started when the computer is rebooted. Unlike most other Java EE servers the default HTTP port after installation is 80. There's no default application installed and requesting http://localhost results in the following screen:

The admin interface is present on port 5858. For some reason the initial login screen asks for very specific browser versions though:

After logging in with username "admin", password "adminadmin", we're presented with a colorful admin console:

As is not rarely the case with admin consoles for Java EE servers there's a lot of ancient J2EE stuff there. Options for generating stubs for EJB CMP beans are happily being shown to the user. In a way this is not so strange. Modern Java EE doesn't mandate a whole lot of things to be configured via a console, thanks to the ongoing standardization and simplification efforts, so what's left is not rarely old J2EE stuff.

I tried to upload a .war file of the OmniFaces showcase, but unfortunately this part of the admin console was still really stuck in ancient J2EE times as it politely told me it only accepted .ear files:

After zipping the .war file into a second zip file and then renaming it to .ear (a rather senseless exercise), the result was accepted and after requesting http://localhost again the OmniFaces showcase home screen was displayed:

As we can see, it's powered by Mojarra 9.2.1. Now we all know that Mojarra moves at an amazing pace, but last time I looked it was still at 2.3 m2. Either NEC travelled some time into the future and got its Mojarra version there, or the renaming in MANIFEST.FM as shown above was a little bit too eagerly done ;)

At any length, all of the functionality in the showcase seemed to work, but as it was tested on GlassFish 3 before this wasn't really surprising.

Conclusion

We took a short look at NEC's WebOTX and discovered it's a GlassFish derivative. This is perhaps a rather interesting thing. Since Oracle stopped commercial support for GlassFish a while ago, many wondered if the code base wouldn't wither at least a little when potentially fewer people would use it in production. However, if a large and well known company such as NEC offers a commercial offering based on GlassFish then this means that next to Payara there remains more interest in the GlassFish code beyond being "merely" an example for other vendors.

While we mainly looked at the similarities with respect to the jar files in the installed product we didn't look at what value NEC exactly added to GlassFish. From a very quick glance it seems that at least some of it is related to management and monitoring, but to be really sure a more in depth study would be needed.

It remains remarkable though that while the company NEC is well known outside Japan for many products, it has its own certified Java EE server that's virtually unheard of outside of Japan.

Arjan Tijms

Monday, May 4, 2015

OmniFaces 2.1-RC1 has been released!

We are proud to announce that OmniFaces 2.1 release candidate 1 has been made available for testing.

OmniFaces 2.1 is the second release that will depend on JSF 2.2 and CDI 1.1 from Java EE 7. Since Java EE 7 availability remains somewhat scarce, we maintain a no-frills 1.x branch for JSF 2.0 (without CDI). For this branch we've simultaneously released a release candidate as well: 1.11-RC1.

A full list of what's new and changed is available here.

OmniFaces 2.1 RC1 can be tested by adding the following dependency to your pom.xml:

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>2.1-RC1</version>
</dependency>

Alternatively the jars files can be downloaded directly.

For the 1.x branch the coordinates are:

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>1.11-RC1</version>
</dependency>
This one too can be downloaded directly.

If no major bugs surface we hope to release OmniFaces 2.1 final soon.

Arjan Tijms

Wednesday, April 22, 2015

Testing JASPIC 1.1 on IBM Liberty EE 7 beta

In this article we take a look at the latest April 2015 beta version of IBM's Liberty server, and specifically look at how well it implements the Java EE authentication standard JASPIC.

The initial version of Liberty implemented only a seemingly random assortment of Java EE APIs, but the second version that we looked at last year officially implemented the (Java EE 6) web profile. This year however the third incarnation is well on target to implement the full profile of Java EE 7.

This means IBM's newer and much lighter Liberty (abbreviated WLP), will be a true alternative for the older and incredibly obese WebSphere (abbreviated WAS) where it purely concerns the Java EE standard APIs. From having by far the most heavyweight server on the market (weighing in at well over 2GB), IBM can now offer a server that's as light and small as various offerings from its competition.

For this article we'll be specifically looking at how well JASPIC works on Liberty. Please take into account that the EE 7 version of Liberty is still a beta, so this only concerns an early look. Bugs and missing functionality are basically expected.

We started by downloading Liberty from the beta download page. The download page initially looked a little confusing, but it's constantly improving and by the time that this article was written it was already a lot clearer. Just like the GlassFish download page, IBM now offers a very straightforward Java EE Web profile download and a Java EE full profile one.

For old time WebSphere users who were used to installers that were themselves 200GB in size and only run on specific operating systems, and then happily downloaded 2GB of data that represented the actual server, it beggars belief that Liberty is now just an archive that you unzip. While the last release of Liberty already greatly improved matters by having an executable jar as download, effectively a self-extracting archive, nothing beats the ultimate simplicity of an "install" that solely consists of an archive that you unzip. This represents the pure zen of installing, shaving every non-essential component off it and leaving just the bare essentials. GlassFish has an unzip install, JBoss has it, TomEE and Tomcat has it, even the JDK has it these days, and now finally IBM has one too :)

We downloaded the Java EE 7 archive, wlp-beta-javaee7-2015.4.0.0.zip, weighing in at a very reasonable 100MB, which is about the same size as the latest beta of JBoss (WildFly 9.0 beta2). Like last year there is no required registration or anything. A license has to be accepted (just like e.g. the JDK), but that's it. The experience up to this point is as perfect as can be.

A small disappointment is that the download page lists a weird extra step that supposedly needs to be performed. It says something called a "server" needs to be created after the unzip, but luckily it appeared this is not the case. After unzipping Liberty can be started directly on OS X by pointing Eclipse to the directory where Liberty was extracted, or by typing the command "./server start" from the "./bin" directory where Liberty was extracted. Why this unnecessary step is listed is not clear. Hopefully it's just a remainder of some early alpha version. On Linux (we tried Ubuntu 14.10) there's an extra bug. The file permissions of the unzipped archive are wrong, and a "chmod +x ./bin/server" is needed to get Liberty to start using either Eclipse or the commandline.

(UPDATE: IBM responded right away by removing the redundant step mentioned by the download page)

A bigger disappointment is that the Java EE full profile archive is by default configured to only be a JSP/Servlet container. Java EE 7 has to be "activated" by manually editing a vendor specific XML file called "server.xml" and finding out that in its "featureManager" section one needs to type <feature>javaee-7.0</feature>. For some reason or the other this doesn't include JASPIC and JACC. Even though they really are part of Java EE (7), they have to be activated separately. In the case of JASPIC this means adding the following as well: <feature>jaspic-1.1</feature>. Hopefully these two issues are just packaging errors and will be resolved in the next beta or at least in the final version.

On to trying out JASPIC, we unfortunately learned that by default JASPIC doesn't really work as it should. Liberty inherited a spec compliance issue from WebSphere 8.x where the runtime insists that usernames and groups that an auth module wishes to set as the authenticated identity also exist in an IBM specific server internal identity store that IBM calls "user registry". This is however not the intend of JASPIC, and existing JASPIC modules will not take this somewhat strange requirement into account which means they will therefor not work on WebSphere and now Liberty. We'll be looking at a hack to work around this below.

Another issue is that Liberty still mandates so called group to role mapping, even when such mapping is not needed. Unlike some other servers that also mandate this by default there's currently no option to switch this requirement off, but there's an open issue for this in IBM's tracker. Another problem is that the group to role mapping file can only be supplied by the application when using an EAR archive. With lighter weight applications a war archive is often the initial choice, but when security is needed and you don't want or can't pollute the server itself with (meaningless) application specific data, then the current beta of Liberty forces the EAR archive upon you. Here too however there's already an issue filed to remedy this.

One way to work around the spec compliance issue mentioned above is by implementing a custom user registry that effectively does nothing. IBM has some documentation on how to do this, but unfortunately it's not giving exact instructions but merely outlines the process. The structure is also not entirely logical.

For instance, step 1 says "Implement the custom user registry (FileRegistrysample.java)". But in what kind of project? Where should the dependencies come from? Then step 2 says: "Creating an OSGi bundle with Bundle Activation. [...] Import the FileRegistrysample.java file". Why not create the bundle project right away and then create the mentioned file inside that bundle project? Step 4 says "Register the services", but gives no information on how to do this. Which services are we even talking about, and should they be put in an XML file or so and if so which one and what syntax? Step 3.4 asks to install the feature into Liberty using Eclipse (this works very nicely), but then step 4 and 5 are totally redundant, since they explain another more manually method to install the feature.

Even though it's outdated, IBM's general documentation on how to create a Liberty feature is much clearer. With those two articles side by side and cross checking it with the source code of the example used in the first article, I was able to build a working NOOP user registry. I had to Google for the example's source code though as the link in the article resulted in a 404. A good thing to realize is that the .esa file that's contained in the example .jar is also an archive that once unzipped contains the actual source code. Probably a trivial bit of knowledge for OSGi users, but myself being an OSGi n00b completely overlooked this and spent quite some time looking for the .java files.

The source code of the actual user registry is as follows:

package noopregistrybundle;

import static java.util.Collections.emptyList;

import java.rmi.RemoteException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.naming.InvalidNameException;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;

import com.ibm.websphere.security.CertificateMapFailedException;
import com.ibm.websphere.security.CertificateMapNotSupportedException;
import com.ibm.websphere.security.CustomRegistryException;
import com.ibm.websphere.security.EntryNotFoundException;
import com.ibm.websphere.security.NotImplementedException;
import com.ibm.websphere.security.PasswordCheckFailedException;
import com.ibm.websphere.security.Result;
import com.ibm.websphere.security.UserRegistry;
import com.ibm.websphere.security.cred.WSCredential;

public class NoopUserRegistry implements UserRegistry {

    @Override
    public void initialize(Properties props) throws CustomRegistryException, RemoteException {
    }

    @Override
    public String checkPassword(String userSecurityName, String password) throws PasswordCheckFailedException, CustomRegistryException, RemoteException {
        return userSecurityName;
    }

    @Override
    public String mapCertificate(X509Certificate[] certs) throws CertificateMapNotSupportedException, CertificateMapFailedException, CustomRegistryException, RemoteException {
        try {
            for (X509Certificate cert : certs) {
                for (Rdn rdn : new LdapName(cert.getSubjectX500Principal().getName()).getRdns()) {
                    if (rdn.getType().equalsIgnoreCase("CN")) {
                        return rdn.getValue().toString();
                    }
                }
            }
        } catch (InvalidNameException e) {
        }

        throw new CertificateMapFailedException("No valid CN in any certificate");
    }

    @Override
    public String getRealm() throws CustomRegistryException, RemoteException {
        return "customRealm"; // documentation says can be null, but should really be non-null!
    }

    @Override
    public Result getUsers(String pattern, int limit) throws CustomRegistryException, RemoteException {
        return emptyResult();
    }

    @Override
    public String getUserDisplayName(String userSecurityName) throws EntryNotFoundException, CustomRegistryException, RemoteException {
        return userSecurityName;
    }

    @Override
    public String getUniqueUserId(String userSecurityName) throws EntryNotFoundException, CustomRegistryException, RemoteException {
        return userSecurityName;
    }

    @Override
    public String getUserSecurityName(String uniqueUserId) throws EntryNotFoundException, CustomRegistryException, RemoteException {
        return uniqueUserId;
    }

    @Override
    public boolean isValidUser(String userSecurityName) throws CustomRegistryException, RemoteException {
        return true;
    }

    @Override
    public Result getGroups(String pattern, int limit) throws CustomRegistryException, RemoteException {
        return emptyResult();
    }

    @Override
    public String getGroupDisplayName(String groupSecurityName) throws EntryNotFoundException, CustomRegistryException, RemoteException {
        return groupSecurityName;
    }

    @Override
    public String getUniqueGroupId(String groupSecurityName) throws EntryNotFoundException, CustomRegistryException, RemoteException {
        return groupSecurityName;
    }

    @Override
    public List<String> getUniqueGroupIds(String uniqueUserId) throws EntryNotFoundException, CustomRegistryException, RemoteException {
        return new ArrayList<>(); // Apparently needs to be mutable
    }

    @Override
    public String getGroupSecurityName(String uniqueGroupId) throws EntryNotFoundException, CustomRegistryException, RemoteException {
        return uniqueGroupId;
    }

    @Override
    public boolean isValidGroup(String groupSecurityName) throws CustomRegistryException, RemoteException {
        return true;
    }

    @Override
    public List<String> getGroupsForUser(String groupSecurityName) throws EntryNotFoundException, CustomRegistryException, RemoteException {
        return emptyList();
    }

    @Override
    public Result getUsersForGroup(String paramString, int paramInt) throws NotImplementedException, EntryNotFoundException, CustomRegistryException, RemoteException {
        return emptyResult();
    }

    @Override
    public WSCredential createCredential(String userSecurityName) throws NotImplementedException, EntryNotFoundException, CustomRegistryException, RemoteException {
        return null;
    }
    
    private Result emptyResult() {
        Result result = new Result();
        result.setList(emptyList());
        return result;
    }
}

There were two small caveats here. The first is that the documentation for getRealm says it may return null and that "customRealm" will be used as the default then. But when you actually return null authentication will fail with many null pointer exceptions appearing in the log. The second is that getUniqueGroupIds() has to return a mutable collection. If Collections#emptyList is returned it will throw an exception that no element can be inserted. Likely IBM merges the list of groups this method returns with those that are being provided by the JASPIC auth module, and directly uses this collection for that merging.

The Activator class that's mentioned in the article referenced above looks as follows:

package noopregistrybundle;

import static org.osgi.framework.Constants.SERVICE_PID;

import java.util.Dictionary;
import java.util.Hashtable;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;

import com.ibm.websphere.security.UserRegistry;

public class Activator extends NoopUserRegistry implements BundleActivator, ManagedService {

    private static final String CONFIG_PID = "noopUserRegistry";
    
    private ServiceRegistration<ManagedService> managedServiceRegistration;
    private ServiceRegistration<UserRegistry> userRegistryRegistration;

    @SuppressWarnings({ "rawtypes", "unchecked" })
    Hashtable getDefaults() {
        Hashtable defaults = new Hashtable();
        defaults.put(SERVICE_PID, CONFIG_PID);
        return defaults;
    }

    @SuppressWarnings("unchecked")
    public void start(BundleContext context) throws Exception {
        managedServiceRegistration = context.registerService(ManagedService.class, this, getDefaults());
        userRegistryRegistration = context.registerService(UserRegistry.class, this, getDefaults());
    }
    
    @Override
    public void updated(Dictionary<String, ?> properties) throws ConfigurationException {

    }

    public void stop(BundleContext context) throws Exception {
        if (managedServiceRegistration != null) {
            managedServiceRegistration.unregister();
            managedServiceRegistration = null;
        }
        if (userRegistryRegistration != null) {
            userRegistryRegistration.unregister();
            userRegistryRegistration = null;
        }
    }
}

Here we learned what that cryptic "Register the services" instruction from the article meant; it are the two calls to context.registerService here. Surely something that's easy to guess, or isn't it?

Finally a MANIFEST.FM file had to be created. The Eclipse tooling should normally help here, but it our case it worked badly. The "Analyze code and add dependencies to the MANIFEST.MF" command in the manifest editor (under the Dependencies tab) didn't work at all, and "org.osgi.service.cm" couldn't be chosen from the Imported Packages -> Add dialog. Since this import is actually used (and OSGi requires you to list each and every import used by your code) I added this manually. The completed file looks as follows:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: NoopRegistryBundle
Bundle-SymbolicName: NoopRegistryBundle
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: noopregistrybundle.Activator
Import-Package: com.ibm.websphere.security;version="1.1.0",
 javax.naming,
 javax.naming.ldap,
 org.osgi.service.cm,
 org.osgi.framework
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Export-Package: noopregistrybundle

Creating yet another project for the so-called feature, importing this OSGi bundle there and installing the build feature into Liberty was all pretty straightforward when following the above mentioned articles.

The final step consisted of adding the noop user registry to Liberty's server.xml, which looked as follows:

<?xml version="1.0" encoding="UTF-8"?>
<server description="new server">

    <featureManager>
        <feature>javaee-7.0</feature>
        <feature>jaspic-1.1</feature>
        <feature>localConnector-1.0</feature>
        <feature>usr:NoopRegistryFeature</feature>
    </featureManager>

    <httpEndpoint httpPort="9080" httpsPort="9443" id="defaultHttpEndpoint"/>

    <noopUserRegistry/>
</server>

With this in place, JASPIC indeed worked on Liberty, which is absolutely great! To do some more thorough testing of how compatible Liberty exactly is we used the JASPIC tests that I contributed to the Java EE 7 samples project. These tests have been used by various other server vendors already and give a basic impression of what things work and do not work.

The tests had to be adjusted for Liberty because of its requirement to add an EAR wrapper that hosts the mandated group to role mapping.

After running the tests, the following failures were reported:

Test Class Comment
testPublicPageNotRememberLogin org.javaee7.jaspic.basicauthentication.BasicAuthenticationPublicTest
testPublicPageLoggedin org.javaee7.jaspic.basicauthentication.BasicAuthenticationPublicTest
testProtectedAccessIsStateless org.javaee7.jaspic.basicauthentication.BasicAuthenticationStatelessTest
testPublicServletWithLoginCallingEJB org.javaee7.jaspic.ejbpropagation.ProtectedEJBPropagationTest
testProtectedServletWithLoginCallingEJB org.javaee7.jaspic.ejbpropagation.PublicEJBPropagationLogoutTest
testProtectedServletWithLoginCallingEJB org.javaee7.jaspic.ejbpropagation.PublicEJBPropagationTest
testLogout org.javaee7.jaspic.lifecycle.AuthModuleMethodInvocationTest SAM method cleanSubject not called, but should have been
testJoinSessionIsOptional org.javaee7.jaspic.registersession.RegisterSessionTest
testRemembersSession org.javaee7.jaspic.registersession.RegisterSessionTest
testResponseWrapping org.javaee7.jaspic.wrapping.WrappingTest Response wrapped by SAM did not arrive in Servlet
testRequestWrapping org.javaee7.jaspic.wrapping.WrappingTest Request wrapped by SAM did not arrive in Servlet

Specifically the EJB, "logout calls cleanSubject" & register session (both new JASPIC 1.1 features) and request/response wrapper tests failed.

Two of those are new JASPIC 1.1 features and likely IBM just hasn't implemented those yet for the beta. Request/response wrapper failures is a known problem from JASPIC 1.0 times. Although most servers implement it now curiously not a single JASPIC implementation did so back in the Java EE 6 time frame (even though it was a required feature by the spec).

First Java EE 7 production ready server?

At the time of writing, which is 694 days (1 year, ~10 months) after the Java EE 7 spec was finalized, there are 3 certified Java EE servers but none of them is deemed by their vendor as "production ready". With the implementation cycle of Java EE 6 we saw that IBM was the first vendor to release a production ready server after 559 days (1 year, 6 months), with Oracle following suit at 721 days (1 year, 11 months).

Oracle (perhaps unfortunately) doesn't do public beta releases and is a little tight lipped about their up coming Java EE 7 WebLogic 12.2.1 release, but it's not difficult to guess that they are working hard on it (I have it on good authority that they indeed are). Meanwhile IBM has just released a beta that starts to look very complete. Looking at the amount of time it took both vendors last time around it might be a tight race between the two for releasing the first production ready Java EE 7 server. Although JBoss' WildFly 8.x is certified, a production ready and supported release is likely still at least a full year ahead when looking at the current state of the WildFly branch and if history is anything to go by (it took JBoss 923 days (2 years, 6 months) last time).

Conclusion

Despite a few bugs in the packaging of the full and web profile servers, IBM's latest beta shows incredible promise. The continued effort in making its application server yet again simpler to install for developers is nothing but applaudable. IBM clearly meant it when they started the Liberty project a few years ago and told their mission was to optimize the developer experience.

There are a few small bugs and one somewhat larger violation in its JASPIC implementation, but we have to realize it's just a beta. In fact, IBM engineers are already looking at the JASPIC issues.

To summarize the good and not so good points:

Good

  • Runs on all operating systems (no special IBM JDK required)
  • Monthly betas of EE 7 server
  • Liberty to support Java EE 7 full profile
  • Possibly on its way to become the first production ready EE 7 server
  • Public download page without required registration
  • Very good file size for full profile (100MB)
  • Extremely easy "download - unzip - ./server start" experience

Not (yet) so good

  • Download page lists totally unnecessary step asking to "create a server" (update: now fixed by IBM)
  • Wrong file permissions in archive for usage on Linux; executable attribute missing on bin/server (update: now fixed by IBM)
  • Wrong configuration of server.xml; both web and full profile by default configured as JSP/Servlet only
  • "javaee-7.0" feature in server.xml doesn't imply JASPIC and JACC, while both are part of Java EE (update: now fixed by IBM)
  • JASPIC runtime tries to validate usernames/groups in internal identity store (violation of JASPIC spec)
  • Mandatory group to role mapping, even when this is not needed
  • Mandatory usage of EAR archive when group to role mapping has to be provided by the application
  • Not all JASPIC features implemented yet (but remember that we looked at a beta version)

Arjan Tijms

Thursday, April 2, 2015

How Java EE translates web.xml constraints to Permission instances

It's a well known fact that in Java EE security one can specify security constraints in web.xml. It's perhaps a little lesser known fact that in full profile Java EE servers those constraints are translated by the container to instances of the Permission class. The specifications responsible for this are Servlet and JACC. This article shows a simple example of what this translation looks like.

Web.xml constraints

We're putting the following constraints in web.xml:

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Forbidden Pattern</web-resource-name>
        <url-pattern>/forbidden/*</url-pattern>
    </web-resource-collection>
    <auth-constraint/>
</security-constraint>

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Protected Pattern</web-resource-name>
        <url-pattern>/protected/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>architect</role-name>
        <role-name>administrator</role-name>
    </auth-constraint>
</security-constraint>

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Protected Exact</web-resource-name>
        <url-pattern>/adminservlet</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>administrator</role-name>
    </auth-constraint>
</security-constraint>

<security-role>
    <role-name>architect</role-name>
</security-role>
<security-role>
    <role-name>administrator</role-name>
</security-role>

Java Permissions

Given the above shown constraints in web.xml the following WebResourcePermission instances will be generated, in 3 collections as shown below. For brevity only WebResourcePermission is shown. The other types are omitted.

Excluded

  • WebResourcePermission "/forbidden/*"

Unchecked

  • WebResourcePermission "/:/adminservlet:/protected/*:/forbidden/*"

Per Role

  • architect
    • WebResourcePermission "/protected/*"
  • administrator
    • WebResourcePermission "/protected/*"
    • WebResourcePermission "/adminservlet"

Below is very short explanation for the different permission types normally used for the translation. The interested reader is suggested to study the Javadoc of each type for more detailed information.


Java EE will generate 3 types of Permission instances when translating constraints expressed in web.xml; WebRoleRefPermission, WebUserDataPermission and WebResourcePermission.

WebRoleRefPermission

A web role ref permission is about mapping Servlet local roles to application roles. Especially with MVC frameworks like JSF and the upcoming JAX-RS based MVC 1.0 the use for this is perhaps questionable, as there's only one Servlet in that case that serves many different views.

WebUserDataPermission

A web user data permission is about the transport level guarantees for accessing resources (practically this almost always means HTTP vs HTTPS). This can be specified using the <user-data-constraint> element in web.xml, which we have omitted here.

WebResourcePermission

The web resource permission is about the actual access to a resource. This can be specified using the <web-resource-collection> element in web.xml, which we have used in the example above.

So let's take a look at what's going on here.

Our first web.xml constraint shown above defined so-called "excluded access", which means that nobody can access the resources defined by that pattern. In XML this is accomplished by simply omitting the auth-constraint element. This was translated to Java code by means of putting a WebResourcePermission with the pattern "/forbidden/*" in the "Excluded" collection. Although there are some differences, this is a reasonably direct translation from the XML form.

The permission shown above for the "Unchecked" collection concerns the so-called "unchecked access", which means that everyone can access those resources. This one wasn't explicitly defined in XML, although XML does have syntax for explicitly defining unchecked access. The permission shown here concerns the Servlet default mapping (a fallback for everything that doesn't match any other declared Servlet pattern).

The pattern used here may need some further explanation. In the pattern the colon (:) is a separator of a list of patterns. The first pattern is the one we grant access to, while the rest of the patterns are the exceptions to that. So unchecked access for "/:/adminservlet:/protected/*:/forbidden/*" means access to everything (e.g. /foo/readme.text) is granted to everyone, with the exception of "/adminservlet" and paths that starts with either "/protected" or "/forbidden". In this case the translation from the XML form to Java is not as direct.

The next two constraints that we showed in web.xml concerned "role-based access", which means that only callers who are in the associated roles can access resources defined by those patterns. In XML this is accomplished by putting one or more patterns together with one or more roles in a security constraint. This is translated to Java by generating {role, permission} pairs for each unique combination that appears in the XML file. It's typically most convenient then to put these entries in a map, with role the key and permission the value, as was done above, but this is not strictly necessary. Here we see that the translation doesn't directly reflect the XML structure, but the link to the XML version can surely be seen in the translation.

Obtaining the generated Permissions

There is unfortunately no API available in Java EE to directly obtain the generated Permission instances. Instead, one has to install a JACC provider that is called by the container for each individual Permission that is generated. A ready to use provider was given in a previous article, but as we saw before they are not entirely trivial to install.

Conclusion

We've shown a few simple web.xml based security constraints and saw how they translated to Java Permission instances.

There are quite a few things that we did not look at, like the option to specify one or more HTTP Methods (GET, POST, etc) with or without the deny uncovered methods feature, the option to specify a transport level guarantee, the "any authenticated user" role, combinations of overlapping patterns with different constraints, etc etc. This was done intentionally to keep the example simple and to focus on the main concept of translation without going in to too many details. In a future article we may take a look at some more advanced cases.

Arjan Tijms