Grails, Vaadin and Spring Security Core

I got kind of bored with Flex and all the complexity it introduces by forcing you to switch between ActionScript and whatever you are using for the backend (Groovy in my case). I also got bored with having to regenerate my data service stubs on each server-side change, and having to handle the asynchronous remoting. So I started to have a look at Vaadin.

Vaadin offers the same richness of components as Flex, but I can code my UI with Groovy and it completely removes the need to bother about remoting and all that stuff. It’s really like my old Swing days and I love it.

Last week-end, I tried their AddressBook tutorial, and I adapted it to Grails using the Grails-Vaadin plugin. Then I modified the sample so that it uses GORM to store contacts. And finally I installed spring-security-core plugin to secure my business services with @Secured annotations. And it worked absolutely great.

I just released a new version of the Grails-Vaadin plugin with Vaadin upgraded to 6.5.1 (the latest version at this point), and I uploaded my version of addressbook to GitHub.

For me, the most interesting part is how I got security to work. All I had to do was to install spring-security-core plugin into grails and then define a simple SecurityService like the following:

package org.epseelon.addressbook.business

import org.springframework.security.core.context.SecurityContextHolder as SCH
import org.springframework.security.authentication.BadCredentialsException
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken

class SecurityService {

    static transactional = true

    def springSecurityService
    def authenticationManager

    void signIn(String username, String password) {
        try {
            def authentication = new UsernamePasswordAuthenticationToken(username, password)
            SCH.context.authentication = authenticationManager.authenticate(authentication)
        } catch (BadCredentialsException e) {
            throw new SecurityException("Invalid username/password")
        }
    }

    void signOut(){
        SCH.context.authentication = null
    }

    boolean isSignedIn(){
        return springSecurityService.isLoggedIn()
    }
}

Then I injected this SecurityService into my AddressBookApplication and used it:

class AddressBookApplication extends Application {
    private SecurityService security = (SecurityService)getBean(SecurityService)

    [...]

    boolean login(String username, String password) {
        try {
            security.signIn(username, password)
            refreshToolbar()
            return true
        } catch (SecurityServiceException e) {
            getMainWindow().showNotification(e.message, Notification.TYPE_ERROR_MESSAGE);
            return false
        }
    }
}

Then whenever I try to call a @Secured method:

package org.epseelon.addressbook.business

import org.epseelon.addressbook.dto.PersonListItem
import org.epseelon.addressbook.domain.Person
import grails.plugins.springsecurity.Secured

class PersonService {

    static transactional = true

    [...]

    @Secured(["ROLE_USER"])
    PersonListItem updatePerson(PersonListItem item) {
        Person p = Person.get(item.id)
        if(p){
            p.firstName = item.firstName
            p.lastName = item.lastName
            p.email = item.email
            p.phoneNumber = item.phoneNumber
            p.streetAddress = item.streetAddress
            p.postalCode = item.postalCode
            p.city = item.city
            p.save()

            return new PersonListItem(
                firstName: p.firstName,
                lastName: p.lastName,
                email: p.email,
                phoneNumber: p.phoneNumber,
                streetAddress: p.streetAddress,
                postalCode: p.postalCode,
                city: p.city
            )
        }
        return null
    }
}

If I’m not logged in as a user, I get an “access denied” exception:

package org.epseelon.addressbook.presentation.data

import com.vaadin.data.util.BeanItemContainer
import org.epseelon.addressbook.dto.PersonListItem
import org.epseelon.addressbook.business.PersonService
import com.vaadin.data.util.BeanItem
import com.vaadin.ui.Window.Notification
import org.epseelon.addressbook.presentation.AddressBookApplication

/**
 *
 * @author sarbogast
 * @version 19/02/11, 11:12
 */
class PersonContainer extends BeanItemContainer<PersonListItem> implements Serializable {
    [...]
    boolean updateItem(Object itemId) {
        try {
            personService.updatePerson((PersonListItem) itemId)
            return true
        } catch (Exception e) {
            AddressBookApplication.application.getMainWindow().showNotification(
                    e.message,
                    Notification.TYPE_ERROR_MESSAGE
            );
            return false
        }
    }
}

To see what it looks like, all you have to do is to download the code from GitHub, and run “grails run-app” at the root of it.
If you try to create a new contact of edit an existing one and save it without being logged in, you get an “access denied” message. But if you login as ramon/password, it works.

Note that this project uses Grails 1.3.6 but the plugin supports any version of Grails above 3.2 included. As always, your feedback is more than welcome.