Tuesday, September 7, 2010

Integration Tests with Shiro and Nimble

Today we have a guest posting from Uris on the freenode.net #grails channel. I know he spent quite a bit of time trying to solve this issue so rather than keep it to himself he wanted to share it with everyone else.

I recently hit a road block when trying to do integration tests for some controllers that required an authenticated user. We were using Nimble plugin, which basically uses shiro for authentication. Nimble injects an instance of the authenticated user into each controller called 'authenticatedUser'. The particular controller we were testing required that an 'authenticatedUser' be passed on to a service to do a certain action. I kept getting the following error when running the test:


No SecurityManager accessible to the calling code, either bound to the
org.apache.shiro.util.ThreadContext or as a vm static singleton.
This is an invalid application configuration.
java.lang.IllegalStateException: No SecurityManager accessible to the
calling code, either bound to the org.apache.shiro.util.ThreadContext
or as a vm static singleton.
This is an invalid application configuration.
at org.apache.shiro.SecurityUtils.getSecurityManager(SecurityUtils.java:115)
at org.apache.shiro.SecurityUtils.getSubject(SecurityUtils.java:57)
at org.apache.shiro.SecurityUtils$getSubject.call(Unknown Source)
at NimbleGrailsPlugin$_injectAuthn_closure8.doCall(NimbleGrailsPlugin.groovy:137)
at NimbleGrailsPlugin$_injectAuthn_closure8.doCall(NimbleGrailsPlugin.groovy)

I needed to find a way to mock an authenticated user into the test for the controller to use. After a lot of googling, volumes of java, and lots of Iron Maiden, we finally discovered how to do it. First we needed to mock an instance of org.apache.shiro.subject.Subject:

def subject = [getPrincipal: {863},
isAuthenticated: {true}
]as Subject

863 is the id of the default admin user created by Nimble during bootstrap. It might differ for another application, but in this particular case it is 863. Next we need to inject this Subject into org.apache.shiro.util.ThreadContext:

ThreadContext.put(ThreadContext.SECURITY_MANAGER_KEY,
[getSubject: {subject} as SecurityManager])

The final step is to make sure that Shiro's SecurityUtils returns an instance of Subject:

SecurityUtils.metaClass.static.getSubject = {subject}

The final code looks something like this:

import org.apache.shiro.util.ThreadContext
import org.apache.shiro.subject.Subject
import org.apache.shiro.SecurityUtils

def subject = [getPrincipal: {863},
isAuthenticated: {true}
]as Subject
ThreadContext.put(ThreadContext.SECURITY_MANAGER_KEY,
[getSubject: {subject} as SecurityManager])
SecurityUtils.metaClass.static.getSubject = {subject}

2 comments:

  1. Tried and got:

    java.util.LinkedHashMap cannot be cast to org.apache.shiro.mgt.SecurityManager

    with Grails 2.0.4

    ReplyDelete