Question

We are creating a grails aplication where we want the user to log in using their Active Directory credentials. Additionally, we want to give the business owner of this application the ability to control who has access to certain links (actions). Because of this we are using the following plugins in our grails application:

  1. Spring Security Core
  2. Spring Security LDAP
  3. Spring Security UI

Because we want to empower the business user to create customized roles with certain permissions(actions) on the fly when necessary, we think that the best Spring Security Configuration is the Requestmap database based approach

So far we have accomplished as follows:

  • We are being able to authenticate against Active Directory successfully.
  • We have also been able to create different Request Mappings for different roles (ROLE_XXX) through the UI interface of the spring-security-ui plugin

Problems/Questions

The spring-security-core plugin created the following tables:

  • PERSON
  • AUTHORITY
  • PERSON_AUTHORITY
  • REQUESTMAP

These are the tables that support the creation of Roles, the assignment of URLs to roles. However, the Person_Authoritity table as the convention name implies it's a many to many relationship between a PERSON and an AUTHORITY (ROLE) since a person can potentially have more than one role. My problem is that I do not have a Person because the person already exists in Active Directory (an external source) and it was not created in the application.

Is there a way to make the authenticated user to be the PERSON ? The spring security solution requires that Person row or object however you prefer to refer to it.

I have also posted the question here:

http://grails.1312388.n4.nabble.com/Issues-integrating-LDAP-Authentication-with-Requestmap-to-Secure-URLs-td4644040.html

Thanks,

Was it helpful?

Solution

So you need to essentially map the AD user to a Person.

Here are the 3 classes you need in src/groovy. Obviously modify them as needed:

    package yourpackagename

    import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUser
    import org.springframework.security.core.GrantedAuthority

    class CustomUserDetails extends GrailsUser{
        final String firstName
        final String lastName

        CustomUserDetails(String username, String password, boolean enabled,
                          boolean accountNonExpired, boolean credentialsNonExpired,
                          boolean accountNonLocked,
                          Collection<GrantedAuthority> authorities,
                          long id, String firstName, String lastName) {
            super(username, password, enabled, accountNonExpired,
                    credentialsNonExpired, accountNonLocked, authorities, id)

            this.firstName = firstName
            this.lastName = lastName
        }
    }

package yourpackagenamehere

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

class CustomUserDetailsService implements GrailsUserDetailsService {

    /**
     * Some Spring Security classes (e.g. RoleHierarchyVoter) 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)
    throws UsernameNotFoundException {
        return loadUserByUsername(username)
    }

    UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        User.withTransaction { status ->

            User user = User.findByUsername(username)
            if (!user) throw new UsernameNotFoundException('User not found', username)

            def authorities = user.authorities.collect {new GrantedAuthorityImpl(it.authority)}

            return new CustomUserDetails(user.username, user.password, user.enabled,
                    !user.accountExpired, !user.passwordExpired,
                    !user.accountLocked, authorities ?: NO_ROLES, user.id,
                    user.firstName, user.lastName)
        } as UserDetails
    }
}

package yourpackagenamehere

import groovy.sql.Sql

import org.springframework.ldap.core.DirContextAdapter
import org.springframework.ldap.core.DirContextOperations
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper
import org.springframework.security.core.authority.GrantedAuthorityImpl
import org.springframework.security.core.GrantedAuthority
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils

import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.security.authentication.DisabledException

class CustomUserDetailsContextMapper implements UserDetailsContextMapper {

    private static final List NO_ROLES = [new GrantedAuthorityImpl(SpringSecurityUtils.NO_ROLE)]

    def dataSource

    @Override
    public CustomUserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection<GrantedAuthority> authority) {

        username = username.toLowerCase()

        User user = User.findByUsername(username)

        String firstName = ctx.originalAttrs.attrs['givenname'].values[0]
        String lastName = ctx.originalAttrs.attrs['sn'].values[0]


        def roles

        User.withTransaction {

            if(!user){
                user = new User(username: username, enabled: true, firstName: firstName, lastName: lastName)
                user.save(flush: true)
            }
            else {
                user = User.findByUsername(username)
                user.firstName = firstName
                user.lastName = lastName
                user.save(flush: true)
            }

            roles = user.getAuthorities()
        }

        if ( !user.enabled )
            throw new DisabledException("User is disabled", username)


        def authorities = roles.collect { new GrantedAuthorityImpl(it.authority) }
        authorities.addAll(authority)
        def userDetails = new CustomUserDetails(username, user.password, user.enabled, false, false, false, authorities, user.id, user.firstName, user.lastName)

        return userDetails
    }

    @Override
    public void mapUserToContext(UserDetails arg0, DirContextAdapter arg1) {
    }
}

Under configuration in spring/resources.groovy :

import yourpackagenamehere.CustomUserDetailsService
import yourpackagenamehere.CustomUserDetailsContextMapper
beans = {
    userDetailsService(CustomUserDetailsService)

    ldapUserDetailsMapper(CustomUserDetailsContextMapper) {
        dataSource = ref("dataSource")
    }
}

Under Config.groovy, here are my settings:

grails.plugins.springsecurity.ldap.context.managerDn = 'CN=username,OU=People,DC=foo,DC=com'
grails.plugins.springsecurity.ldap.context.managerPassword = 'password'
grails.plugins.springsecurity.ldap.context.server = 'ldap://foo.com:389/'
grails.plugins.springsecurity.ldap.authorities.ignorePartialResultException = true
grails.plugins.springsecurity.ldap.search.base = 'ou=People,dc=foo,dc=com'
grails.plugins.springsecurity.ldap.search.filter="sAMAccountName={0}"
grails.plugins.springsecurity.ldap.search.searchSubtree = true
grails.plugins.springsecurity.ldap.auth.hideUserNotFoundExceptions = false
grails.plugins.springsecurity.ldap.search.attributesToReturn = null
grails.plugins.springsecurity.providerNames = ['ldapAuthProvider', 'anonymousAuthenticationProvider']
grails.plugins.springsecurity.ldap.mapper.userDetailsClass = 'CustomUserDetails'

grails.plugins.springsecurity.ldap.authorities.retrieveGroupRoles = true
grails.plugins.springsecurity.ldap.authorities.retrieveDatabaseRoles = true
grails.plugins.springsecurity.ldap.authorities.groupSearchBase ='dc=foo,dc=com'
grails.plugins.springsecurity.ldap.authorities.groupSearchFilter = 'member={0}' 
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top