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
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
- 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
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 (
AuthorizeTagLib) into packages so projects that use packages can access them.
There were some relatively minor issues:
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
- 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
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 🙂