title: Grails Spring Security extending User attributes
body: <p>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: <a href="https://stackoverflow.com/questions/19710841/grails-and-cas-basic-setup">Grails and CAS Basic Setup</a>). Also, I'm not using the s2-quickstart.</p>
  
  <p>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:</p>
  
  <p>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:</p>
  
  <pre><code>def userDetails = springSecurityService.principal
  then: userDetails ?.getUsername() to get the username
  and: userDetails ?.getAuthorities() to get roles
  </code></pre>
  
  <p>This is all working, except is this the <em>best</em> way to access these properties?
  i.e., are there other properties in the userdetails, and how do I access those? </p>
  
  <p>Perhaps the answer to this depends on hwo I go about this next bit...</p>
  
  <p>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 <a href="http://grails-plugins.github.io/grails-spring-security-core/docs/manual/guide/userDetailsService.html" rel="nofollow noreferrer">http://grails-plugins.github.io/grails-spring-security-core/docs/manual/guide/userDetailsService.html</a> 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?</p>
  
  <p>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.</p>
  
  <p>tia,</p>
  
  <p>Bean</p>
  
  <h1><strong>UPDATE</strong></h1>
  
  <p>Here's my config...see error at the end:</p>
  
  <h1>Config.groovy snip</h1>
  
  <pre><code>    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'],  // &lt;- 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'
  </code></pre>
  
  <h1>resources.groovy</h1>
  
  <pre><code>// 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)
  
  
  }
  </code></pre>
  
  <h1>CustomUserDetails.groovy</h1>
  
  <pre><code>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&lt;GrantedAuthority&gt; authorities,
                   long id, String mail) {
  
      super(username, password, enabled, accountNonExpired,
              credentialsNonExpired, accountNonLocked, authorities, id)      
  
      this.mail = mail
     }
  }
  </code></pre>
  
  <h1>MyUserDetailsService.groovy# (does this go in services...?...or in src/groovy?)</h1>
  
  <pre><code>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 -&gt;   
                                  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)                    }           }   }
  </code></pre>
  
  <h1>User.groovy (domain class)</h1>
  
  <pre><code>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&lt;Role&gt; 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)
      }
  }
  </code></pre>
  
  <p><strong>Error I'm getting</strong></p>
  
  <p>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)</p>
  
  <p>Any ideas on this:</p>
  
  <p>No such property: id for class: org.springframework.security.ldap.userdetails.LdapUserDetailsImpl</p>