Grails Spring Security for LDAP authentication and Authorization from Database

tastelessbeachInternet and Web Development

Nov 12, 2013 (3 years and 6 months ago)

141 views

Grails Spring Security for LDAP authentication and Authorization from Database

1.

Create your application using grails create
-
app

2.

Install Spring Security Core Plugin



grails install
-
plugin spring
-
security
-
core

3.

Create your Employee and Role class using grails
s2
-
quickstart (
Note: I am using Employee
instead of User because I am using an MS
-
SQL database and received errors when I used User
)



grails s2
-
quickstart com.app.security Employee Role



In your bootstrap.groovy create a new Role

(
here
)


Role role = new Role(authority:"ROLE_USER")



In your Employee domain class comment out beforeInsert(), beforeUpdate() and
encodePassword() methods. We will not be requiring this as we will be authenticating
against LDAP

(
here
)



I will be using the interceptUrlMap in my config.groovy to secure urls. Copy paste the
content in the section
InterceptUrl

to your config.groovy file

4.

We are now ready to install grails spring security ldap p
lugin. Install by using



grails install
-
plugin spring
-
security
-
ldap



Copy the contents in the section
LDAP Configuration

to your config.groovy . I have not
documented how to setup your AD configuration but I have set it

up for multiple
programs and would be able to help you out if you need.

5.

We have to create three classes and I will be creating them in the package com.app.security .
Note: Your IDE might show a lot of Unresolved class errors but ignore them at this stage.

I spent
a lot of time trying to resolve these classes without trying to run the application, but finally just
ignoring them was all I had to do.



CustomUserDetails

(
code available here
)



CustomUserDetailsContextMapper

(
code available here
)



CustomUserDetailsService

(
code available here
)

6.

Finally your configuration/spring/resources.xml should look like
this

7.

That’s it. Create a controller and add it to your InterceptUrlMap and see it work.

I am a beginner at grails and had to spent quite a lot of time figuring out how this works. This document
is just to help other grails beginners setup LDAP authentication and database authorization. If I have
made any mistakes in this document I would be r
eally happy to learn more. I would also appreciate it if
anyone would be interested in updating this document and including the purpose for each step and
class.

Everything in this document has been taken of posts from the awesome grails community
(
http://g
rails.1312388.n4.nabble.com/

).

Thank you

Contact:
bhushan154@gmail.com



InterceptUrl


grails.plugins.springsecurity.securityConfigType = "InterceptUrlMap"

grails.plugins.springsecurity.interceptUrlMap = [


'/secure/**': ['ROLE_ADMIN'],


'/finance/**': ['ROLE_FINANCE', 'IS_AUTHENTICATED_FULLY'],


'/js/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],


'/css/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],


'/images/**': ['IS_AUTHENTICATED_ANONYMOUS
LY'],


'/*': ['IS_AUTHENTICATED_ANONYMOUSLY'],


'/login/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],


'/logout/**': ['IS_AUTHENTICATED_ANONYMOUSLY']

]




LDAP Configuration


// LDAP config

grails.plugins.springsecurity.ldap.context.manage
rDn =
'[distinguishedName]'

grails.plugins.springsecurity.
ldap.context.managerPassword =
'[password]'

grails.plugins.springsecurity.ldap.context.server = 'ldap://[IP]:[PORT] /'

grails.plugins.springsecurity.ldap.authorities.ignorePartialResultException = tr
ue // typically needed for
Active Directory

grails.plugins.springsecurity.ldap.search.base

=

'[the base directory to start the search. usually
something like dc=mycompany,dc=com]'

grails.plugins.springsecurity.ldap.search.filter="sAMAccountName={0}" // fo
r 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',
'displayName'] // 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 = false



CustomUserDetails


package com.app.security

import org.spr
ingframework.security.core.GrantedAuthority

import org.springframework.security.core.userdetails.User

import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUser


class CustomUserDetails extends GrailsUser{


final String fullName



CustomUserD
etails(String username, String password, boolean enabled,


boolean accountNonExpired, boolean credentialsNonExpired,


boolean accountNonLocked,


Collection<GrantedAuthority> authorities,


long

id, String fullName) {


super(username, password, enabled, accountNonExpired,


credentialsNonExpired, accountNonLocked, authorities, id)



this.fullName = fullName


}


}



CustomUserDetailsContextMapper


package com.app.security

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.GrantedAuthority

import org.springframework.security.core.authority.GrantedAuthorityImpl

import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils

class CustomUserDetailsContextMapper implements U
serDetailsContextMapper {


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


def springSecurityService



@Override


public CustomUserDetails mapUserFromContext(DirContextOperations ctx, String usernam
e,
Collection<GrantedAuthority> authority) {


Employee user = Employee.findByUsername(username)


if(!user){


Employee.withTransaction(){


user = new Employee(username: username,


password:"doesnot
apply",


enabled: true,


accountExpired: false,


accountLocked: false,


passwordExpired: false


)


if (user.validate()){


user.save()


EmployeeRole.create user, Role.findByAuthority('ROLE_USER'), true


}


else{


String errors = ""


user.errors.allErrors.e
ach {


errors = errors + it


}


throw new Exception ("User could not be created." + errors)


}


}


}


def roles = user.getAuthorities()


def

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


def userDetails = new CustomUserDetails(username, user.password, user.enabled, false,


false, false, authorities, user.id, username)


return userDetails


}


@Override


public void mapUserToContext(UserDetails arg0, DirContextAdapter arg1) {


}

}



CustomUserDetailsService


package com.app.security

import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUserDetailsService

import
org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils

import org.springframework.security.core.authority.GrantedAuthorityImpl

import org.springframework.security.core.userdetails.UserDetails

import org.springframework.security.core.userdeta
ils.UsernameNotFoundException

import org.springframework.security.core.GrantedAuthority

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 user
name, boolean loadRoles)


throws UsernameNotFoundException {


return loadUserByUsername(username)


}



UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {



Employee.withTransaction { status
-
>




Employee user = Employee.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.username)


}


}

}



Resour
ces.xml


import com.app.security.*

beans = {


userDetailsService(CustomUserDetailsService)




ldapUserDetailsMapper(CustomUserDetailsContextMapper) {


// bean attributes


springSecurityService = ref("springSecurityService")


}

}


Boots
trap.groovy


import com.app.security.*

class BootStrap {



def init = { servletContext
-
>


Role role = new Role(authority:"ROLE_USER")


}


def destroy = {


}

}




Employee


package com.app.security


class Employee {



transient
springSecurityService



String username


String password


boolean enabled


boolean accountExpired


boolean accountLocked


boolean passwordExpired



static constraints = {



username blank: false, unique: true



password blank: false


}



static mapping = {



password column: '`password`'


}



Set<Role> getAuthorities() {



EmployeeRole.findAllByEmployee(this).collect { it.role } as Set


}



/*


def beforeInsert() {



encodePassword()


}



def beforeUpdate() {



if (isDirty('password')) {




encodePasswor
d()



}


}



protected void encodePassword() {



password = springSecurityService.encodePassword(password)


}*/

}