Archive for the Category 'security'

ACLs in the Grails Spring Security Plugin

Sunday, December 27th, 2009

Update: If you’re interested in using ACLs with Spring Security and Grails, you can use the Spring Security ACL plugin


It has taken way too long, but the Grails Spring Security plugin finally has ACL support. It’s not officially available yet, but people have offered to beta test an early version of the plugin with ACLs, so you can download that here and report any issues back. Once it’s stable I’ll do an official release.

History

Stephan February did the first work adding ACL support to the plugin. Unfortunately at the time the plugin was based on Acegi 1.0.x and I had just converted it to use Spring Security 2.0. No one did the work to convert the ACL support to the new package layout and approach, so this wasn’t used.

This is a frequently requested feature, so I created a feature request as a TODO item for myself. I found some time to work on this over the summer and created an initial GORM-based implementation (the standard Spring Security implementation uses JDBC). I was fortunate to be able to use this at a client project at InnoCentive which helped to flesh out the ideas and identify a few issues.

Around the same time, Phillip Merensky mentioned on the mailing list that he was working on an implementation. He wrote about his approach here and attached his version of the plugin to the JIRA issue. Phillip’s work was very helpful; I’ve merged his version with mine for the current implementation.
(more…)

Hierarchical Roles in the Grails Spring Security Plugin

Monday, December 21st, 2009

Update: This is a feature in the Spring Security Core plugin – see section “14 Hierarchical Roles” in the docs.


I was looking at a non-Grails Spring Security application that used hierarchical roles and wondered what it’d take to get this working with the Grails plugin. Turns out it’s pretty simple.

Non-hierarchical roles are checked by a RoleVoter but to use hierarchical roles you need a RoleHierarchyVoter. Replacing the roleVoter bean in resources.groovy is all it takes.

RoleHierarchyVoter needs an implementation of RoleHierarchy and the default implementation in Spring Security is RoleHierarchyImpl which parses a String defining the hierarchy. For example, this configuration defines the hierarchy ROLE_SUPERADMIN > ROLE_ADMIN > ROLE_USER:

import org.springframework.security.userdetails.hierarchicalroles.RoleHierarchyImpl
import org.springframework.security.vote.RoleHierarchyVoter

beans = {

   roleHierarchy(RoleHierarchyImpl) {
      hierarchy = '''
         ROLE_SUPERADMIN  > ROLE_ADMIN
         ROLE_ADMIN  > ROLE_USER
      '''
   }

   roleVoter(RoleHierarchyVoter, ref('roleHierarchy'))
}

You can download a small demo app here that shows how it works. Unpack the app and run grails run-app, and then open http://localhost:8080/hierarchical/secure/. The app creates three users in BootStrap:

Username Password Role
user user ROLE_USER
admin admin ROLE_ADMIN
superadmin superadmin ROLE_SUPERADMIN

so you can login as each user to test the secured actions:

class SecureController {

   def index = {}

   @Secured(['ROLE_USER'])
   def user = {
   ...
   }

   @Secured(['ROLE_ADMIN'])
   def admin = {
   ...
   }

   @Secured(['ROLE_SUPERADMIN'])
   def superadmin = {
   ...
   }
}

Logout in between by navigating to http://localhost:8080/hierarchical/logout. Although only one role is defined for each action, as the super admin you can access all three, as the admin you can access admin and user, and as the user you can only access user.


I’ll make this part of the plugin at some point to make configuration simpler, but for now it’s not much work to do it explicitly.

SpringOne 2GX Sample Apps – Spring Security LDAP Login

Tuesday, December 01st, 2009

This is the second in a series of posts making the demo applications that I used for my SpringOne 2GX presentations available. I’ll describe here how to create a Grails application using the Spring Security plugin that authenticates users from LDAP. This is based on topics from the Demystifying Spring Security in Grails talk (you can download the presentation here) but wasn’t shown there since I ran out of time.

Also refer to the plugin documentation for other tutorials here.


To create an application that authenticates users from LDAP, run

grails create-app springone2gx_ldap
cd springone2gx_ldap

To make classpath management simpler in Eclipse/STS I create a grails-app/conf/BuildConfig.groovy (in Grails 1.1 apps; in 1.2 this is done for you) with the line

grails.project.plugins.dir='plugins'

to keep plugins in the project root like in 1.0.x but this is optional.

Next install the plugin:

grails install-plugin acegi

Run the create-auth-domains script to generate the person, authority, and request map domain classes and also grails-app/conf/SecurityConfig.groovy:

grails create-auth-domains com.burtbeckwith.springone2gx.User com.burtbeckwith.springone2gx.Role com.burtbeckwith.springone2gx.Requestmap

The other two scripts that the plugin provides are optional and create CRUD pages (generate-manager) and basic user registration (generate-registration). It’s a good idea to run generate-manager; run generate-registration if it’s useful to you.

grails generate-manager

As with the previous post we’ll use annotated controllers, so we’ll need to configure that, and we can delete the request map class and CRUD pages. The plugin scripts currently asssume you’ll be using request maps, so we have to run generate-manager and generate-registration before deleting these.

  • delete grails-app/domain/com/burtbeckwith/springone2gx/Requestmap.groovy
  • delete grails-app/controller/RequestmapController.groovy
  • delete the grails-app/views/requestmap directory and its GSPs
  • remove the com.burtbeckwith.springone2gx.Requestmap import from grails-app/controller/RoleController.groovy
  • in grails-app/conf/SecurityConfig.groovy, disable requestmaps (useRequestMapDomainClass = false) and enable annotations (useControllerAnnotations = true), and remove the requestMapClass property:
    security {
    
       active = true
    
       loginUserDomainClass = "com.burtbeckwith.springone2gx.User"
       authorityDomainClass = "com.burtbeckwith.springone2gx.Role"
    
       useRequestMapDomainClass = false
       useControllerAnnotations = true
    }
    

In Eclipse or STS the steps to configure the classpath are:

  • add PLUGIN_DIR/src/groovy as a source folder
  • add PLUGIN_DIR/src/java as a source folder
  • add PLUGIN_DIR/grails-app/services as a source folder
  • add these jars from PLUGIN_DIR/lib
    • facebook-java-api-2.0.4.jar
    • jcifs-1.2.25.jar
    • spring-ldap-1.2.1.jar
    • spring-ldap-tiger-1.2.1.jar
    • spring-security-core-2.0.4.jar
    • spring-security-core-tiger-2.0.4.jar
    • spring-security-ntlm-2.0.4.jar
    • spring-security-openid-2.0.4.jar

Having done all that, let’s create a secured controller to test annotations:

grails create-controller secure

and add the import for the annotation, and annotate at the class level that you must be an admin to access this controller:

import org.codehaus.groovy.grails.plugins.springsecurity.Secured

@Secured(['ROLE_ADMIN'])
class SecureController {

   def index = {
      redirect action: foo
   }

   def foo = {
      render 'OK'
   }

   def bar = {
      render 'also OK'
   }
}

The controller has a redirect from the default action and a second action so we can test that all methods inherit the class-level annotation.

Next lets configure LDAP. To make this a self-contained demo, we’ll use the excellent LDAP server plugin but obviously you’ll need to configure the application to connect to your LDAP server.

Install the plugin by running

grails install-plugin ldap-server

and add the necessary LDAP configuration option to grails-app/conf/SecurityConfig.groovy (at a minimum useLdap = true)

security {
   ...
   useLdap = true
   ldapServer = 'ldap://localhost:10389'
   ldapManagerDn = 'uid=admin,ou=system'
   ldapManagerPassword = 'secret'
   ldapSearchBase = 'dc=d1,dc=example,dc=com'
   ldapSearchFilter = '(uid={0})'
   ldapGroupSearchBase = 'ou=groups,dc=d1,dc=example,dc=com'
   ldapGroupSearchFilter = 'uniquemember={0}'
   ldapUsePassword = false
}

The LDAP plugin requires one or more configured LDAP servers in grails-app/conf/Config.groovy and we’ll need just one:

ldapServers {
   d1 {
      base = 'dc=d1,dc=example,dc=com'
      port = 10389
      indexed = ['objectClass', 'uid', 'mail', 'userPassword', 'description']
   }
}

The plugin will auto-load .ldif data files with user information, so put these records in grails-app/ldap-servers/d1/data/users.ldif:

dn: ou=groups,dc=d1,dc=example,dc=com
objectclass: organizationalUnit
objectclass: top
ou: groups

dn: cn=USER,ou=groups,dc=d1,dc=example,dc=com
objectclass: groupOfUniqueNames
cn: USER
objectclass: top
uniqueMember: cn=person1,dc=d1,dc=example,dc=com
uniqueMember: cn=person2,dc=d1,dc=example,dc=com
uniqueMember: cn=person3,dc=d1,dc=example,dc=com

dn: cn=ADMIN,ou=groups,dc=d1,dc=example,dc=com
objectclass: groupOfUniqueNames
objectclass: top
cn: ADMIN
uniqueMember: cn=person2,dc=d1,dc=example,dc=com

dn: cn=person1,dc=d1,dc=example,dc=com
objectClass: uidObject
objectClass: person
objectClass: top
objectClass: organizationalPerson
uid: person1
userPassword: {SHA}44rSFJQ9qtHWTBAvrsKd5K/p2j0=
cn: person1
sn: jones

dn: cn=person2,dc=d1,dc=example,dc=com
objectClass: uidObject
objectClass: person
objectClass: top
objectClass: organizationalPerson
uid: person2
userPassword: {SHA}KqYKj/f81HPTIeAUav2eJt85UUc=
cn: person2
sn: jones

dn: cn=person3,dc=d1,dc=example,dc=com
objectClass: uidObject
objectClass: person
objectClass: top
objectClass: organizationalPerson
uid: person3
userPassword: {SHA}ERnP037iRzV+A0oI2ETuol9v0g8=
cn: person3
sn: jones

Spring Security will by default convert LDAP groups (‘groupOfUniqueNames’) to roles, prefixing the group names with ROLE_, so this data creates three users; person1, person2, and person3 (with passwords ‘password1’, ‘password2’, and ‘password3’ respectively), all with ROLE_USER and person2 with ROLE_ADMIN.

Since LDAP is only managing authentication details we need local data in the database; create the corresponding entries in BootStrap:

import com.burtbeckwith.springone2gx.User

class BootStrap {

   def init = { servletContext ->
      new User(username: 'person1', enabled: true).save()
      new User(username: 'person2', enabled: true).save()
      new User(username: 'person3', enabled: true).save(flush: true)
   }

   def destroy = {}
}

Since LDAP is handling authentication, we can (partially) remove password-related fields from User.groovy along with other unused fields:

class User {

   static hasMany = [authorities: Role]
   static belongsTo = Role

   String username
   String passwd = 'notused'
   boolean enabled

   static constraints = {
      username blank: false, unique: true
   }
}

We need to leave in the passwd property since GrailsDaoImpl expects it but its value isn’t important so we’ll just hard-code it in the domain class. A custom subclass GrailsDaoImpl or a new implementation of UserDetailsService would remove this requirement.

Start the app using

grails run-app

and open http://localhost:8080/springone2gx_ldap/secure/ in a browser and it should prompt you to login. If you login as person1 or person3 you’ll be denied access since those users only have ROLE_USER but person2 has ROLE_ADMIN and will be allowed.

After successful login it’ll redirect to http://localhost:8080/springone2gx_ldap/secure/foo – verify that http://localhost:8080/springone2gx_ldap/secure/bar is also secured by going to that in your browser.


You can download a finished application based on this discussion here

SpringOne 2GX Sample Apps – Spring Security Form Login

Tuesday, December 01st, 2009

This is the first in a series of posts making the demo applications that I used for my SpringOne 2GX presentations available. I’ll describe here how to create a standard Grails application using the Spring Security plugin that authenticates users from a database. This was used in the Demystifying Spring Security in Grails talk (you can download the presentation here)

Also refer to the plugin documentation for other tutorials here.


To create a standard application that loads users from a database, run

grails create-app springone2gx
cd springone2gx

To make classpath management simpler in Eclipse/STS I create a grails-app/conf/BuildConfig.groovy (in Grails 1.1 apps; in 1.2 this is done for you) with the line

grails.project.plugins.dir='plugins'

to keep plugins in the project root like in 1.0.x but this is optional.

Next install the plugin:

grails install-plugin acegi

Run the create-auth-domains script to generate the person, authority, and request map domain classes and also grails-app/conf/SecurityConfig.groovy:

grails create-auth-domains com.burtbeckwith.springone2gx.User com.burtbeckwith.springone2gx.Role com.burtbeckwith.springone2gx.Requestmap

The other two scripts that the plugin provides are optional and create CRUD pages (generate-manager) and basic user registration (generate-registration). It’s a good idea to run generate-manager; run generate-registration if it’s useful to you.

grails generate-manager

There are three methods for securing URLs in the plugin and for this demo we’ll use controller annotations, so we’ll need to configure that. We can delete the request map class, controller, and CRUD pages since they won’t be needed. The plugin scripts currently assume you’ll be using request maps, so we have to run generate-manager and generate-registration before deleting these.

  • delete grails-app/domain/com/burtbeckwith/springone2gx/Requestmap.groovy
  • delete grails-app/controller/RequestmapController.groovy
  • delete the grails-app/views/requestmap directory and its GSPs
  • remove the com.burtbeckwith.springone2gx.Requestmap import from grails-app/controller/RoleController.groovy
  • in grails-app/conf/SecurityConfig.groovy, disable requestmaps (useRequestMapDomainClass = false) and enable annotations (useControllerAnnotations = true), and remove the requestMapClass property:
    security {
    
       active = true
    
       loginUserDomainClass = "com.burtbeckwith.springone2gx.User"
       authorityDomainClass = "com.burtbeckwith.springone2gx.Role"
    
       useRequestMapDomainClass = false
       useControllerAnnotations = true
    }
    

The current plugin is monolithic – it has support for several authentication mechanisms (OpenID, Facebook, etc.) If you’re not using these you can delete that code and associated jar files. This is completely optional, and you can always add them back by extracting them from the plugin zip.

If you’re not using OpenID, here are the steps to remove that code:

  • delete the org.codehaus.groovy.grails.plugins.springsecurity.openid package: PLUGIN_DIR/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/openid
  • delete these jars
    • openid4java-0.9.2.jar
    • spring-security-openid-2.0.4.jar
    • xmlsec-1.3.0.jar
    • htmlparser-1.6.jar
    • commons-httpclient-3.0.1.jar
    • openxri-client.jar
    • openxri-syntax.jar
  • in LoginController.groovy
    • delete def openIDConsumer
    • delete def openIDAuthenticationProcessingFilter
    • delete the openIdAuthenticate() action
    • remove the config.useOpenId part in the auth() action

If you’re not using Facebook, here are the steps to remove that code:

  • delete the org.codehaus.groovy.grails.plugins.springsecurity.facebook package: PLUGIN_DIR/src/java/org/codehaus/groovy/grails/plugins/springsecurity/facebook
  • delete these jars
    • facebook-java-api-2.0.4.jar
    • json-20070829.jar
  • in LoginController.groovy
    • remove the config.useFacebook part in the auth() action

If you’re not using Kerberos, here are the steps to remove that code:

  • delete the org.codehaus.groovy.grails.plugins.springsecurity.kerberos package: PLUGIN_DIR/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/kerberos

If you’re not using LDAP, here are the steps to remove that code:

  • delete the org.codehaus.groovy.grails.plugins.springsecurity.ldap package: PLUGIN_DIR/src/groovy/org/codehaus/groovy/grails/plugins/springsecurity/ldap and PLUGIN_DIR/src/java/org/codehaus/groovy/grails/plugins/springsecurity/ldap
  • delete these jars
    • spring-ldap-1.2.1.jar
    • spring-ldap-tiger-1.2.1.jar
  • in LoginController.groovy
    • remove the config.useFacebook part in the auth() action

If you’re not using CAS, here are the steps to remove that code:

  • delete these jars

    • cas-client-core-3.1.1.jar
    • spring-security-cas-client-2.0.4.jar

Even if you don’t use NTLM it’s best to leave it in – the jars and code aren’t large and it’s more complicated to remove than just deleting.

In Eclipse or STS the steps to configure the classpath are:

  • add PLUGIN_DIR/src/groovy as a source folder
  • add PLUGIN_DIR/src/java as a source folder
  • add PLUGIN_DIR/grails-app/services as a source folder
  • add PLUGIN_DIR/lib/spring-security-core-2.0.4.jar
  • add PLUGIN_DIR/lib/spring-security-core-tiger-2.0.4.jar
  • add PLUGIN_DIR/lib/spring-security-ntlm-2.0.4.jar
  • add PLUGIN_DIR/lib/jcifs-1.2.25.jar

Having done all that, let’s create a secured controller to test annotations:

grails create-controller secure

and add the import for the annotation, and annotate at the class level that you must be an admin to access this controller:

import org.codehaus.groovy.grails.plugins.springsecurity.Secured

@Secured(['ROLE_ADMIN'])
class SecureController {

   def index = {
      redirect action: foo
   }

   def foo = {
      render 'OK'
   }

   def bar = {
      render 'also OK'
   }
}

The controller has a redirect from the default action and a second action so we can test that all methods inherit the class-level annotation.

Hitting this controller will require a login, so lets add code to BootStrap to create a user:

import com.burtbeckwith.springone2gx.Role
import com.burtbeckwith.springone2gx.User

class BootStrap {

   def passwordEncoder

   def init = { servletContext ->

      def adminRole = new Role(description: 'Admin role',
                  authority: 'ROLE_ADMIN').save()

      String password = passwordEncoder.encodePassword('p4ssw0rd', null)
      def me = new User(username: 'admin',
                  passwd: password, enabled: true).save()
      adminRole.addToPeople(me)
      adminRole.save(flush: true)
   }

   def destroy = {}
}

Start the app using

grails run-app

and open http://localhost:8080/springone2gx/secure/ in a browser and it should prompt you to login – use the username and password from the user created in BootStrap.

After successful login it’ll redirect to http://localhost:8080/springone2gx/secure/foo – verify that http://localhost:8080/springone2gx/secure/bar is also secured by going to that in your browser.


The ‘secure’ controller blocks access, but anyone can access the role or user controller – open http://localhost:8080/springone2gx/user/list to verify that. So let’s finish locking down the app.

Add the same annotation to UserController and RoleController that you added to SecureController:

import org.codehaus.groovy.grails.plugins.springsecurity.Secured

@Secured(['ROLE_ADMIN'])
class UserController {
...
}
import org.codehaus.groovy.grails.plugins.springsecurity.Secured

@Secured(['ROLE_ADMIN'])
class RoleController {
...
}

Up to now we’ve assumed that all access should be allowed unless it’s explicitly blocked. This is appropriate for many applications, but you might want to take the pessimistic approach of denying access unless it’s allowed. That’s simple to do.

Set controllerAnnotationsRejectIfNoRule to true in grails-app/conf/SecurityConfig.groovy, and since we can’t annotate CSS/JavaScript/images/etc., we’ll need to allow access to those using the controllerAnnotationStaticRules property:

security {

   active = true

   loginUserDomainClass = 'com.burtbeckwith.springone2gx.User'
   authorityDomainClass = 'com.burtbeckwith.springone2gx.Role'

   useRequestMapDomainClass = false
   useControllerAnnotations = true

   controllerAnnotationsRejectIfNoRule = true
   controllerAnnotationStaticRules = [
      '/**/js/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
      '/**/css/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
      '/**/images/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
      '/*': ['IS_AUTHENTICATED_ANONYMOUSLY']
   ]
}

Users won’t be able to log in now, since you’re now allowing access to LoginController. That’s easy to do: add an annotation:

import org.codehaus.groovy.grails.plugins.springsecurity.Secured

@Secured(['IS_AUTHENTICATED_ANONYMOUSLY'])
class LoginController {
...
}

IS_AUTHENTICATED_ANONYMOUSLY means that any access is allowed – “anonymous” logins as well as real logins. Do the same for LogoutController, and CaptchaController and RegisterController if you’re using the registration features of the plugin.

Using this approach, new controllers will be inaccessible until you annotate them. If you need the pessimistic approach, this is preferred to the alternative that controllers accidentally allow full access because you forgot to lock them down.


You can download a finished application based on this discussion here

Fixing the User/Role Many-to-Many in the Grails Spring Security Plugin

Sunday, October 25th, 2009

The User/Role many-to-many relationship in the Grails Spring Security plugin is modeled using the standard GORM mapping approach, i.e. using hasMany and belongsTo. As I pointed out here this is a performance concern when you have a large number of users, since granting a new user a popular role (e.g. ROLE_USER) will cause all other users with that role to be loaded from the database.

To fix this in the current plugin would be a breaking change, but I’m planning on creating a new plugin that will use Spring Security 3 once it’s released, so I thought I’d write up some notes on how to fix the many-to-many mapping for current users. It’s only a few steps.

The first is to map the join table, so you’ll need to create a UserRole domain class (I’m assuming that your person class is named User and your authority class is named Role – translate as appropriate):

import org.apache.commons.lang.builder.HashCodeBuilder

class UserRole implements Serializable {

   User user
   Role role

   boolean equals(other) {
      if (!(other instanceof UserRole)) {
         return false
      }

      return other.user.id == user.id && other.role.id == role.id
   }

   int hashCode() {
      return new HashCodeBuilder().append(user.id).append(role.id).toHashCode()
   }

   static UserRole create(User user, Role role, boolean flush = false) {
      new UserRole(user: user, role: role).save(flush: flush, insert: true)
   }

   static boolean remove(User user, Role role, boolean flush = false) {
      UserRole userRole = UserRole.findByUserAndRole(user, role)
      return userRole ? userRole.delete(flush: flush) : false
   }

   static void removeAll(User user) {
      executeUpdate("DELETE FROM UserRole WHERE user=:user", [user: user])
   }

   static mapping = {
      id composite: ['role', 'user']
      version false
      table 'role_people'
   }
}

Some notes on this class:

  • it has to implement Serializable since it’s a Hibernate composite primary key class
  • the mapping block settings ensure that the table DDL is the same as that for the autogenerated join table, so you won’t need to update your database
  • the hashCode and equals methods are just suggestions; feel free to re-implement

Next remove static hasMany = [people: User] from Role and static hasMany = [authorities: Role] and static belongsTo = Role from User.

While we don’t want to map the Role’s User collection, we still need convenient access to the User’s roles, so next add a utility method to User to mimic what we removed when deleting the hasMany. While we’re here let’s add a hasRole method:

Set<Role> getAuthorities() {
   UserRole.findAllByUser(this).collect { it.role } as Set
}

boolean hasRole(Role role) {
   UserRole.countByUserAndRole(this, role) > 0
}

If you’re using the plugin-generated CRUD pages (created via grails generate-manager) you’ll want to remove the User listings from views/role/show.gsp:

<tr class="prop">
   <td valign="top" class="name">People:</td>
   <td valign="top" class="value">${authority.people}</td>
</tr>

and views/role/edit.gsp:

<tr class="prop">
  <td valign="top" class="name"><label for="people">People:</label></td>
  <td valign="top" class="value ${hasErrors(bean:authority,field:'people','errors')}">
  <ul>
  <g :each var="p" in="${authority.people?}">
     <li>${p}</li>
  </g>
  </ul>
  </td>
</tr>

Then in RegisterController.groovy change

role.addToPeople(person)

to

UserRole.create(person, role)

and finally in UserController.groovy, change (in two places)

Role.findAll().each { it.removeFromPeople(person) }

to

UserRole.removeAll(person)

and

Role.findByAuthority(key).addToPeople(person)

to

UserRole.create(person, Role.findByAuthority(key))

And that’s it. You shouldn’t need to make any database changes, since the new code will map to the existing tables just like the old code. If you’ve used the addToPeople and removeFromPeople dynamic many-to-many methods elsewhere in your code you’ll need to convert those to use the UserRole helper methods, but otherwise the impact should be fairly minor.

Fixing Grails 1.0 Many-to-Many Mappings in 1.1

Sunday, March 01st, 2009

One of the more frequently asked questions about Grails involves its “backwards” Many-to-Many mapping. Given domain classes Foo and Bar, the join table foo_bar that’s generated for them will have a ‘foo’ column that points to the bar table and a ‘bar’ column that points to the foo table. In the comments of this bug it’s explained that this was intentional. But in 1.1 the Grails team decided to listen to the users and rework the approach.

Unfortunately this is a stealth fix that’s listed in the release notes along with many other changes but not in the breaking changes and it is very much a breaking change – unless you make changes your many-to-many relationships will not work after a 1.0 -> 1.1 upgrade. This is of particular interest to me as a developer on the Spring Security (Acegi) plugin since Role < -> User relationships are modeled as a many-to-many.

The plugin uses three domain classes, User, Role, and Requestmap. You can use whatever names you want but I’ll assume those names here; note that Requestmap isn’t affected by this issue. In Grails 1.0 your database would have two tables, user and role, and a many-to-many join table role_user, with backwards mappings, i.e. people_id refers to role and authorities_id refers to user:

CREATE TABLE role (
   id BIGINT NOT NULL auto_increment,
   version BIGINT NOT NULL,
   authority VARCHAR(255) NOT NULL UNIQUE,
   description VARCHAR(255) NOT NULL,
   PRIMARY KEY (id)
) ENGINE=InnoDB;

CREATE TABLE user (
   id BIGINT NOT NULL auto_increment,
   version BIGINT NOT NULL,
   description VARCHAR(255) NOT NULL,
   email VARCHAR(255) NOT NULL,
   email_show bit NOT NULL,
   enabled bit NOT NULL,
   passwd VARCHAR(255) NOT NULL,
   user_real_name VARCHAR(255) NOT NULL,
   username VARCHAR(255) NOT NULL unique,
   PRIMARY KEY (id)
) ENGINE=InnoDB;

CREATE TABLE role_user (
   people_id BIGINT NOT NULL,
   authorities_id BIGINT NOT NULL,
   PRIMARY KEY (people_id, authorities_id)
) ENGINE=InnoDB;

ALTER TABLE role_user ADD INDEX FK1407FDF48F01F561 (people_id),
ADD CONSTRAINT FK1407FDF48F01F561 FOREIGN KEY (people_id) REFERENCES role (id);

ALTER TABLE role_user ADD INDEX FK1407FDF4CF6CDEE4 (authorities_id),
ADD CONSTRAINT FK1407FDF4CF6CDEE4 FOREIGN KEY (authorities_id) REFERENCES user (id);

Here the name of the mapping table is the owning end (‘Role’) followed by the owned end (‘User’). Note that the foreign key and index names (‘FK1407FDF48F01F561’, etc.) will probably be different for your tables.

However in Grails 1.1 the join table looks like this:

CREATE TABLE role_people (
	role_id BIGINT NOT NULL,
	user_id BIGINT NOT NULL,
	PRIMARY KEY (role_id, user_id)
) ENGINE=InnoDB;

ALTER TABLE role_people ADD INDEX FK28B75E7852388A1A (role_id),
ADD CONSTRAINT FK28B75E7852388A1A FOREIGN KEY (role_id) REFERENCES role (id);

ALTER TABLE role_people ADD INDEX FK28B75E78F7634DFA (user_id),
ADD CONSTRAINT FK28B75E78F7634DFA FOREIGN KEY (user_id) REFERENCES user (id);

and the mappings are correct – role_id references role and user_id references user. The name is the owning end (‘role’) followed by the collection name of the owned end (‘people’).

This means that your many-to-many relationships are completely broken, and in particular it means that you won’t be able to log in to your application if you’re using the Spring Security plugin. You’ll see errors like this in your logs:

ERROR springsecurity.GrailsDaoImpl  - User [foo] has no GrantedAuthority

So there are two options – migrate your data to the new table, or use the GORM mapping closure to get Grails to work in ‘1.0 mode’. The first option is cleaner, but if you’re accessing your join tables explicitly, e.g. for reporting queries using SQL, then this approach will break those.

For MySQL, the migration command is:

INSERT INTO role_people (role_id, user_id)
SELECT people_id, authorities_id FROM role_user;

and you’ll want to create the missing foreign keys and associated indexes:

ALTER TABLE role_people ADD INDEX FK28B75E7852388A1A (role_id),
ADD CONSTRAINT FK28B75E7852388A1A FOREIGN KEY (role_id) REFERENCES role (id);

ALTER TABLE role_people ADD INDEX FK28B75E78F7634DFA (user_id),
ADD CONSTRAINT FK28B75E78F7634DFA FOREIGN KEY (user_id) REFERENCES user (id);

Once you verify that this worked, you should drop role_user and its foreign keys:

ALTER TABLE role_user DROP FOREIGN KEY FK1407FDF48F01F561;
ALTER TABLE role_user DROP FOREIGN KEY FK1407FDF4CF6CDEE4;
DROP TABLE role_user;

Alternatively, to continue to use “1.0 mode”, you just need to add a mapping closure to each of your domain classes:

In Role.groovy:

static mapping = {
   people column: 'people_id', joinTable: 'role_user'
}

and in User.groovy:

static mapping = {
   authorities column: 'authorities_id', joinTable: 'role_user'
}

and your app will continue to work – and still be backwards 😉

Grails Acegi (Spring Security) Plugin v0.5 Released

Monday, January 12th, 2009

Version 0.5 went out yesterday. This was an interesting one to work on. There were a few relatively minor bug fixes, but there are four new features/enhancements and the plugin now works with Grails 1.1.


Probably the coolest new feature is being able to define security rules directly in the Controllers. Previously there were two ways to define the URL->Role mappings, in a static string (the standard Spring Security approach) or using Requestmap entries in the database. Now there’s a third – using annotations in controllers.

I’d written about using annotations previously and I used that approach. Annotations can be ugly, but I think here they’re great here since they’ll only ever have a fairly short list of values (roles plus special tokens like IS_AUTHENTICATED_FULLY, etc.). You can annotate individual actions and/or put an annotation at the class level and then all of the actions in the controller share those mappings, and if needed you can override that for individual actions. And they’re inheritable, so if you use controller base classes you can define them there and share rules throughout the hierarchy. For example, if you have an AbstractAdminController that all administrative controllers extend you could annotate just the base class with @Secured(['ROLE_ADMIN']) and restrict access to the entire admin hierarchy.

There are some notes here describing the new approach and the original two.

To better support stuff like administrative sections of a site, I added in an IP address filter so you can specify IP ranges (using Ant patterns or masks) for URLs. This way you can restrict access for your admin area to ’10.**’ or ‘192.168.**’ in addition to requiring specific roles to ensure that only users in the intranet or VPN have access.

I also added in support for ChannelProcessingFilter to allow defining which URLs require HTTPS and which require HTTP. With this addition there aren’t many Spring Security filters left that haven’t been mapped in the plugin – just ConcurrentSessionFilter, SessionFixationProtectionFilter, X509PreAuthenticatedProcessingFilter, and RequestHeaderPreAuthenticatedProcessingFilter.

Someone on the Grails user mailing list was talking about Facebook logins, and I’d been wanting an excuse to play with their API so I wired up support for that. It’s of limited use (like the OpenID support) since you don’t get much information from the authentication, so there would be a lot of manual work required in the app when setting up user information in the database. For example, you don’t even get the login name/email, only the numeric ID, so that has to be the username attribute in the User table.

Getting things going with Grails 1.1 was interesting. There was one real bug that I’d introduced in AuthenticatedVetoableDecisionManager – I think it’s a Groovy bug but I worked around it. I had named both a variable and a method deny, and it was trying to invoke deny() on the variable instead of the method. I changed it to denyCount which is more self-documenting and it was fine.

The plugin generates its own controllers and CRUD pages, so I needed to update those to use new 1.1 features. The allowedMethods map is now static so I changed those to avoid warnings at startup. Optimistic locking is now checked in controllers by adding an <input type='hidden'> form element in the GSP with the current domain instance version and checking it against the database after submitting updates, so I wired that up so the User/Role/Requestmap management pages work like non-plugin pages.

I also had to rework my automated testing due to the changes in the way plugins are installed in 1.1. I have an Ant task that creates a test project, installs the plugin, and configures the app for testing. Then I run a suite of automated tests using the WebTest plugin. Plugins are now global by default, so installing a plugin for a second app will share the plugin that the first app installed, which will save a bunch of space both locally and in source control. But I need the plugin to be local to the test app, and this is possible by overriding the default behavior and reverting to the 1.0 approach by setting grails.plugins.dir='plugins' in grails-app/conf/BuildConfig.groovy. And this doesn’t affect 1.0 since BuildConfig.groovy is ignored.

So check out the new release. There are no backwards compatibility issues, so upgrading is simple. Check out the fixes and enhancements here and the docs here.

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 org.springframework.security.annotation.SecurityAnnotationAttributes
    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
    (org.codehaus.groovy.grails.plugins.springsecurity.SecurityAnnotationAttributes)
    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.