Registering Custom Mongo User Types Part 2

It turns out that the registring process described in my previous blog post currently doesn’t work. The reason: Datastore mapping is already finished before Bootstrap is called.

Digging through the grails-datastore-core source code I found out that AbstractMappingContextFactoryBean has a method registerCustomTypeMarshallers which scans all spring beans and automatically registers beans of type org.grails.datastore.mapping.engine.types.CustomTypeMarshaller as custom type marshallers.

So the simple solution for registering Custom Type Marshaller (for now) is to first write the marshaller class and then to register it as spring bean. For the custom Birthday type from the mongodb plugin documentation this would be:

In grails-app/conf/spring/resources.groovy:

// Place your Spring DSL code here
beans = {
  customBirthdayType(CustomBirthdayType)
}

Registering Custom Mongo User Type during Grails Bootstrap

Short note to self for future reference: Creating custom user types for grails mongodb plugin is nicely desribed in the user guide. Actually registering it is not. So here we go…

In grails-app/conf/Bootstrap.groovy:

class BootStrap {
  MongoDatastore mongoDatastore

  def init = { servletContext ->
   //register sample user type from plugin documentation
   mongoDatastore.mappingContext.mappingFactory.registerCustomType(new BirthdayType())
  }
  def destroy = {
  }
}

Update: Concurrent user sessions management in grails 2 using spring-security-core plugin

While the approach towards managing concurrent users sessions in grails 2 using Spring Security Plugin described in my previous post works well, I just noticed that the configuration breaks some of the custom config options from Spring Security Core plugin.

The reason for that is simple: We replaced the authenticationProcessingFilter that comes with Spring Security Core plugin with our custom implementation in order to set our concurrentSessionControlStrategy:

authenticationProcessingFilter(UsernamePasswordAuthenticationFilter) {
  sessionAuthenticationStrategy=concurrentSessionControlStrategy
  authenticationManager=ref("authenticationManager")
}

A more elegant way to achieve the same is to not redefine the complete bean in resources.groovy but instead to only set the sessionAuthenticationStrategy in Boostrap.groovy.
For your reference, here is how the updated resources.groovy looks like:

import org.springframework.security.core.session.SessionRegistryImpl
import org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy
import org.springframework.security.web.session.ConcurrentSessionFilter

beans = {
  sessionRegistry(SessionRegistryImpl)

  concurrencyFilter(ConcurrentSessionFilter) {
    sessionRegistry = sessionRegistry
    logoutHandlers = [ref("rememberMeServices"), ref("securityContextLogoutHandler")]
    expiredUrl='/login/concurrentSession'
  }
  concurrentSessionControlStrategy(ConcurrentSessionControlStrategy, sessionRegistry) {
    alwaysCreateSession = true
    exceptionIfMaximumExceeded = false
    maximumSessions = 1
  }
  /*
  authenticationProcessingFilter(UsernamePasswordAuthenticationFilter) {
    sessionAuthenticationStrategy=concurrentSessionControlStrategy
    authenticationManager=ref("authenticationManager")
  }
  */
}

And here is the updated Bootstrap.groovy:

class BootStrap {

  def authenticationManager
  def concurrentSessionController
  def securityContextPersistenceFilter
  def authenticationProcessingFilter
  def concurrentSessionControlStrategy

  def init = { servletContext ->
    SpringSecurityUtils.clientRegisterFilter('concurrencyFilter', SecurityFilterPosition.CONCURRENT_SESSION_FILTER)
    authenticationProcessingFilter.sessionAuthenticationStrategy = concurrentSessionControlStrategy
  }

  def destroy = {
  }
}

Now all spring security related config options from Config.groovy are left untouched and only out authentication processing strategy is updated.

I’ll put all this into a short demo app and then publish it on github as soon as I have time for it again…

Restricting concurrent user sessions in grails 2 using spring-security-core plugin

A common requirement in web application development is to restrict multiple concurrent user logins. In other words, a person who subscribed to your shiny new web app using the cheap single-user-subscription should not be able to login from several different computers at the same time.

Let’s see who we can implement that functionality using Grails 2.0 and Spring Security Core Plugin (v1.2.7.1, which uses Spring Security 3.0.7 under the hood). While digging for extisting solutions I cam across several slightly outdated posts on the grais mailing lists (post1 post2 post3), an unanswered question on stackoverflow, as well as the excellent Spring Security 3 and Grails Spring Security Plugin documentations.

Based on all this I worked my way towards the solution described below.

First we need to enable the HttpSessionEventPublisher in order to participate in session events. Simply add

grails.plugins.springsecurity.useHttpSessionEventPublisher = true

to Config.groovy and spring security plugin takes care of the rest. As soon as the listener is registered an ApplicationEvent will be published to Spring ApplicationContext each time a HttpSession commences or ends. This event can then be consumed by a ConcurrentSessionFilter as described below.

Then, in resources.groovy, we define a sessionRegistry bean (refering to the default SessionRegistryImpl provided by Spring Security), a concurrencyFilter bean, and a concurrentSessionControlStrategy bean.

import org.springframework.security.core.session.SessionRegistryImpl
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
import org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy
import org.springframework.security.web.session.ConcurrentSessionFilter

beans = {
  sessionRegistry(SessionRegistryImpl)

  concurrencyFilter(ConcurrentSessionFilter) {
    sessionRegistry = sessionRegistry
    logoutHandlers = [ref("rememberMeServices"), ref("securityContextLogoutHandler")]
    expiredUrl='/login/concurrentSession'
  }
  concurrentSessionControlStrategy(ConcurrentSessionControlStrategy, sessionRegistry) {
    alwaysCreateSession = true
    exceptionIfMaximumExceeded = false
    maximumSessions = 1
  }
  // Better not to override the bean but to just set the strategy in Bootstrap.groovy (Details  below)
  /*
  authenticationProcessingFilter(UsernamePasswordAuthenticationFilter) {
    sessionAuthenticationStrategy=concurrentSessionControlStrategy
    authenticationManager=ref("authenticationManager")
  }
  */
}

The concurrentSessionControl strategy allows us to (1) define the maximum permitted number of concurrent sessions a user is allowed to have, and (2) the consequences if this number is exceeded by adjusting exceptionIfMaximumExceeded. It this property is set to false, old sessions of a user are quietly invalidated as soon as he creates a new session (i.e. he logs in on another computer). Otherwise the new login attempt would fail with an exception as long as other valid sessions exist.

The concurrencyFilter does two things. (1) Update sessionRegistry so that the registered session’s “last update” time is always correct, and (2) Check if the session is marked as expired and – if that is the case – call the configured logoutHandlers.

Note: We explicitly configure [ref("rememberMeServices"), ref("securityContextLogoutHandler")] as logoutHandlers because by default Spring Security would inject only the securityContextLogoutHandler into the filter.
Why is that important? If we leave the defaults untouched the following can happen:
User Alice logs in on computer A with “remember-me”-box on the default login page checked. Afterwards Alice also logs in on computer B using the same credentials as before. Upon login on computer B our securityContextLogoutHandler invalidates Alice’s session for computer A and then lets her proceed. If Alice now returns to computer A her session is invalid so that the system triggers a new login. But as it finds a valid remember-me cookie it automatically re-authenticates alice and lets her proceed on Computer A too as long as the cookie remains valid. Clearly not what we wanted to achieve… So always remember to register rememberMeServices as logout handler too whenever your system uses cookie based authentication.

Finally, we override authenticationProcessingFilter (which ships readily configured with spring security core plugin) so that it uses our custom sessionAuthenticationStrategy.
Note that we use the name authenticationProcessingFilter although in Spring Security 3 this class was renamed into UsernamePasswordAuthenticationFilter. But as the spring-security-core plugin (v1.2.7.1) still uses the old name in its default filter chain and as we want to override that particular bean with our new configuration we stick to the old bean name.

Update: Overwriting this bean would break some of the Spring Security Plugin config options. A better approach is to just set the sessionAuthenticationStrategy in Bootstrap.grooby as described in my other blog post..

And that’s pretty much it!! As a very last step we register our concurrencyFilter in the spring security filter chain and then we’re done.

class BootStrap {

  def authenticationManager
  def concurrentSessionController
  def securityContextPersistenceFilter

  def init = { servletContext ->
    SpringSecurityUtils.clientRegisterFilter('concurrencyFilter', SecurityFilterPosition.CONCURRENT_SESSION_FILTER)
  }

  def destroy = {
  }
}

Inject springSecurityService into grails domain class for controller unit testing

With grails Spring Security Core plugin release 1.2 password encryption logic shifted into the Person domain class. This is great as it simplifies password handling. Still its not totally obvious on how to unit test these domain classes and accompanying controllers.

In particular, an instance of grails.plugins.springsecurity.SpringSecurityService needs to be injected into Person domain class in order for password encryption to work properly. But with the new unit testing framework introduced in grails 2 this is easy to accomplish:

The trick is to inject spring security service into the unit test:

defineBeans {
  springSecurityService(SpringSecurityService)
}

Also we need to mock Person domain class and PersonController manually after the springSecurityService bean has been defined. In the scaffolded controller unit test we thus replace the @TestFor(PersonController) and @Mock(Person) annotation with @TestMixin([ControllerUnitTestMixin,DomainClassUnitTestMixin]).
Now we can define mocked controller and domain instances inside our test methods with def controller = testFor(PersonController) and mockDomain(Person).
And that’s it! Controller and domain class now have full access to a properly injected springSecurityService instance.

Please find a complete example below:

import grails.plugins.springsecurity.SpringSecurityService
import grails.test.mixin.TestMixin
import grails.test.mixin.domain.DomainClassUnitTestMixin
import grails.test.mixin.web.ControllerUnitTestMixin
import javax.servlet.http.HttpServletResponse

@TestMixin([ControllerUnitTestMixin,DomainClassUnitTestMixin])
class PersonControllerTests {

void testSave() {
    defineBeans {
      springSecurityService(SpringSecurityService)
    }
    mockDomain(Person)
    def controller = testFor(PersonController)
    controller.save()
    assert response.status == HttpServletResponse.SC_METHOD_NOT_ALLOWED

    response.reset()
    request.method = 'POST'
    controller.save()

    assert model.personInstance != null
    assert view == '/person/create'

    response.reset()

    params["username"] = 'someUser'
    params["password"] = 'somePassword'
    params["enabled"] = true
    controller.save()

    assert response.redirectedUrl == '/person/show/1'
    assert controller.flash.message != null
    assert Person.count() == 1
  }
}

Deploy Grails App to Cloud Foundry via Command Line

A few days ago I received my beta subscription for the great new Cloud Foundry PaaS Platform. I immediately tried to publish one of my Grails projects using the cloud foundry grails plugin as described in Peter Ledbrook’s excellent blog post. Single command deployment of the app works nicely though grails environment startup somewhat slows down deployment process. Also dislike keeping my cloud foundry user credentials in Config.groovy (as it increases the likelihood of accidentally exposing them through version control).
Update: See Burt’s comments below on externalizing config and using grails interactive mode to address these issues.

That’s why I decided to use vmc command line tool instead for managing my cloud foundry instances. I didn’t find a specific grails vmc tutorial, so I figured it out on my own and now record the steps here for reference.

First install vmc. If you have ruby and ruby gem installed (vmc is written in ruby; go to the ruby download page if you need to install ruby on your system) this as simple as

computer:~ user$ gem install vmc

Next, log in to Cloud Foundry:

computer:~ user$ vmc target api.cloudfoundry.com
Succesfully targeted to [http://api.cloudfoundry.com

computer:~ user$ vmc login email@myhost.com
Password: ************
Successfully logged into [http://api.cloudfoundry.com]

Now you’re ready to deploy your application. First create a grails war and then point vmc to the war directory. vmc automatically analyzes the local war file and the remotely available resources in order to only upload those resources not available on Cloud Foundry.

computer:~ user$ cd /my/grails/demoproject
computer:~ user$ rm -rf target/
computer:~ user$ grails prod war
Welcome to Grails 1.3.7 - http://grails.org/
[...]
Done creating WAR /my/grails/demoproject/target/demoproject-0.1-SNAPSHOT.war

computer:~ user$ vmc push demoproject --path target/
Application Deployed URL: 'demoproject.cloudfoundry.com'?
Detected a Java SpringSource Grails Application, is this correct? [Yn]:
Memory Reservation [Default:512M] (64M, 128M, 256M, 512M or 1G)
Creating Application: OK
Would you like to bind any services to 'demoproject'? [yN]: y
Would you like to use an existing provisioned service [yN]? N
The following system services are available::
1. mongodb
2. mysql
3. redis
Please select one you wish to provision: 2
Specify the name of the service [mysql-xyz]:
Creating Service: OK
Binding Service: OK
Uploading Application:
  Checking for available resources: OK
  Processing resources: OK
  Packing application: OK
  Uploading (16K): OK
Push Status: OK
Staging Application: OK
Starting Application: OK

And that’s it! Great! Some things to note, though:

  1. vmc searches for a war file in the place specified by the --path parameter. That’s why you have to run grails prod war first.
  2. vmc simply chooses the first war file it finds in the specified directory. So make sure that you delete old warfiles first
  3. vmc examines the war Meta Data (manifest.mf) to determine required resources. It then uploads only those resources not yet available on Cloud Foundry, which leads to optimized upload sizes. A more detailed description of the magic in behind can be found in this blog post.

After repeating the above steps several times I wrote a minimal shell file (deploy.sh in my grails app root folder) that bundles target folder cleanup, war generation, and deployment for me:

#!/bin/bash
rm -rf target
grails prod war
vmc update demoproject --path target