I have no special talent. I'm only passionately curious - Albert Einstein
August 04, 2010
Grails Integration Testing with the new Spring Security Core Plugin
Posted by Dave Malone
in Grails,
Java,
Technology
If you're not new to Grails, you'll understand that the system and it's plugins have been changing at a whirlwind pace. I had written a previous article on how to get the Acegi Security plugin for Grails to work correctly. Recently though, the Acegi Security plugin page advises that we use the Spring Security Core plugin instead. So, I started using that plugin, and I wanted to find a way to write integration tests on services that rely on an authenticated user being present in the Spring SecurityContextHolder. This article will outline two concepts; the first is adding createdBy and lastUpdatedBy user auditing to domain classes. The second concept is how to test these services in integration or unit tests with the dependancy on an "logged in" or authenticated user.
My setup is fairly simple. First, I have a BaseAuditableEntity class which is a domain class that other domain classes would extend in order to be used in Auditing:
import java.util.Date
class AuditableBaseEntity {
Date dateCreated = new Date()
Date lastUpdated = new Date()
User createdBy
User lastUpdatedBy
static constraints = {
dateCreated nullable:false
lastUpdated nullable:false
createdBy nullable:false
lastUpdatedBy nullable:false
}
static mapping = {
sort dateCreated:'asc'
}
}
Next, I have a BaseAuditableEntityService class which is responsible for obtaining the current principal from the springSecurityService, using the principal to obtain the User object that are used to set on the createdBy and lastUpdatedBy fields on any AuditableBaseEntity class:
class BaseAuditableService {
def springSecurityService
def save(auditable){
def user = User.get(springSecurityService.principal.id)
if(auditable instanceof AuditableBaseEntity){
if(auditable.createdBy == null){
auditable.createdBy = user
}
auditable.lastUpdatedBy = user
}
return auditable.save()
}
}
----------------------------------------------------------------------------
Update August 4th 2010 6:04PM CDT:
It's much easier to test using the SpringSecurityUtils.doWithAuth(username, closure) method like this:
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils
...
void testSaveWithDoWithAuth(){
SpringSecurityUtils.doWithAuth(testUsername, {
def blogEntry = new BlogEntry(content:'test content')
assertNotNull blogEntryService.save(blogEntry)
assertNotNull blogEntry.createdBy
assertEquals testUsername, blogEntry.createdBy.username
assertNotNull blogEntry.lastUpdatedBy
assertEquals testUsername, blogEntry.lastUpdatedBy.username
assertNotNull blogEntry.dateCreated
assertNotNull blogEntry.lastUpdated
})
}
You don't need to use the method I described below, but I still feel the example below contains useful information for other purposes.
----------------------------------------------------------------------------
Then I create a GrailsUnitTestCase class, with the following two fields:
def baseAuditableService
def daoAuthenticationProvider
The daoAuthenticationProvider class is provided by the Spring Security Core plugin. Next, lets create the test method for save:
void testSave() {
def auth = new UsernamePasswordAuthenticationToken('testusername', 'testpassword')
def authtoken = daoAuthenticationProvider.authenticate(auth)
SecurityContextHolder.getContext().setAuthentication(authtoken)
def blogEntry = new BlogEntry(content:'test content')
blogEntryService.save(blogEntry)
assertNotNull blogEntry.createdBy
assertNotNull blogEntry.lastUpdatedBy
assertNotNull blogEntry.dateCreated
assertNotNull blogEntry.lastUpdated
}
You'll notice two classes being introduced here. The first is the org.springframework.security.authentication.UsernamePasswordAuthenticationToken, which we need to use to instantiate the authentication object in order to 'login' to the system (login is performed by calling "daoAuthenticationProvider.authenticate(auth)"). Next is the org.springframework.security.core.context.SecurityContextHolder class, which we use to set the authentication token. This will allow you to mock out the existance of an authenticated user session for use in unit and integration tests.
The Spring Security Core plugin class does contain a utility class called SpringSecurityUtils, with a utility method doWithAuth(username, closure), but I am yet to investigate if this works or not.
Resources: