Grails LDAP authentication and authorization

Today I’ve been investigating how to integrate a Grails aplication with an LDAP server to perform authentication and authorization.

There are several plugins available to do the authentication in a grails way.

My choice is Spring Security Core Plugin because it is mature and has several extension plugins to extend the functionality and provide integrations with external systems like Facebook, Twitter, OpenId and LDAP .

Installation and configuration are very well documented at : http://grails-plugins.github.com/grails-spring-security-core/docs/manual/

Peter Ledbrook wrote a great introductory article Simplified Spring Security with Grails .

I’ve updated the example application in the post to do the authentication using an LDAP Server.

My server has the following structure:

Sample LDAP structure

Yo can download the sample.ldif

Once you have all the LDAP structure, install the extension LDAP plugin for Spring Security Core

1
grails install-plugin spring-security-ldap

Now modify Config.groovy adding the specific configuration for LDAP spring-security-ldap

1
2
3
4
5
6
7
8
9
10
11
grails.plugins.springsecurity.providerNames = ['ldapAuthProvider','anonymousAuthenticationProvider','rememberMeAuthenticationProvider']
grails.plugins.springsecurity.ldap.context.managerDn = 'uid=admin,ou=system'
grails.plugins.springsecurity.ldap.context.managerPassword = 'YOUR_PASSWORD'
grails.plugins.springsecurity.ldap.context.server = 'ldap://10.99.8.135:10389'
grails.plugins.springsecurity.ldap.authorities.groupSearchBase = 'ou=Groups,dc=example,dc=com'
grails.plugins.springsecurity.ldap.authorities.retrieveGroupRoles = true
grails.plugins.springsecurity.ldap.authorities.retrieveDatabaseRoles = false
grails.plugins.springsecurity.ldap.authorities.groupSearchFilter = 'member={0}'
grails.plugins.springsecurity.ldap.search.base = 'dc=example,dc=com'
grails.plugins.springsecurity.ldap.search.attributesToReturn = ['mail', 'cn', 'sn', 'givenName', 'jpegPhoto' , 'telephoneNumber']
grails.plugins.springsecurity.ldap.authenticator.attributesToReturn = ['mail', 'cn', 'sn', 'givenName', 'jpegPhoto' , 'telephoneNumber']

 

Now you are ready to authenticate against your LDAP, also the group membership is readed from LDAP.

In the expample application the sec tag lib is used to show a link to the create post action based on the role of the logged user.

1
2
3
<sec:ifAllGranted roles="ROLE_USER">
  <g:link controller="post" action="timeline">My Timeline</g:link>
</sec:ifAllGranted>

How is this managed if you are using an LDAP? the answer is simple as everything in grails. Create a group in your LDAP named USER and add the users to it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
dn: cn=USER,ou=Groups,dc=example,dc=com
objectClass: groupOfNames
objectClass: top
cn: USER
description: USER_ROLE
member: uid=wpauli,ou=Users,dc=example,dc=com
member: uid=aeinstein,ou=Users,dc=example,dc=com
member: uid=mborn,ou=Users,dc=example,dc=com
member: uid=mcurie,ou=Users,dc=example,dc=com
member: uid=sito,ou=Users,dc=example,dc=com
createTimestamp: 20111121102018Z
creatorsName: 0.9.2342.19200300.100.1.1=admin,2.5.4.11=system
modifiersName: 0.9.2342.19200300.100.1.1=admin,2.5.4.11=system
modifyTimestamp: 20111121110901Z

I’ve added to the application some other properties that came from LDAP ( Photo, Telephone number, Full Name ).

This is done extending the org.springframework.security.core.userdetails.User to add all new attributes:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.userdetails.User
 
class MyUserDetails extends User {
 
    // extra instance variables final String fullname final String email final String title
    String fullname
    String email
    String title
    String phone
 
    byte[] photo
 
    MyUserDetails(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection authorities, String fullname, String email, String title, byte[] photo, String phone) {
 
        super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities)
 
        this.fullname = fullname
        this.email = email
        this.title = title
        this.photo = photo
        this.phone = phone
    }
 
}

And providing your own implementation for the org.springframework.security.ldap.userdetails.UserDetailsContextMapper interface.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
/**
 *
 * @author SiTo
 */
 
class MyUserDetailsContextMapper implements UserDetailsContextMapper {
 
    UserDetails mapUserFromContext(DirContextOperations ctx, String username, Collection authorities) {
 
        String fullname = ctx.originalAttrs.attrs['cn'].values[0]
        String email = ctx.originalAttrs.attrs['mail'].values[0].toString().toLowerCase()
 
        def title = ctx.originalAttrs.attrs['sn']
 
        def phone = ctx.getStringAttribute('telephoneNumber')
 
        byte[] photo = (byte[])ctx.getObjectAttribute('jpegPhoto')
 
        def userDetails = new MyUserDetails(username, '', true, true, true, true,
            authorities, fullname, email, title == null ? '' : title.values[0], photo, phone)
 
        return userDetails
    }
 
    void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
        throw new IllegalStateException("Only retrieving data from LDAP is currently supported")
    }
 
}

The final step is register the custom implementation using the Spring DSL in the resources.groovy file

1
2
3
4
5
beans = {
   ldapUserDetailsMapper(MyUserDetailsContextMapper) {
      // bean attributes
   }
}

Now we are ready to use all new properties that are maintained by the LDAP into our app.

For example, show the Photo of the logged user:

Add the following method to your PersonController.groovy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class PersonController {
 
    def springSecurityService
 
    // PersonController CODE
 
    def photo = {
        def userDetails = springSecurityService.principal
        def photo = new File(GrailsResourceUtils.WEB_APP_DIR  + "/images/person.jpg").readBytes()
 
        if( userDetails.photo != null ){
            photo = userDetails.photo
        }
 
        response.outputStream << photo
        response.setHeader("Content-disposition", "attachment; filename=avatar.jpg")
        response.contentType = 'image/jpeg'
        response.outputStream << photo
        response.outputStream.flush()
        return;
 
    }
}

And modify the main.gsp to show the photo:

 

1
2
3
4
         <sec:ifLoggedIn>
          Hola <sec:loggedInUserInfo field="fullname"/> [<sec:loggedInUserInfo field="phone"/>] (<g:link controller="logout">Salir</g:link>)
          <img src="${createLink(controller:'person', action:'photo')}" width="40px" />
         </sec:ifLoggedIn>

 

Done!

 

This is how it looks:

That’s all…

Grails Rocks!

See you.

21 Responses to “ Grails LDAP authentication and authorization ”

  1. Hi,

    I have a question, when we use ldap authentication, do we need to create the classes using spring-security-core or create the indivisual class for user like you creted for this example

    Atul

  2. Hi Alfonsorv,

    Thanks for the response and thanks for the post which really help me a lot.
    I still have a few questions:
    1. Which User class ? Which spring-security-core plugin provides or our own custom User class like which you create?
    2. Is it possible to save the user, role information in to the database? if yes then how?

    Please help me solve these issues.

    Thanks in advance,
    Atul

  3. Hi Alfonsorv,

    Thanks for the help !!!
    Keep posting, it really help me a lot and hope will help others to.

    Atul

  4. Hi Alfonsorv,

    As you said, I tried only User and its working fine. But one point I would like to mention here is that, if you want to implement Authentication process via ldap then you need to create security-core class for User as well as for login controller.

    Atul

  5. [...] LDAP, AD and SVN | Inflecto VitaMajor Functionality Associated With Virtual Directory | iPad …Grails LDAP authentication and authorization at blog.alfonsorv.comCopyright © 2012 IT技术园地. All Rights [...]

  6. Bhushan N.N

    Alfonso,

    I would really appreciate your help on this. I installed the grails spring security plugin but when I get to the step where we create the MyUserDetails and MyUserDetailsContextMapper, my imports are not recognised.

    Ex: import org.springframework.ldap.core.DirContextAdapter is not recognised

    Which external jar should I include for this? And which version?

    Please help me out. Also, is there a way to authenticate against LDAP but authorize on our DB?

    Thank you
    Bhushan

  7. Yannier Estévez

    Hi, I just need to check password in LDAP and manage roles from my app. I don’t need any other atributes from LDAP. I generate using s2-quickstart. password in my class user must accept blank in contrains, so I must create a user that exists in LDAP but leaving password blank?. Please help. How can I do it?

  8. Hi, I just need to check password in LDAP and manage roles from my app. I don’t need any other atributes from LDAP. I generate using s2-quickstart. password in my class user must accept blank in contrains, so I must create a user that exists in LDAP but leaving password blank?. Please help. How can I do it?

  9. Hi,
    I am able to authenticate user through LDAP by follows in this blog but i can’t get role of that user,,,
    So please help me on that
    This is my config.groovy file content

    grails.plugins.springsecurity.userLookup.userDomainClassName = ‘com.reportes.SecurityUser’
    grails.plugins.springsecurity.userLookup.authorityJoinClassName = ‘com.reportes.SecurityUserSecurityRole’
    grails.plugins.springsecurity.authority.className = ‘com.reportes.SecurityRole’

    //ldap
    grails.plugins.springsecurity.ldap.context.server = ‘ldap://localhost:389′
    grails.plugins.springsecurity.ldap.context.managerDn = ‘CN=ss1,CN=Users,DC=example,DC=com’
    grails.plugins.springsecurity.ldap.context.managerPassword = ‘pwd’
    grails.plugins.springsecurity.ldap.authorities.groupSearchBase =’CN=Users,DC=example,DC=com’
    //grails.plugins.springsecurity.ldap.authorities.groupSearchBase =’DC=example,DC=com’
    grails.plugins.springsecurity.providerNames=['ldapAuthProvider', 'anonymousAuthenticationProvider']
    grails.plugins.springsecurity.ldap.authorities.retrieveDatabaseRoles = false
    grails.plugins.springsecurity.ldap.authorities.retrieveGroupRoles = true

    grails.plugins.springsecurity.ldap.authorities.ignorePartialResultException= true
    grails.plugins.springsecurity.ldap.search.base = ‘DC=example,DC=com’
    grails.plugins.springsecurity.ldap.search.filter = ‘(sAMAccountName={0})’
    grails.plugins.springsecurity.ldap.search.attributesToReturn = ['cn', 'sn']
    grails.plugins.springsecurity.ldap.authenticator.attributesToReturn = ['cn', 'sn']

    Please look into this

    • Hi Ashish,

      I don’t know how your LDAP is structured, but I think
      grails.plugins.springsecurity.ldap.authorities.groupSearchBase =’CN=Users,DC=example,DC=com’
      is pointing to Users branch not Groups.
      In my example:
      grails.plugins.springsecurity.ldap.authorities.groupSearchBase = ‘ou=Groups,dc=example,dc=com’

      Alfonso

  10. Mi servidor tiene la siguiente configuracion me podrian ayudar a configurar mi ldap:

    Controlador de Dominio: ldap://uci.cu:389

    Nombre de usuario: ad.search@uci.cu

    DN base: OU=UCI Domain Users,DC=uci,DC=cu

    Filtro de búsqueda: objectClass=person

    Tipo de servidor LDAP: Active Directory

    • Hola,
      Pero ¿tienes algún problema? O errores?

      Salu2

      • Si el problema es que no se me conecta, aqui le mando la configuracion que hice, para ver que error tengo…

        // LDAP config
        grails.plugins.springsecurity.ldap.context.managerDn = ‘cn=user, OU=UCI Domain Users, OU=Users,dc=uci, dc=cu’
        grails.plugins.springsecurity.ldap.context.managerPassword = ”
        grails.plugins.springsecurity.ldap.context.server = ‘ldap://10.0.0.2:389/’
        grails.plugins.springsecurity.ldap.authorities.ignorePartialResultException = true // typically needed for Active Directory
        grails.plugins.springsecurity.ldap.search.base = ‘OU=UCI Domain Users, OU=Users,dc=uci,dc=cu’
        grails.plugins.springsecurity.ldap.search.filter=”sAMAccountName={0}” // for Active Directory you need this
        grails.plugins.springsecurity.ldap.search.searchSubtree = true
        grails.plugins.springsecurity.ldap.auth.hideUserNotFoundExceptions = false
        grails.plugins.springsecurity.ldap.search.attributesToReturn = ['mail', 'cn', 'sn', 'givenName', 'jpegPhoto' , 'telephoneNumber'] // extra attributes you want returned; see below for custom classes that access this data
        grails.plugins.springsecurity.providerNames = ['ldapAuthProvider', 'anonymousAuthenticationProvider'] // specify this when you want to skip attempting to load from db and only use LDAP

        // role-specific LDAP config
        grails.plugins.springsecurity.ldap.useRememberMe = false
        grails.plugins.springsecurity.ldap.authorities.retrieveGroupRoles = true
        grails.plugins.springsecurity.ldap.authorities.groupSearchBase =’OU=UCI Domain Users, OU=Users,dc=uci,dc=cu’
        grails.plugins.springsecurity.ldap.authorities.groupSearchFilter = ‘member={0}’ // Active Directory specific – the example settings will work fine for a plain LDAP server

  11. Hola no entiendo esto:
    grails.plugins.springsecurity.ldap.authorities.groupSearchBase = ‘ou=Groups,dc=example,dc=com’
    y
    grails.plugins.springsecurity.ldap.search.base = ‘dc=example,dc=com’
    grails.plugins.springsecurity.ldap.authorities.groupSearchFilter = ‘member={0}’

    Se supone que la busqueda tendria que hacerla en uo=”Users”, por qué no haces referencia ahi?

    O como el plugin sabe asociar eso?

    Saludos

    • Hola,

      toda la configuración de ‘authorities’, tiene que ver con los grupos.

      La documentación de la configuración del plugin la puedes ver en:
      http://burtbeckwith.github.com/grails-spring-security-ldap/docs/manual/guide/single.html#3. Configuration

      • Hola tu ejemplo es bueno, tengo montado un ldap y mi aplicacion grails conecta al LDAP, pero es un proyecto ya creado y no se parece al tuyo, tiene un controlador que llama a esto:

        https://github.com/grails-plugins/grails-spring-security-ldap/blob/master/SpringSecurityLdapGrailsPlugin.groovy

        Mi pregunta es si sabes como puedo obtener lo valores de retorno teniendo yo esto:

        ldap.search.attributesToReturn = ["givenName", "Email", "cn"]

        Gracias

        • Hola Luis,

          creo que no entiendo muy bien tu pregunta, pero a lo mejor esto es lo que necesitas:

          Hay una configuración del plugin donde le indicas los atributos de LDAP que tiene que traer (arriba puedes ver el ejemplo completo), es:

          grails.plugins.springsecurity.ldap.search.attributesToReturn

          Implementas UserDetailsContextMapper para mapear los atributos que vienen del LDAP a las propiedades de tu clase:

          MyUserDetailsContextMapper implements UserDetailsContextMapper

          Luego en tu “controller” puedes injectar el springSecurityService y acceder a esos atributos.

          def springSecurityService
          userDetails.photo

          Creo que es lo que necesitas

          Salu2

Comments are closed.