One of the cool things about working for Internal Systems is the opportunity to work on new systems. My most recent project is the development of my ShipIt VII project into a fully-fledged application.
Where my ShipIt VII project was a standard Java web application, I’m doing the real version using Grails with Acegi. However, I have to use our existing Crowd instance, to leverage its user base and its single sign-on capabilities. While there’s some good documentation out there about the individual components, it’s still a little bit of work to get Grails + Acegi + Crowd going together. This is how I did it.

Step 1: Grails & Acegi Installation

I downloaded and installed Grails v1.0.1 and Grails’ Acegi Security Plugin v0.2, and set my GRAILS_HOME and PATH environment variables in .bash_login so I didn’t drive myself batshit insane by having to set them for each new terminal window.

Step 2: Create Grails Application with Acegi Security

I created my application via the command line,

Kate:grails kellingburg$ grails create-app AcegiApp

installed the Acegi Plugin,

Kate:grails kellingburg$ cd AcegiApp/
Kate:AcegiApp kellingburg$ grails install-plugin PATH_TO_PLUGIN/

created the domain classes that Acegi uses,

Kate:AcegiApp kellingburg$ grails create-auth-domains

and created a controller that only authorised users are able to access.

Kate:AcegiApp kellingburg$ grails create-controller AuthOnly

I then edited /conf/Bootstrap.groovy to add the Requestmap object that will secure the controller (note that the url is all in lowercase),

class BootStrap {
def init = { servletContext ->
new Requestmap(url:"/authonly/**",configAttribute:"ROLE_USER").save()
def destroy = {

put some dummy code in /controllers/AuthOnlyController.groovy,

class AuthOnlyController {
def index = {
render "Hello, World!"

and finally edited /web-app/index.gsp to use some of the Acegi taglibs:

<g:isLoggedIn>Hello <b><g:loggedInUserInfo field="username"/></b>!</g:isLoggedIn>
<g:isNotLoggedIn>You are not logged in</g:isNotLoggedIn>
<g:ifAllGranted role="ROLE_ADMIN,ROLE_TEST">-- ROLE_ADMIN and ROLE_TEST granted<br/></g:ifAllGranted>
<g:ifAnyGranted role="ROLE_ADMIN,ROLE_TEST">-- ROLE_ADMIN or ROLE_TEST granted<br/></g:ifAnyGranted>
<g:ifNotGranted role="ROLE_TEST">-- ROLE_TEST not granted<br/></g:ifNotGranted>
<g:ifAnyGranted role="ROLE_USER">-- ROLE_USER granted<br/></g:ifAnyGranted>

I was now ready to run the application!

Kate:AcegiApp kellingburg$ grails run-app

Browsing to http://localhost:8080/AcegiApp/ showed the front page, and as expected, I was not logged in:
Clicking on AuthOnlyController brought up the login screen:
If this was a Grails + Acegi application, I could now begin to add users and roles to my application (by default, Acegi calls these Person objects and Authority objects). But I’m going to get my users from Crowd instead.

Step 3: Crowd Download

I grabbed a beta version of Crowd v1.3, but seeing as it was released earlier this week, you can download Crowd v1.3 from the Atlassian website.

Step 4: Crowd Installation & Configuration

I followed the Crowd v1.3 installation instructions to install Crowd locally. When my application is deployed, I will integrate with Atlassian’s Crowd instance, but for development I wanted to set up my own. Because I wanted set up to be as painless as possible, I chose to use the database supplied with Crowd. I was able to quickly grab an evaluation license from the My Account section of the Atlassian website.
Installing and configuring Crowd was a cinch. My previous experiences with single-sign on products had left me very hesitant to get my feet wet with Crowd, but boy am I glad I did. It’s hard to imagine how it could have been easier.
I configured one directory, one application, two users and two groups. The group names matched the Authorities that my AcegiApp will expect: ROLE_USER and ROLE_ADMIN.
To check that I’d set everything up correctly, Crowd provides an option under “Config Test” that verifies whether a user can authenticate with the application:

Step 5: Grails + Acegi + Crowd Integration

Crowd comes with centralised authentication and single sign-on connectors for Acegi, and some pretty darn good instructions. However, because this was a Grails app, I had to do things slightly differently.

I copied from CROWD_INSTALL/client/conf to ACEGI_APP. It would be nice if Grails provided a /properties folder to hold properties files, but I needed to put the file in the root directory. I needed to edit this file as per the Crowd documentation: acegiapp
application.password password
application.login.url http://localhost:8080/AcegiApp/login/auth


I copied applicationContext-CrowdClient.xml from the CROWD/client/crowd-integration-client-1.3.jar to ACEGI_APP/conf/spring and renamed it resources.xml.
I added some beans to resources.xml, similar to but not the same as the Crowd documentation:

<bean id="crowdUserDetailsService" class="com.atlassian.crowd.integration.acegi.CrowdUserDetailsService">
<property name="securityServerClient" ref="securityServerClient"/>
<bean id="crowdAuthenticationProvider" class="com.atlassian.crowd.integration.acegi.CrowdAuthenticationProvider">
<property name="userDetailsService" ref="crowdUserDetailsService"/>
<property name="httpAuthenticator" ref="httpAuthenticator"/>
<property name="securityServerClient" ref="securityServerClient"/>
<bean id="authenticationProcessingFilter" class="com.atlassian.crowd.integration.acegi.CrowdAuthenticationProcessingFilter">
<property name="httpAuthenticator" ref="httpAuthenticator"/>
<property name="authenticationManager" ref="authenticationManager"/>
<property name="authenticationFailureUrl" value="/login/authfail?login_error=1"/>
<property name="defaultTargetUrl" value="/"/>
<property name="filterProcessesUrl" value="/j_acegi_security_check"/>
<bean id="crowdLogoutHandler" class="com.atlassian.crowd.integration.acegi.CrowdLogoutHandler">
<property name="httpAuthenticator" ref="httpAuthenticator"/>
<bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter">
<constructor-arg value="/"/>
<ref bean="crowdLogoutHandler"/>
<bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"/>
<property name="filterProcessesUrl" value="/j_acegi_logout"/>

jar files

I copied the following jar files to the /lib directory. This step is where I really, really missed Maven 2 — I spent a lot of time tracking down dependencies, particularly of jars not provided in CROWD/client/lib. (Note: this was a problem in the beta version of Crowd 1.3, the Crowd developers assure me they’ve since fixed this.)


I didn’t copy all the files in CROWD/client/lib as Grails includes many of the dependencies by default.


I then edited ACEGI_APP/plugins/acegi-0.2/AcegiGrailsPlugin.groovy — which means I’ll have to be careful if I ever upgrade the plugin.
At line 91, I removed the declarations of logoutFilter and authenticationProcessingFilter, and at line 179, I replaced daoAuthenticationProvider with crowdAuthenticationProvider:



I changed line 70 in ACEGI_APP/plugins/acegi-0.2/grails-app/taglib/AuthorizeTagLib.groovy so that it didn’t refer to the domainClass field. This is because the authPrincipal is now a com.atlassian.crowd.integration.acegi.CrowdUserDetails object, and it does not have a domainClass field.

def loggedInUserInfo = {attrs,body->
def authPrincipal = SCH?.context?.authentication?.principal
if( authPrincipal!=null && authPrincipal!="anonymousUser"){
//out << authPrincipal?.domainClass?."${attrs.field}"
out << authPrincipal?."${attrs.field}"
out << body()

As with AcegiGrailsPlugin.groovy, I’ll have to be careful if I ever upgrade the plugin.

Step 5: Grails + Acegi + Crowd

I started up my Grails applications, AcegiApp, went to the front page, clicked on AuthOnly and logged in as the first user I created. I’m now able to see the page:
And my front page now shows some information about the logged in user:

In Summary

Getting started with the individual components was pretty straight forward. I did a lot of trial-and-error to figure out how to integrate with Crowd, but the Crowd documentation was a great starting place. Some of that trial-and-error was due to *ahem* developer issues rather than Grails, Acegi or Crowd.
Two areas of uncoolness: transitive dependencies and plugin editing.
I really, really missed having Maven 2 support for Grails (currently the maven-grails-plugin does not support Grails v1.0.1) purely because of transitive dependency resolution.
I had to edit the two of the plugin files, which means I will have to be careful when upgrading the plugin. Plus, I ignore some of the fields in AcegiConfig.groovy. Ideally, integrating Crowd would require changes only to AcegiConfig.groovy and any fields set here would be referenced by the Crowd beans.
The biggest surprise from the whole endeavour – besides spending hours on an issue that arose because I typed a comma instead of a full stop – was how easy Crowd was. Installation and configuration were a breeze, and quick to boot. All Internal Systems applications will now include Crowd support from the get go – not because we have to, but because we want to.
Does this sound like fun to you? We are hiring in Internal Systems!

Three’s a Crowd – securing a Grails ap...