I have a project that will be an add-on to an existing ERP app. I've got SSO working, with a basic Spring Security setup (see my ticket here: Grails and CAS Basic Setup). Also, I'm not using the s2-quickstart.
I am still new to Java development, so please bear with me...I've been looking through documentation and examples and in some ways I'm more confused by what I've seen. I have a few questions:
1) This setup allows me access to the username of the logged in user, but not much else? How can I access the User object to access these properties? Right now, I am using:
def userDetails = springSecurityService.principal
then: userDetails ?.getUsername() to get the username
and: userDetails ?.getAuthorities() to get roles
This is all working, except is this the best way to access these properties? i.e., are there other properties in the userdetails, and how do I access those?
Perhaps the answer to this depends on hwo I go about this next bit...
2) I am authenticating with CAS, but now I want to pull some additional attributes from LDAP. I have tried creating a custom UserDetailsService like this http://grails-plugins.github.io/grails-spring-security-core/docs/manual/guide/userDetailsService.html but even after I got past initial compilation erros, it still seems to require the User domain class. Must I implement a User domain class if I want to extend UserDetails with additional values?
In my case, I really want just 1 LDAP attribute - and this all seems like a lot of lifting to get it. I have looked and looked but can't find a good working/simple example of doing this...many have Gorm involved or the s2-quickstart installed.
tia,
Bean
UPDATE
Here's my config...see error at the end:
Config.groovy snip
grails.plugins.springsecurity.providerNames = ['casAuthenticationProvider']
grails.plugins.springsecurity.cas.active = true
grails.plugins.springsecurity.cas.sendRenew = false
grails.plugins.springsecurity.cas.serverUrlEncoding = 'UTF-8'
grails.plugins.springsecurity.cas.key = 'changeme'
grails.plugins.springsecurity.cas.loginUri = '/login'
grails.plugins.springsecurity.cas.serviceUrl = '${grails.serverURL}/j_spring_cas_security_check'
grails.plugins.springsecurity.cas.serverUrlPrefix = 'https://cas2.mydomain.com:8443/cas'
grails.plugins.springsecurity.cas.proxyCallbackUrl = '${grails.serverURL}/secure/receptor'
grails.plugins.springsecurity.cas.proxyReceptorUrl = '/secure/receptor'
grails.plugins.springsecurity.logout.afterLogoutUrl = 'https://cas2.mydomain.com:8443/cas/logout?service=' + appProtocol + '://cas2.mydomain.com:' + appPort + '/' + appName + '/'
grails.plugins.springsecurity.cas.artifactParameter = 'ticket'
grails.plugins.springsecurity.cas.serviceParameter = 'service'
grails.plugins.springsecurity.cas.filterProcessesUrl = '/j_spring_cas_security_check'
grails.plugins.springsecurity.cas.useSingleSignout = true
grails.server.loopback.url = ""
//Spring Security Core Config
grails.plugins.springsecurity.rejectIfNoRule = false // V.044::to fix redirect loop::true
grails.plugins.springsecurity.securityConfigType = "InterceptUrlMap"
/*
* Order matters...put the most restrictive first
*/
grails.plugins.springsecurity.interceptUrlMap = [
'/js/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
'/css/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
'/images/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
'/login/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
'/logout/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
'/secure/receptor': ['IS_AUTHENTICATED_ANONYMOUSLY'], // <- allows CAS to contact the receptor
'/protected/**': ['IS_AUTHENTICATED_FULLY'],
'/unprotected/**': ['IS_AUTHENTICATED_ANONYMOUSLY','IS_AUTHENTICATED_FULLY'],
'/filtered/edit': ["hasRole('ROLE_XYZ')"],
'/filtered/create': ["authentication.uid == 'criderk'"],
'/filtered/list': ["hasRole('ROLE_44808')"],
'/filtered/index': ["hasRole('ROLE_44808')"],
'/': ['IS_AUTHENTICATED_ANONYMOUSLY','IS_AUTHENTICATED_FULLY']
]
grails.plugins.springsecurity.ldap.search.attributesToReturn = ['uid','mail']
grails.plugins.springsecurity.userLookup.userDomainClassName = 'basecas_04.User'
grails.plugins.springsecurity.userLookup.authorityJoinClassName = 'basecas_04.UserRole'
grails.plugins.springsecurity.authority.className = 'basecas_04.Role'
resources.groovy
// Place your Spring DSL code here
beans = {
// load ldap roles from spring security
initialDirContextFactory(org.springframework.security.ldap.DefaultSpringSecurityContextSource,
"ldap://xx.xx.xx.xx:389"){
userDn = "cn=admin,dc=mydomain,dc=com"
password = "pw"
}
ldapUserSearch(org.springframework.security.ldap.search.FilterBasedLdapUserSearch,
"ou=employees,dc=mydomain,dc=com", "uid={0}", initialDirContextFactory){
}
ldapAuthoritiesPopulator(org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator,
initialDirContextFactory,"ou=groups,dc=mydomain,dc=com"){
groupRoleAttribute = "gidNumber"
groupSearchFilter = "memberUid={1}"
searchSubtree = true
rolePrefix = "ROLE_"
convertToUpperCase = true
ignorePartialResultException = true
}
ldapUserDetailsService(org.springframework.security.ldap.userdetails.LdapUserDetailsService,
ldapUserSearch,
ldapAuthoritiesPopulator)
userDetailsService(basecas_04.CustomUserDetailsService)
}
CustomUserDetails.groovy
package basecas_04
import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUser
import org.springframework.security.core.GrantedAuthority
import org.springframework.security.core.userdetails.User
class MyUserDetails extends GrailsUser {
final String mail
MyUserDetails(String username, String password, boolean enabled,
boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked,
Collection<GrantedAuthority> authorities,
long id, String mail) {
super(username, password, enabled, accountNonExpired,
credentialsNonExpired, accountNonLocked, authorities, id)
this.mail = mail
}
}
MyUserDetailsService.groovy# (does this go in services...?...or in src/groovy?)
package basecas_04
import org.codehaus.groovy.grails.plugins.springsecurity.GrailsUser 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.userdetails.UsernameNotFoundException
import basecas_04.User
class CustomUserDetailsService implements GrailsUserDetailsService { 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 MyUserDetails(user.username, user.password, user.enabled, !user.accountExpired,
!user.passwordExpired, !user.accountLocked,
authorities ?: NO_ROLES, user.id, user.mail) } } }
User.groovy (domain class)
class User {
transient springSecurityService
String username
String password
String mail
//String displayName
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() {
UserRole.findAllByUser(this).collect { it.role } as Set
}
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
protected void encodePassword() {
password = springSecurityService.encodePassword(password)
}
}
Error I'm getting
2014-02-18 08:37:48,106 [http-apr-8444-exec-9] ERROR errors.GrailsExceptionResolver - MissingPropertyException occurred when processing request: [GET] /basecas_04/protected/list No such property: id for class: org.springframework.security.ldap.userdetails.LdapUserDetailsImpl Possible solutions: dn. Stacktrace follows: groovy.lang.MissingPropertyException: No such property: id for class: org.springframework.security.ldap.userdetails.LdapUserDetailsImpl Possible solutions: dn at grails.plugins.springsecurity.SpringSecurityService.getCurrentUser(SpringSecurityService.groovy:80) at basecas_04.ProtectedController.list(ProtectedController.groovy:22) at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195) at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63) at org.jasig.cas.client.session.SingleSignOutFilter.doFilter(SingleSignOutFilter.java:65) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:722)
Any ideas on this:
No such property: id for class: org.springframework.security.ldap.userdetails.LdapUserDetailsImpl