Sunday, June 20, 2010

Problem Accessing Grails Layouts

Just a quick note for posterity: If you're running a grails app (or probably any webapp) on jetty, and you configure the app to use the root (/) contextPath, and you leave jetty's default root webapp (/usr/share/jetty/webapps/root) around, you're in for a world of hurt. With grails, you'll get an obtuse 404 error page with the following message:

Problem accessing /WEB-INF/grails-app/views/layouts/my-custom-layout.gsp

(And no info in the logs.) The fix is of course to delete that vestigial root webapp.

Saturday, June 19, 2010

Cherokee Django

The Cherokee Cookbook and Yann Malet's Blog Roll post have pretty helpful directions for setting up the Cherokee webserver with Django. I found it a little tricky to set it up on a virgin Ubuntu Lucid Lynx system, though, so I thought I'd share my steps to get it going:

Install Cherokee and Django

You can get a fairly recent version of each from the Lucid repositories. Because you will compile uWSGI in a sec, you will also need to get python-dev and libxml2-dev:

$ sudo apt-get install cherokee libcherokee-mod-libssl python-django python-dev libxml2-dev

Install uWSGI

According to the cherokee docs, the best way to run django is via uWSGI. On the uwsgi home page, they do have a link to an ubuntu ppa for uwsgi. It looks pretty well maintained, and it's nice of Steve Crozz to maintain it, but I don't feel great about using someone's personal-package archive in a production environment. So I figured I would just get the code and build it myself.

Unfortunately uwsgi doesn't quite have the "forehead install" kind of make file. As I mentioned before, on ubuntu you need the python-dev and libxml2-dev debs, and you also need to specify the version of python you will use it with. These are the exact steps I took:

$ wget -nd -O /tmp/uwsgi.tar.gz http://projects.unbit.it/downloads/uwsgi-0.9.4.4.tar.gz $ tar xz -C /tmp -f /tmp/uwsgi.tar.gz $ cd /tmp/uwsgi-0.9.4.4 $ VERSION_TO_MAKE=`python --version 2>&1 | perl -ne '/(\d)\.(\d)/; print "$1$2"'` $ make -f Makefile.Py$VERSION_TO_MAKE $ sudo mv uwsgi$VERSION_TO_MAKE /usr/local/bin/uwsgi

Create the django_wsgi File

You need to have a wsgi script just like Yann described in his Blog Roll post; since I'm not all that hip to django, though, I didn't know exactly what to call it or where to put it — this is how I eventually ended up organizing my django project:

/srv sites MyProject __init__.py django_wsgi.py manage.py settings.py urls.py

That is, I called the wsgi script django_wsgi.py, and put it in the root of my django project, at the same level as the settings.py file. The django_wsgi.py script contains exactly what Yann described (where MyProject is the name of your project):

import os import django.core.handlers.wsgi # init django settings os.environ['DJANGO_SETTINGS_MODULE'] = 'MyProject.settings' # define wsgi app application = django.core.handlers.wsgi.WSGIHandler() # mount this application at the webroot applications = { '/': 'application' }

Configure Cherokee

Since I'm a complete cherokee noob, the cherokee docs were a little bit thin on this point (the screencast included in the docs might have been exactly what I needed, but I can't stand to sit around and watch screencasts, so I skipped it); this is what you do:

  1. Run the cherokee admin server: $ sudo cherokee-admin
  2. Open up your web browser to http://127.0.0.1:9090 and enter the credentials that cherokee-admin spit out.
  3. Click the Virtual Servers navigation link.
  4. Click the Wizards button at the bottom of the virtual servers list.
  5. Select Platforms from the Category list, and click the Run Wizard link next to uWSGI.
  6. On the first page of the wizard, enter the full domain name of your server in the New Host Name (once you get everything working with the new virtual server added by the wizard, you may want to delete it and just re-add the uwsgi handler manually in the default virtual server).
  7. The Document Root field is irrelevant for now; I just set it to my django project's static directory.
  8. For the Configuration File field, enter the path to your wsgi script (you'll change this setting in a sec anyway, so don't sweat it too much), like /srv/sites/MyProject/django_wsgi.py.
  9. For the Same logs as vserver field, I have no idea what this does, so I chose the default.
  10. Click the Submit button to finish the wizard, then go back and click the Information Sources navigation link, and select the new uWSGI 1 source you just created.
  11. The Interpreter field will contain something like this:
    /usr/local/bin/uwsgi -s 127.0.0.1:41058 -t 10 -M -p 1 -C -w /srv/sites/MyProject/django_wsgi.py
    — change it to this:
    /usr/local/bin/uwsgi -s 127.0.0.1:41058 -t 10 -M -p 1 -C --pythonpath /srv/sites -w MyProject.django_wsgi
    (where /srv/sites is the directory which contains your django project-directory (MyProject), and MyProject is the name of your project).
  12. Save it, and try navigating to your server in a browser.

Troubleshooting

Getting the right Interperter field setting in cherokee took me a while, and the logs were of no help. What you need to do is just run the uwsgi command (/usr/local/bin/uwsgi ...) yourself in a shell (killing the existing uwsgi instances if necessary); as long as you run it with the same socket as specified in cherokee's Connection field, cherokee will try to use the uwsgi instance you spun up, instead of spinning up its own instance. When you run uwsgi from a shell, you do get all the regular logging and error messages you would expect to see from django (from the shell stdout/stderr).

The other thing I found (which is probably pretty obvious if you know what you're doing) is that I had to give the uwsgi user (by default the same as the cherokee user, www-data) write rights on the django project. So I ended up creating a new copy of the django source from my working copy to /srv/sites/MyProject, and then making www-data the owner of that copy (sudo chown -R www-data:www-data /srv/sites/MyProject). There's probably a better way to do that, but this way worked.

Sunday, June 13, 2010

Mocking Taglibs with Grails Units

Grails taglibs are a million times better than JSP taglibs (requiring way less busywork), but they still can be tricky to unit test. Here are some tricks I've learned so far:

Basic Unit Testing

The first trick is that there's basically no official documentation for how to write even basic taglib unit tests. There is, however, a nice TagLibUnitTestCase class provided with grails that's easy to use (once you figure it out). For a simple taglib like this:

package myapp class MyTagLib { // optionally use <my:tag> namespace instead of <g:tag> static namespace = 'my' def heading = { attrs, body -> def level = attrs.level ?: 1 out << "<h$level>" << body() << "</h$level>" } }

You can write a TagLibUnitTestCase like this:

package myapp import grails.test.TagLibUnitTestCase class MyTagLibUnitTests extends TagLibUnitTestCase { void testHeadingWithNoLevelAndNoContent() { tagLib.heading [:], {''} assertEquals '<h1></h1>', tagLib.out.toString() } void testHeadingWithLevelAndSimpleContent() { tagLib.heading level: '2', {'simple'} assertEquals '<h2>simple</h2>', tagLib.out.toString() } }

The TagLibUnitTestCase class automatically determines which taglib you're testing based on the name of your test class, and sets up a stub taglib as the tagLib member of the test class. Content written to the out variable in your taglib is written to the out (StringWriter) member of this stubbed taglib.

Using Codecs

If you use one of grails' encodeAs string methods (like encodeAsHTML()) in your taglib, you need to explicitly set up that codec in your unit test, using the loadCodec helper method, like this:

package myapp import grails.test.TagLibUnitTestCase import org.codehaus.groovy.grails.plugins.codecs.HTMLCodec class MyTagLibUnitTests extends TagLibUnitTestCase { protected void setUp() { super.setUp() loadCodec(HTMLCodec) } }

Another thing to note specifically about the HTMLCodec is that encodeAsHTML() escapes double-quotes, but not single-quotes — so when you output html from a taglib, make sure you always use double-quotes for attributes in your html. The reason for this is that if you print out a variable in a single-quoted attribute — or you change your code later to print out a variable — and that variable's value came from some content that your users can manipulate, you're vulnerable to xss — even though you used encodeAsHTML() to escape that variable's content. Here's a quick example of what not to do:

class MyTagLib { def foo = { attrs, body -> def bar = "' onmouseover='alert(\"xss\")'" out << "<a href='" << bar.encodeAsHTML() << "'>Bar</a>" } }

When the taglib is used, a single-quote in the variable content "breaks-out" of the attribute, giving the attacker full access to act as the user (presumably to do something nastier than simply display an alert):

<a href='' onmouseover='alert("xss")'>Bar</a>

Calling Taglibs from Taglibs

The first trick for calling taglibs from other taglibs is to make sure always to return an empty string from all your taglibs:

package myapp class MyTagLib { // optionally use <my:tag> namespace instead of <g:tag> static namespace = 'my' def linkToWikipedia = { attrs, body -> out << "<a href=\"http://en.wikipedia.org/wiki/${body().encodeAsHTML()}\">${body().encodeAsHTML()}</a>" '' } }

Otherwise you get duplicated text or other weird values in the stubbed output-stream when you try to unit-test the taglib. When you actually call one taglib from another taglib, the second trick is to append the result from the called taglib to the output of the caller:

package myapp class MyTagLib { // optionally use <my:tag> namespace instead of <g:tag> static namespace = 'my' def heading = { attrs, body -> def level = attrs.level ?: 1 out << "<h$level>" << (attrs.wikipedia ? linkToWikipedia([:], body) : body()) << "</h$level>" '' } def linkToWikipedia = { attrs, body -> out << "<a href=\"http://en.wikipedia.org/wiki/${body().encodeAsHTML()}\">${body().encodeAsHTML()}</a>" '' } }

Otherwise the output of the called taglib is discarded when you run your app in the full grails environment.

And the third trick is that when you're trying to simulate nested taglibs in a unit test, just call the nested taglibs in the container's closure, and don't do anything else — don't try to append their output to the stubbed output-stream or anything fancy like that; for example, when you're trying to simulate this GSP code:

<my:table in="mydata" var="data"> <my:tr><my:td>${data.id}</my:td><my:td>${data.name}</my:td></my:tr> </my:table>

Your unit test should look like this:

package myapp import grails.test.TagLibUnitTestCase class MyTagLibUnitTests extends TagLibUnitTestCase { void testTableWithDataAndTwoCells() { def mydata = [[id:'123', name:'Foo'], [id:'456', name:'Bar']] tagLib.table([ 'in': mydata, 'var': 'data' ], { m -> tagLib.tr([:], { tagLib.td([:], { "${m.data.id}" }) tagLib.td([:], { "${m.data.name}" }) '' }); '' }) assertEquals '<div class="data"><table><tbody><tr class="odd">' + '<td>123</td>' + '<td>Foo</td>' + '</tr><tr class="even">' + '<td>456</td>' + '<td>Bar</td>' + '</tr></tbody></table></div>', tagLib.out.toString() } }

Note that in the example test, the closures simulating the content of the my:table and my:tr taglibs both return an empty string (so you don't get extra junk in the output, as mentioned above); and although when you use (this made-up and hypothetical) my:table in GSP code you can reference the data variable that my:table sets directly (like ${data.name}), in the test you have to reference it as it is actually passed — as an entry in the map passed to the tag's body closure (like ${m.data.name}, where m is the first argument of the table body closure in the example test code).

Beyond Grails Stubs

There are some things that aren't covered by the stub tagLib object provided by the TagLibUnitTestCase. For example, you can't stub the request params map, and accessing the taglib's controllerName or actionName properties fails with a giant flaming fireball.

The simplest way to deal with that is with some groovy metaprogramming. For example, say you want to test a taglib that grabs some data from the request params map:

package myapp class MyTagLib { static namespace = 'my' def seeAlsoWikipedia = { attrs, body -> if (params.q) out << "See also <a href=\"http://en.wikipedia.org/wiki/${params.q.encodeAsHTML()}\">${params.q.encodeAsHTML()}</a> in wikipedia" } }

You can use the tagLib instance's metaClass property to stub out the params map:

package myapp import grails.test.TagLibUnitTestCase class MyTagLibUnitTests extends TagLibUnitTestCase { void testSeeAlsoWikipediaWithAQuery() { tagLib.metaClass.params = [q: 'foo'] tagLib.seeAlsoWikipedia [:], {''} assertEquals 'See also <a href="http://en.wikipedia.org/wiki/foo">foo</a> in wikipedia', tagLib.out.toString() } }

GMock and Expectations

There's a good deal of overlap between the GrailsUnitTestCase class (superclass of TagLibUnitTestCase) and the functionality provided by GMock (in the form of the GMockTestCase class), but GMock still can be useful when you need to stub functionality not already stubbed by TagLibUnitTestCase — and you'd like to do it just by setting up some simple expectations (ie "mocks" in TDD-correct terminology).

GMock has some pretty good documentation about how to include the GMock jar into Grails; to integrate GMock into a TagLibUnitTestCase, use the @WithGMock annotation:

package myapp import grails.test.TagLibUnitTestCase import org.gmock.WithGMock @WithGMock class MyTagLibUnitTests extends TagLibUnitTestCase { void testSeeAlsoWikipediaWithAQuery() { mock(tagLib) { params.returns([q: 'foo']) } play { tagLib.seeAlsoWikipedia [:], {''} assertEquals 'See also <a href="http://en.wikipedia.org/wiki/foo">foo</a> in wikipedia', tagLib.out.toString() } } }

Use GMock's mock() method to mock an object instance (in the above example, the tagLib object already stubbed initially by TagLibUnitTestCase); in the closure passed to the mock() method, set up your expectations. In the above example, the one and only expectation for the tagLib object is for its params property to be accessed; when accessed, GMock will make it return [q: 'foo']. After you've set up the mocks and expectations, everything in the play {} closure will be executed with those mocks.

The example above performs the same exact test as the example before — the only functional difference is that GMock also validates your expectation that params will be accessed.

GMock with Other Taglibs

One thing GMock is especially good for is when a taglib you want to test calls some other taglib. When unit testing, you don't really want to test that other taglib; GMock can help by replacing that other taglib with a mock which validates your expectation that the other taglib was called with the right attributes — but without doing anything other than returning some fixed content.

For example, say you've got a taglib that prints the label, content, and errors for a given property, rendering the errors with grails' standard g:hasErrors and g:renderErrors tags:

package myapp class MyTagLib { static namespace = 'my' def propertyLine = { attrs, body -> def beanAndField = attrs.findAll { ['bean','field'].contains(it) } out << '<div class="property">' out << "<label>${attrs.label}</label>" out << '<div class="content">' << body() << '</div>' out << g.hasErrors(beanAndField, { out << '<div class="errors">' << g.renderErrors(beanAndField, {''}) << '</div>' '' }) out << '</div>' '' } }

You can pretty much assume that g:hasErrors and g:renderErrors has already been tested thoroughly and is working — you just need to test that you're correctly passing the bean and field parameters to them. With the help of GMock, you can test it like this:

package myapp import grails.test.TagLibUnitTestCase import org.gmock.WithGMock @WithGMock class MyTagLibUnitTests extends TagLibUnitTestCase { void testPropertyLineWithBeanAndField() { def bean = new Expando() mock(tagLib.g) { hasErrors([bean:bean, field:'foo'], match{ it instanceof Closure }).returns('_errors_') } play { tagLib.errors [bean:bean, field:'foo'] {''} assertEquals '<div class="property">' + '<label>null</label>' + '<div class="content"></div>' + '_errors_' + '</div>', tagLib.out.toString() } } }

The GMock expectations in the above example validates that g:hasErrors is called with the right bean and field properties, and is passed a closure. It also makes g:hasErrors return '_errors_', so you can validate that the content g:hasErrors would normally generate is in the right place in the context of all the other output generated by your taglib.

Built-in Groovy Mocking

Groovy also has built-in stubbing and mocking functionality, via its StubFor and MockFor classes. These are most useful when you want to stub an object that your taglib creates itself, like the TokenGenerator class in this example:

package myapp class MyTagLib { static namespace = 'my' def onetimeToken = { attrs, body -> def onetime = new TokenGenerator() out << '<input type="hidden" value="' << onetime.generateToken() << '">' '' } }

You can mock the TokenGenerator class like this:

package myapp import grails.test.TagLibUnitTestCase class MyTagLibUnitTests extends TagLibUnitTestCase { void testOnetimeToken() { def mockTokenGenerator = new MockFor(TokenGenerator) mockTokenGenerator.demand.generateToken { '123' } mockTokenGenerator.use { tagLib.onetimeToken [:] {''} assertEquals '<input type="hidden" value="123">', tagLib.out.toString() } } }

The code inside the use closure will use your mocked TokenGenerator behavior in place of the regular TokenGenerator behavior (ie it will return '123' for the generateToken() method), and it will raise an exception if generateToken() wasn't called within the use closure.

Sunday, June 6, 2010

Grails Passwords, Salted

Getting authentication with Spring Security (s2) set up on Grails is nice and easy; getting your s2 passwords salted with a unique value, not so much. There's a real nice grails s2 plugin that Burt Beckwith maintains. It actually is quite well documented, but there's an awful lot of places where the code is still a ways ahead of the documentation.

So here's a quick tutorial to get your grails passwords salted:

1 Install S2

First install the spring-security plugin:

$ grails install-plugin spring-security-core

Then create your "user" and "role" domain objects. You can call the "user" and "role" classes whatever you want, and put them in whatever package you choose. I called mine cq.User and cq.Role:

$ grails s2-quickstart cq User Role

2 Basic Configuration

The s2-quickstart script will automatically add the following to your grails-app/conf/Config.groovy config file:

// Added by the Spring Security Core plugin: grails.plugins.springsecurity.userLookup.userDomainClassName = 'cq.User' grails.plugins.springsecurity.userLookup.authorityJoinClassName = 'cq.UserRole' grails.plugins.springsecurity.authority.className = 'cq.Role'

If you know that your usernames won't change, you can use them to salt the password. While not ideal as salts (an attacker can still build out rainbow tables of common username/password combinations pretty easily), they're a lot better than no salt at all.

To use the username as a salt, all you need to do is add one config setting to your Config.groovy. Unfortunately, the s2 manual had the wrong name for this setting; this is the right setting to add to Config.groovy:

grails.plugins.springsecurity.dao.reflectionSaltSourceProperty = 'username'

I'd also recommend turning on the setting that encodes the hashed passwords as base-64 strings (instead of strings of hex digits; it'll shave a dozen characters off the size of the hashed password):

grails.plugins.springsecurity.password.encodeHashAsBase64 = true

3 Basic Password Hashing

The other thing you need to do is make sure you hash the password with the salt whenever the password is saved. The s2 quickstart tutorial directs you to do this in your user controller. Don't; you should do this in the domain models, so you don't repeat yourself.

So update your "user" class to look like this:

package cq class User { def springSecurityService String username String password boolean enabled boolean accountExpired boolean accountLocked boolean passwordExpired static mapping = { // password is a keyword in some sql dialects, so quote with backticks // password is stored as 44-char base64 hashed value password column: '`password`', length: 44 } static constraints = { username blank: false, size: 1..50, unique: true password blank: false, size: 8..100 } def beforeInsert() { encodePassword() } def beforeUpdate() { if (isDirty('password')) encodePassword() } Set getAuthorities() { UserRole.findAllByUser(this).collect { it.role } as Set } protected encodePassword() { password = springSecurityService.encodePassword(password, username) } }

The main difference between the above and what s2-quickstart generates is the internal encodePassword() method. When a new user is saved, the beforeInsert() method will be called by the gorm framework, and our user class will hash the password, with the username as a salt. When an existing user is updated, the beforeUpdate() method will be called; it will check if the password has changed, and if it has, it will also hash the new password the same way.

This way you never have to hash a user's password in a controller or other code; just pass it on through to the domain model like any other property.

4 A Quick Test

At this point you've done enough to store passwords hashed with the username as a salt. Test it out by adding some test users in your bootstrap code, and a check for authenticated users on your home page.

In grails-app/conf/BootStrap.groovy, create and save a new test user:

class BootStrap { def init = { servletContext -> new cq.User(username: 'test', enabled: true, password: 'password').save(flush: true) } def destroy = { } }

And in grails-app/views/index.gsp, add this to the top of the body:

... <body> <sec:ifLoggedIn><h1>Hey, I know you; you're <sec:username/>!</h1></sec:ifLoggedIn> <sec:ifNotLoggedIn><h1>Who are you?</h1></sec:ifNotLoggedIn> ...

Now run your app (with clean, just to make sure everything gets rebuilt properly):

$ grails clean && grails run-app

Navigate to http://localhost:8080/cq/login (where cq is the name of your app), and login with a username of test and a password of password. Pretty sweet what you get (just about) out of the box, huh?

5 Adding a Unique Salt

Let's kick it up a notch. To create a unique salt for each user (making it impractical for an attacker to use rainbow tables to crack the hashed passwords), add a salt field to your "user" class, and override the getter for this field to initialize it with a unique salt:

package cq import java.security.SecureRandom; // add class User { def springSecurityService String username String password String salt // add boolean enabled boolean accountExpired boolean accountLocked boolean passwordExpired static mapping = { // password is a keyword in some sql dialects, so quote with backticks // password is stored as 44-char base64 hashed value password column: '`password`', length: 44 } static constraints = { username blank: false, size: 1..50, unique: true password blank: false, size: 8..100 // salt is stored as 64-char base64 value salt maxSize: 64 // add } def beforeInsert() { encodePassword() } def beforeUpdate() { if (isDirty('password')) encodePassword() } // add: String getSalt() { if (!this.salt) { def rnd = new byte[48]; new SecureRandom().nextBytes(rnd) this.salt = rnd.encodeBase64() } this.salt } Set getAuthorities() { UserRole.findAllByUser(this).collect { it.role } as Set } protected encodePassword() { password = springSecurityService.encodePassword(password, salt) // update } }

Don't forget to also update the encodePassword() method to hash the password with the salt field, instead of the username field.

6 Adding Custom UserDetails

Here's where it gets tricky. S2 maintains a user class for authentication called UserDetails that's completely separate from your "user" domain model. So you have to provide a custom UserDetailsService class that creates a custom UserDetails object given a username, as well as a custom SaltSource helper-class to extract the salt value from the custom UserDetails.

The good news is that you don't have to write a whole lot of code to do this. Create the following class as src/groovy/cq/MyUserDetailsService.groovy (or with whatever namespace and classname you like):

package cq import org.codehaus.groovy.grails.plugins.springsecurity.GormUserDetailsService import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUser import org.springframework.security.core.GrantedAuthority import org.springframework.security.core.userdetails.UserDetails class MyUserDetailsService extends GormUserDetailsService { protected UserDetails createUserDetails(user, Collection authorities) { new MyUserDetails((GrailsUser) super.createUserDetails(user, authorities), user.salt ) } }

And create the following as src/groovy/cq/MyUserDetails.groovy:

package cq import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUser class MyUserDetails extends GrailsUser { public final String salt MyUserDetails(GrailsUser base, String salt) { super(base.username, base.password, base.enabled, base.accountNonExpired, base.credentialsNonExpired, base.accountNonLocked, base.authorities, base.id) this.salt = salt; } }

And create the following as src/groovy/cq/MySaltSource.groovy:

package cq import org.springframework.security.authentication.dao.ReflectionSaltSource import org.springframework.security.core.userdetails.UserDetails class MySaltSource extends ReflectionSaltSource { Object getSalt(UserDetails user) { user[userPropertyToUse] } }

Note that if you use a java UserDetails implementation, instead of a groovy implementation, you can just use ReflectionSaltSource directly — you need to customize it only to do groovy "reflection" (it does java reflection just fine).

7 Configuring UserDetails

Finally, you can configure s2 to use your custom UserDetails class by adding the following to your grails-app/conf/spring/resources.groovy:

import org.codehaus.groovy.grails.commons.ConfigurationHolder as CH beans = { userDetailsService(cq.MyUserDetailsService) { sessionFactory = ref('sessionFactory') transactionManager = ref('transactionManager') } saltSource(cq.MySaltSource) { userPropertyToUse = CH.config.grails.plugins.springsecurity.dao.reflectionSaltSourceProperty } }

If you were to implement your UserDetails class in java you could omit the saltSource bean (since it comes configured out-of-the-box to do reflection on java classes). Otherwise, the one last piece of the puzzle is to go back and change the dao.reflectionSaltSourceProperty setting in your grails-app/conf/Config.groovy to your new salt field:

grails.plugins.springsecurity.dao.reflectionSaltSourceProperty = 'salt'

8 A Real Test

Now to verify that all this stuff is working (and will still work when you mess around with your user domain model in the future), you need some integration tests. First, let's tackle the password-hashing scheme; create a test/integration/cq/UsersTests.groovy class, and dump this in it:

package cq class UserTests extends GroovyTestCase { def springSecurityService void testPasswordIsEncodedWhenUserIsCreated() { def user = new User(username: 'testuser1', password: 'password').save(flush: true) assertEquals springSecurityService.encodePassword('password', user.salt), user.password } void testPasswordIsReEncodedWhenUserIsUpdatedWithNewPassword() { def user = new User(username: 'testuser1', password: 'password').save(flush: true) // update password user.password = 'password1' user.save(flush: true) assertEquals springSecurityService.encodePassword('password1', user.salt), user.password } void testPasswordIsNotReEncodedWhenUserIsUpdatedWithoutNewPassword() { def user = new User(username: 'testuser1', password: 'password').save(flush: true) // update user, but not password user.enabled = true user.save(flush: true) assertEquals springSecurityService.encodePassword('password', user.salt), user.password } void testPasswordIsNotReEncodedWhenUserIsReloaded() { new User(username: 'testuser1', password: 'password').save(flush: true) // reload user def user = User.findByUsername('testuser1') assertNotNull user assertEquals springSecurityService.encodePassword('password', user.salt), user.password } }

Now the authentication part. This is a bit awkward, because what we're really testing is that your custom UserDetails is implemented and configured correctly, but let's pretend that it's testing your login controller and stick it in your LoginControllerTests anyway. Create a test/integration/cq/LoginControllerTests.groovy class, and put this in it:

package cq import java.security.Principal import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; class LoginControllerTests extends GroovyTestCase { def daoAuthenticationProvider void testAuthenticationFailsWithIncorrectPassword() { def user = new User( username: 'testuser1', password: 'password', enabled: true ).save(flush: true) def token = new UsernamePasswordAuthenticationToken( new TestPrincipal('testuser1'), 'password1' ) shouldFail(BadCredentialsException) { daoAuthenticationProvider.authenticate(token) } } void testAuthenticationSucceedsWithCorrectPassword() { def user = new User( username: 'testuser1', password: 'password', enabled: true ).save(flush: true) def token = new UsernamePasswordAuthenticationToken( new TestPrincipal('testuser1'), 'password' ) def result = daoAuthenticationProvider.authenticate(token) assertTrue result.authenticated } class TestPrincipal implements Principal { String name TestPrincipal(def name) { this.name = name } boolean equals(Object o) { if (name == null) return o == null return name.equals(o) } int hashCode() { return toString().hashCode() } String toString() { return String.valueOf(name) } } }

And now run your integration tests:

$ grails test-app integration:

The console outputs only a brief overview of the results. If something went wrong, you can find the details in the target/test-reports folder; enter the following in your browser address bar for the html version of the report (where $PROJECT_HOME is the full path to your project):

file://$PROJECT_HOME/target/test-reports/html/index.html

And hey presto, you've got salt.