Sunday, September 29, 2013

Testing JAAS based authentication using Arquillian + Apache DeltaSpike Servlet Module

Hey all.  Long time no post.

In today's discussion, I'm talking about testing enterprise applications again.  I recently was faced with a dilemma.  I needed to test my application in an authenticated way, where the user was logged in vs logged out to ensure that things were going right with an authenticated principal.

Of course, this assumes a few things.  First, that we've executed something to ensure our database is populated with a user that could log in.  In my case, I did this by calling internal APIs that do registration and registration completion.  You could do other things, such as using the Arquillian Persistence Extension to insert records.  My application is using the @InSequence annotation with Arquillian and JUnit to give methods an ordering.

Now that we have a little bit of setup, we're going to leverage a few newer technologies.  In Servlet 3, methods were added to the HTTPServletRequest interface that can login and logout.  These give us an easy interface to be able to authenticate an end user in to the configured JAAS realm.  The problem though is how can we access these objects from an Arquillian test?

Of course, this approach is assuming you're using the Servlet 3 protocol (or better) to execute your tests.  This ensures that you have a an active HTTP request on every test.  This request is what we'll use to login the user to the application, and logout afterwards.  You can follow the instructions here to setup the Servlet module in your application.  You'll likely also want to use the Maven resolver from ShrinkWrap to bring in the Servlet Module to your deployment WAR file.  That resolver config is as easy as:

File[] impl = Maven.resolver().resolve("org.apache.deltaspike.modules:deltaspike-servlet-module-impl").withTransitivity().asFile();

which can then be added to your WAR as library JARs.

Now, in your test case you just need to inject the request.  Then in your Before/After methods login and logout.

@Inject
@Web
private HttpServletRequest request;

...

@Before
public void loginBeforeTest() throws ServletException { // throws simply in case there is a configuration issue, bad account info, already logged in etc.
    request.login(username,password);
}


...

@After
public void logoutWhenDone() throws ServletException {
    request.logout();
}

Now, when your test executes, any injection point for Principal will be filled in with the account that was logged in.  This will correctly propogate to any security context as well, EJBContext, MessageDrivenContext, etc.  This of course assumes that your username and password are instance variables within the class, or static references that can be found (I have found that the static reference approach works best for handling client and server tests in arquillian).