Archive

Posts Tagged ‘ldap’

Spring security: CAS + LDAP

December 21st, 2011 No comments

After getting straight LDAP authentication to work with the spring-security-ldap plugin, I moved on to the next requirement which was integrating with CAS. Like many projects before us, we need to do authentication through CAS and then follow up authorization (i.e. role checking) through LDAP. The authentication part was easy thanks to the spring-security-cas plugin. However, there are two mildly annoying issues with the plugin as a whole:

First, once it is installed, you can’t turn it off (at least, not in development mode). The value of the cas.active setting is ignored unless you deploy as a war. There is already a bug filed for this and someone has submitted a simple patch. You can either build the patched plugin, or just tweak the few lines directly in your project’s copy of the plugin.

The second issue relates to auto-creating user accounts. I posted about using an AuthenticationEvent listener to do this last week. Unfortunately, this will not work with the default configuration of the CAS plugin. The plugin does not override the userDetailsService so you get the default GormUserDetailsService. That class will throw a “user not found” exception if there is no local user domain object for the given user name. If you have no need for role information (authorities in spring-security speak), then you can simply plug in a simplistic userDetailsService like this one:

import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUserDetailsService
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.User
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils
import org.springframework.security.core.authority.GrantedAuthorityImpl

/**
 * Dumb service which just returns a UserDetails object with the username set.
 * @author esword
 */
class EmptyUserDetailsService implements GrailsUserDetailsService {

    /**
     * Taken from GormUserDetailsService: Some Spring Security classes expect at least one
     * role, so we give a user with no granted roles this one which gets past that restriction
     * but doesn't grant anything.
     */
    static final List NO_ROLES = [new GrantedAuthorityImpl(SpringSecurityUtils.NO_ROLE)]

    UserDetails loadUserByUsername(String username, boolean loadRoles) {
        return loadUserByUsername(username)
    }

    UserDetails loadUserByUsername(String username) {
        new User(username, '', true, true, true, true, NO_ROLES)
    }
}

You could extend InMemoryUserDetailsManager if you didn’t want to re-create the UserDetails all the time, or wrap a GormUserDetailsService to first check if you have a local account and return info from that if so. I just threw together this class so that I could verify that the rest of the authentication process with CAS worked.

LDAP Integration

If you do need role information from LDAP, you will need to add a few more beans to your resources.groovy file. Someone posted a thread on the grails-dev mailing list about a year ago with the core information for this configuration. However, the example they give hard-codes the LDAP connection settings in the bean definitions themselves. Since our app is deployed with the LDAP plugin (it is turned off if CAS is turned on), I wanted to use the same property settings so that we could easily toggle back and forth between plain LDAP and CAS. Here is the revised bean configuration within resources.groovy:

    // If CAS is active and if ldap is configured, do UserDetails lookup from ldap to get the roles.
    // All of these setting names and how they are used come from reading the SpringSecurityLdapGrailsPlugin.groovy
    if (application.config.grails.plugins.springsecurity.cas.active) {
        def config = SpringSecurityUtils.securityConfig
        if (config.ldap.context.server) {
            SpringSecurityUtils.loadSecondaryConfig 'DefaultLdapSecurityConfig'
            config = SpringSecurityUtils.securityConfig

            initialDirContextFactory(org.springframework.security.ldap.DefaultSpringSecurityContextSource,
               config.ldap.context.server){
                userDn = config.ldap.context.managerDn
                password = config.ldap.context.managerPassword
            }

            ldapUserSearch(org.springframework.security.ldap.search.FilterBasedLdapUserSearch,
               config.ldap.search.base,
               config.ldap.search.filter,
                initialDirContextFactory){
            }

            ldapAuthoritiesPopulator(org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator,
                initialDirContextFactory,
               config.ldap.authorities.groupSearchBase){
                  groupRoleAttribute = config.ldap.authorities.groupRoleAttribute
                  groupSearchFilter = config.ldap.authorities.groupSearchFilter
                  searchSubtree = config.ldap.authorities.searchSubtree
                  rolePrefix = "ROLE_"
                  convertToUpperCase = config.ldap.mapper.convertToUpperCase
                  ignorePartialResultException = config.ldap.authorities.ignorePartialResultException
            }

            userDetailsService(org.springframework.security.ldap.userdetails.LdapUserDetailsService,
                ldapUserSearch,
                ldapAuthoritiesPopulator){
            }
        }
        else {
            //Dummy service if LDAP isn't set up
            userDetailsService(EmptyUserDetailsService)
        }
    }

Ideally, I would like to be able to keep the LDAP plugin turned on in an “authorization only” mode so that I could use the userDetailsService configuration directly from it. That is not yet possible with the plugin, so this is the next best thing. You still avoid having to write any new code in your application and at least get the benefit of being able to fall back on the default property settings for the LDAP plugin.

Auto-create User Domain Object with Spring Security

December 12th, 2011 No comments

For those who skip straight to the last page of a book to see how it ends – See Chap 7. Events of the spring-security-core plugin documentation.

For those who like a little more detail…

I just moved our grails app from using the shiro plugin to using the spring-security plugin(s). I like shiro’s filter-based config, but all the pre-built extension modules that Burt Beckwith has put together for spring-security (LDAP, CAS, etc.) makes it much easier for us to support the range of environments in which we have to deploy.

The one feature which took me a little while to figure out was how to have our app auto-create a user domain object when it is using an external authentication source. For example, say an instance of our app is configured to authentication against an LDAP server. The app has a MyUser class that holds local settings for users like preferences, documents, etc. When a user signs in for the first time and makes it past the authentication step, we need to automatically create a MyUser instance and associate it with the LDAP username. With the shiro-based authentication, we did this in the controller method which handled the authentication itself. Spring security works a little differently and there isn’t a central, post-authentication landing point.

If your app is always deployed with the same type of authentication (e.g. always with LDAP), you could put the persistence code into a custom UserDetailsService. There are several posts on the web that discuss creating a custom UserDetails object and a corresponding service for it, so this was the first approach I looked at. Chapter 11 of the spring-security-core plugin’s user guide has info on it as well. The primary shortcoming is that you can’t chain together UserDetailsServices. You have to implement one for each form of authentication with which you want to work.

If your app must work with a variety of authentication methods, it is easier to register a listener with Spring Security. Chapter 7 of the plugin guide discusses the two ways to do this. I found that handling the AuthenticationSuccessEvent was all I needed. Since we already had a Grail’s service that handles various user-related tasks, the listener object was dirt simple:

import org.springframework.beans.factory.InitializingBean
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import org.springframework.context.ApplicationListener
import org.springframework.security.authentication.event.AuthenticationSuccessEvent

class MyAuthenticationEventListener implements ApplicationListener<AuthenticationSuccessEvent>, InitializingBean, ApplicationContextAware {
    ApplicationContext applicationContext
    def userService

    void afterPropertiesSet() {
        userService = applicationContext.getBean('userService')
    }

    void onApplicationEvent(AuthenticationSuccessEvent e) {
        //the principal field of the source object is a UserDetails object of some form.
        //The spring-security API contract guarantees that at least the username field will be populated.
        userService.createUser(e.source.principal)
    }
}

Since the whole class really just has one line of “functional” code, I could have used the closure-based approach described in section 7.3 of the user guide. I just prefer to keep true code out of the Config.groovy file.

Then, within the UserService.createUser method, the key lines look something like this:

    def user = MyUser.findByUsername(userDetails.username)
    if (!user)
    {
        MyUser.withTransaction {status ->
            user = new MyUser(username:userDetails.username /*, set any other props you want to store locally*/)
            user.save(flush: true, failOnError:true)
        }
    }
    return user

One note of interest – without the withTransaction statement, you may get an exception stating that no hibernate session exists and one cannot be opened. The withTransaction closure wraps this up nicely for you.