Wednesday, September 14, 2011

Grails 1.3.7, Spring Security and OpenID... with exception

Here is the list of steps I performed to set up OpenID authentication with Spring Security:

1) Create Project with Grails 1.3.7
grails create-project UserManagement 
The file application.properties will look something like this:
app.grails.version=1.3.7
app.name=UsersManagement
app.servlet.version=2.4
app.version=0.1
plugins.hibernate=1.3.7
plugins.tomcat=1.3.7 

2) Installing the Spring Security plugin (website and documentation)
grails install-plugin spring-security-core
The file application.properties will now look something like this:
app.grails.version=1.3.7
app.name=UsersManagement
app.servlet.version=2.4
app.version=0.1
plugins.hibernate=1.3.7
plugins.spring-security-core=1.2.1
plugins.tomcat=1.3.7

3) Creating Controller and template classes
grails s2-quickstart org.commonsemantics.scigrails.module.users.security User Role
You will notice the following new files
controllers/LoginController.groovy
controllers/LogoutController.groovy
domain/org.commonsemantics.scigrails.module.users.security.User.groovy
domain/org.commonsemantics.scigrails.module.users.security.Role.groovy
domain/org.commonsemantics.scigrails.module.users.security.UserRole.groovy
views/login/auth.gsp
views/login/denied.gsp
The Config.groovy will be updated with the following lines:
grails.plugins.springsecurity.userLookup.userDomainClassName =
      'org.commonsemantics.scigrails.module.users.security.User'
grails.plugins.springsecurity.userLookup.authorityJoinClassName = 
      'org.commonsemantics.scigrails.module.users.security.UserRole'
grails.plugins.springsecurity.authority.className = 
      'org.commonsemantics.scigrails.module.users.security.Role'
If you are going to change the package of the above classes, just remember to update the above properties.

4) Move controllers to the desired package - org.commonsemantics.scigrails.module.users.security

5) Install the OpenID module (website and documentation)
grails install-plugin spring-security-openid
The file application.properties will now look something like this:
app.grails.version=1.3.7
app.name=UsersManagement
app.servlet.version=2.4
app.version=0.1
plugins.hibernate=1.3.7
plugins.spring-security-core=1.2.1
plugins.spring-security-openid=1.0.3
plugins.tomcat=1.3.7

6) Creates OpenId Controller and templates for it.
grails s2-init-openid
This script adds the following files:
controllers/OpenIdController.groovy
views/openId/auth.gsp
views/openId/createAccount.gsp
views/openId/linkAccount.gsp

7) Move OpenIdController to the desired package - org.commonsemantics.scigrails.module.users.security

8) Add support for the remember-me checkbox
grails s2-create-persistent-token 
     org.commonsemantics.scigrails.module.users.security.PersistentLogin
It adds the file:
domain/org.commonsemantics.scigrails.module.users.security.PersistentLogin.groovy
The Config.groovy is updated with the following lines:
grails.plugins.springsecurity.rememberMe.persistent = true
grails.plugins.springsecurity.rememberMe.persistentToken.domainClassName =
     'org.commonsemantics.scigrails.module.users.security.PersistentLogin'
Once again, if you are going to change the package of the above classe, just remember to update the correspontend property.

9) Creating the OpenID domain class
grails s2-create-openid 
     org.commonsemantics.scigrails.module.users.security.OpenID 
The script creates on file:
domain/org.commonsemantics.scigrails.module.users.security.OpenID.groovy
The Config.groovy is updated with the following line:
grails.plugins.springsecurity.openid.domainClass =       
     'org.commonsemantics.scigrails.module.users.security.OpenID'
Once again, if you are going to change the package of the above classe, just remember to update the correspontend property.

10) Adding OpenIDs to the User domain class

The following line of code has to be added to the existing User.groovy class:
static hasMany = [openIds: OpenID]

11) Creating some test users

As suggested by the documentation we can now create some test users by editing the Bootstrap.groovy file as follows
import org.commonsemantics.scigrails.module.users.security.Role
import org.commonsemantics.scigrails.module.users.security.User
import org.commonsemantics.scigrails.module.users.security.UserRole

class BootStrap {

    def springSecurityService

    def init = { servletContext -> 

        String password = springSecurityService.encodePassword('password')
        
        def roleAdmin = new Role(authority: 'ROLE_ADMIN').save() 
        def roleUser = new Role(authority: 'ROLE_USER').save()

        def user = new User(username: 'user', 
            password: password, enabled: true).save() 
        def admin = new User(username: 'admin', 
            password: password, enabled: true).save()

        UserRole.create user, roleUser 
        UserRole.create admin, roleUser 
        UserRole.create admin, roleAdmin, true 
    } 
}


12) Redirect the login requests

It is now necessary to direct the authentication calls to the new Controller that manages also the OpenIDs. We can add to the UrlMappings.groovy file the following:
"/login/auth" {
         controller = 'openId'
         action = 'auth'
      }
      "/login/openIdCreateAccount" {
         controller = 'openId'
         action = 'createAccount'
      }

13) Running Grails and testing
grails run-app 
Welcome to Grails 1.3.7 - http://grails.org/
Licensed under Apache Standard License 2.0
...
...
Configuring Spring Security ...
Configuring Spring Security OpenID ...
Server running. Browse to http://localhost:8080/UsersManagement

When accessing the page you should see something like this:
I will try it yourself but the authentication did not work for me right away. I logged in with my Google OpenID and I got sent to the screen for creating an account.
I then selected the 'link this OpenID' option.
After introducing the testing credential 'user' and 'password' I got a 'user not found' back.

14) Turning on the logging

First thing I wanted to see if there were any weird things happening by turning on the logging in the Config.groovy file:
debug 'org.springframework.security'
No exceptions emerged when repeating the process.

15) Making sure the User is stored in the DB

We can inspect the default HSQLDB by adding at the end of Bootstrap.groovy init the following:
org.hsqldb.util.DatabaseManager.main()'
this will open the in memory database inspector. Just remember that if you close the inspector that will also shutdown your Grails app. Read more about the inspector here and remember to take out that line once the debugging is done..

The users resulted stored in the database.

16) Making sure the encoding process works correctly

After editing the Bootstrap.groovy file with:
String password = springSecurityService.encodePassword('password')
println 'a ' + password
def admin = new User(username: 'admin', password: password, 
     enabled: true).save(failOnError: true)
println 'b ' + User.findByUsername('admin').password
and a restart, I obtained:
a 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
b 113459eb7bb31bddee85ade5230d6ad5d8b2fb52879e00a84ff6ae1067a210d3

It is clear that the login process is not performing correctly. After investigating I found out that the User domain class includes the methods:
def beforeInsert() {
          encodePassword()
     }

     def beforeUpdate() {
          if (isDirty('password')) {
                encodePassword()
          }
     }

17) Updating the Bootstrap.groovy file

As the User domain class is already performing the encoding, we have to edit the Bootstrap.groovy file line:
String password = springSecurityService.encodePassword('password')
Into
String password = 'password'

And now the login process works for me. We can now remove org.hsqldb.util.DatabaseManager.main() from the end of the Bootstrap.groovy and maybe comment out the logging (see point #14).

18) Remove the unused views and controllers?

It is not possible to remove any of the generated controllers. As far as I know the only file that can be removed is
views/login/auth.gsp
as the new auth.gsp file provided by the OpenID plugin is now in use (see point #12).


You can check out the code as follows:
svn checkout 
     https://common-semantics.googlecode.com/svn/tags/UsersManagement20110914  
     UsersManagement

Resources
OpenID website
Spring Security Plugin for Grails website
OpenID Plugin for Spring Security website

No comments: