Archive for April, 2008

An updated Grails Acegi plugin

Monday, April 14th, 2008

Note – this is out of date now since these changes have been merged into the Acegi Plugin. To get the latest features install the standard plugin, i.e. “grails install-plugin acegi”

I’ve implemented Acegi Security (now Spring Security) in a few Spring apps so I greatly appreciate how simple the Grails Acegi plugin makes securing an application. It only takes a few minutes to install and configure and you get to avoid working witht Acegi’s notoriously large XML config (and steep learning curve).

A coworker was asking if it’d be possible to use LDAP instead of a database and this got me thinking about how a lot of the default configuration in the plugin isn’t modifyable. In addition to alternate data stores I could see needing to add in a LogoutHandler or even one or more extra Filters but you’d need to edit the plugin code, which makes upgrading a pain.

Also, I’ve been following the progress of Acegi migrating to Spring Security 2.0 and their new simpler configuration options. I’ve only implemented a small Grails app so far but we’re working on a much more extensive one and I want to avoid having to downgrade to traditional Acegi, so I spent a weekend upgrading and extending the plugin’s configurability and sent it off to the original developers to see if they’d like to incorporate the changes. They’re pretty busy but are reviewing the new code.

Along the way I found and fixed a couple of Hibernate Session-handling bugs
and it turned out to be good timing – just after I fixed them a couple of people complained about them on the user mailing list. If you’re affected by the bugs but don’t want to risk using this update you can replace the 3 fixed files attached to the Jira issue.

It may take a while to get things incorporated into the official plugin, so if any of this is interesting to you, feel free to download the updated plugin (I called it spring-security-0.1 but it’ll most likely be acegi-security-0.3 for consistency), and of course let me know if there are any issues.

The plugin is configured using a default script (DefaultAcegiConfig) and a user-defined script (AcegiConfig). Many options are configurable, and I added a few more:

  • filterNames – a list of filter bean names. If the list is specified in the
    user’s config, the specified filters will be used in the requested order,
    otherwise the standard filters will be used
  • logoutHandlerNames – a list of logout handler bean names. If the list is
    specified in the user’s config, the specified logout handlers will be used in
    the requested order, otherwise the standard handlers will be used
  • decisionVoterNames – a list of voter bean names. If the list is specified in
    the user’s config, the specified voters will be used in the requested order,
    otherwise the standard voters will be used
  • providerNames – a list of authentication provider bean names. If the list is
    specified in the user’s config, the specified authentication providers will
    be used in the requested order, otherwise the standard authentication
    providers will be used

So for example, if you wanted to integrate an SSO solution, you could replace one or more of the default filters and/or add extra filters by overriding the list of filter names, and defining the SSO-specific filters in resources.groovy or resources.xml. The same goes for logout handlers, voters, and providers.

I also made a few string properties customizable:

  • realmName – allows the user to choose the realm name instead of the
    default ‘Grails Realm’ (no idea if this is useful)
  • rememberMeKey – allows the user to choose the
    rememberMeServices/rememberMeAuthenticationProvider key instead of the
    default ‘grailsRocks’
  • afterLogoutUrl – allows the user to choose the logoutFilter
    logoutSuccessUrl instead of the default ‘/’

I tried to keep things backwards compatible for the users, but made a few small
changes. One was to change the ‘show_mail’ parameter
to ‘showMail’ (in the user domain class) and another was to change the ‘loadAcegi’ config attribute to ‘active’ (in AcegiConfig). Also, I renamed DefaultAcegiConfig and AcegiConfig to DefaultSecurityConfig and SecurityConfig respectively.

To make working with LDAP or other authentication stores easier, I reworked GrailsUser and GrailsDaoImpl a bit. GrailsUser is now an interface (extending UserDetails and adding a getter for the user domain object) and GrailsUserImpl is the default concrete implementation. So you can use your own implementation of GrailsUser by subclassing GrailsDaoImpl and overriding createUserDetails() or replace the whole bean by defining your own implementation of UserDetailsService in resources.groovy/resources.xml (it turns out that any beans that you define replace plugins’ beans with the same name/id).

I also moved the artifacts (AuthBase, AuthenticateService, and AuthorizeTagLib) into packages so projects that use packages can access them.

There were some relatively minor issues:

  • org.acegisecurity.annotation.SecurityAnnotationAttributes was apparently
    moved to
    but is no longer available in the code. I couldn’t figure out how to
    implement annotation-based security without this (there’s no 2.0 documenation
    yet) so I re-implemented the Acegi code in the plugin
    and it works fine. This should be replaced with whatever approach is typical
    in 2.0
  • I’m using Java 5 features since the existing annotation support had already broken support for
    Java 1.4. If this is a problem it would be simple to revert
  • I tested as much as I could but haven’t used the email, Ajax login, or
    Switch User features so those might be broken (unlikely though)
  • apparently there’s a bug in the Grails plugin.xml generation code when a
    plugin uses packages, so the elements are broken (I doubt this
    affects anything)

Additionally, Stephan February emailed the Grails user list
around the same time announcing that he’d made a custom version of the plugin with ACL support added, so there’s plenty of merging to be done 🙂

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 License.