2
votes

My grails 2.5.0 app uses Spring Security with the ldap and cas plugins. I trust this CAS server, so I need to allow users with no local (User, Role, UserRole tables) records to authenticate.

If I have a local user, everything works great. If I don't have a local user, then I get the CAS re-direct error followed by "Sorry, we were not able to find a user with that username and password." I read that the GormUserDetailsService supplied with CAS doesn't work for non-local users, so I've written my own MyUserDetailsService that implements GrailsUserDetailsService, and I've registered it in ./conf/spring/resources.groovy:

beans = {
    userDetailsService(edu.uga.reg.MyUserDetailsService)
}

Unfortunately, this doesn't solve the problem: it works with local users, but gives the same re-direct+user not found error if the user isn't in my local database. Here's MyUserDetailService code:

import grails.plugin.springsecurity.SpringSecurityUtils
import grails.plugin.springsecurity.userdetails.GrailsUser
import grails.plugin.springsecurity.userdetails.GrailsUserDetailsService
import grails.transaction.Transactional
import org.springframework.security.core.authority.GrantedAuthorityImpl
import edu.uga.reg.User
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UsernameNotFoundException

class MyUserDetailsService 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) {
      def user = User.findByUsername(username)

      // No local user in my db: create one from this username
      if (!user)
          return new GrailsUser(username, '', true, true, true, NO_ROLES, 999)

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

      return new GrailsUser(user.username, user.password,
         user.enabled, !user.accountExpired, !user.passwordExpired,
         !user.accountLocked, authorities ?: NO_ROLES, user.id)
   }
}

Here are my cas settings in Config.groovy:

grails.plugin.springsecurity.providerNames = ['casAuthenticationProvider']
grails.plugin.springsecurity.useCAS = true
grails.plugin.springsecurity.cas.active = true
grails.plugin.springsecurity.cas.loginUri = '/login'
grails.plugin.springsecurity.cas.serverUrlPrefix = 'https://cas.dev.server/cas'
grails.plugin.springsecurity.cas.serviceUrl = 'https://apps-dev.server:8743/CASIS/j_spring_cas_security_check'
grails.plugin.springsecurity.logout.afterLogoutUrl = 'https://cas.dev.server/cas/logout?url=http://apps-dev.server:8743/CASIS/'

//default cas settings
grails.plugin.springsecurity.cas.sendRenew = false
grails.plugin.springsecurity.cas.key = 'grails-spring-security-cas'
grails.plugin.springsecurity.cas.artifactParameter = 'ticket'
grails.plugin.springsecurity.cas.serviceParameter = 'service'
grails.plugin.springsecurity.cas.filterProcessesUrl = '/j_spring_cas_security_check'
grails.plugin.springsecurity.cas.useSingleSignout = true
grails.plugin.springsecurity.cas.serverUrlEncoding = 'UTF-8'
grails.plugin.springsecurity.cas.proxyCallbackUrl = null
grails.plugin.springsecurity.cas.proxyReceptorUrl = null

Again, CAS works fine if the user is in my local db, but fails otherwise. What am I missing? Thanks.

1
I'm using the standard s2-quickstart created classes: User, Role, and UserRole. I do have some users with local roles. Also using ldap, but for CAS-authenticated, local users, ldap is working perfectly. In my code, if the local-db lookup fails, I create and return a new GrailsUser from the CAS ticket, but this is where I'm getting the error. - Neal

1 Answers

0
votes

The answer to my problem is trivial. It's always a good idea to pass the correct number of arguments to the constructor. Instead of:

return new GrailsUser(username, '', true, true, true, NO_ROLES, 999)

I simply needed:

return new GrailsUser(username, '', true, true, true, true, NO_ROLES, 999)

And my user is setup as the principal!