0

I have several Grails applications running in production and interfacing to a CAS server (CAS 3.3.5) via spring-security and the "Grails CAS plugin". These applications are supported by various versions of Grails from version 1.3.7 to 2.2.4

I am moving one of them to 2.4.4 and I have never ending problems with the authentication. At the end of the exercise I have the notorious: "This webpage has a redirect loop" (in Chrome). At the server side I have the usual (!) error:

ERROR [org.jasig.cas.web.ServiceValidateController] - <TicketException
generating ticket for: [callbackUrl:
https://casclient.mydomain.com:9043/testCas/secure/receptor]>
org.jasig.cas.ticket.InvalidTicketException
    at org.jasig.cas.CentralAuthenticationServiceImpl.delegateTicketGrantingTicket(CentralAuthenticationServiceImpl.java:268)

Here are all the steps I made to reproduce the problem:

  1. grails create-app testCas
  2. grails create-controller showSecure add the action showSecurePage in the controller
  3. grails generate-views
  4. create the page showSecurePage under view/showSecure
  5. Add this to UrlMappings

    class UrlMappings {
        static mappings = {
            "/showSecure" {
                controller = "showSecure"
                action = [ GET: "showSecurePage" ]
            }
            "/"(view:"/index")
            "500"(view:'/error')
        }
    }
    

At this point if I start the application I can access the page "showSecurePage" without problems. Let's move on and install the "CAS plugin".

  1. Add in the "plugins" snippet of BuildConfig.groovy the strings:

    compile ":spring-security-core:2.0-RC4"
    compile ":spring-security-cas:2.0-RC1"
    

and in the "dependencies" snippet:

    mavenRepo 'http://repo.spring.io/milestone'
  1. Execute grails clean and grails compile.

  2. Run: grails s2-quickstart testCas User Role

The Config.groovy is modified and has to be "tuned".

  1. Since we don't use annotations I replace the entries added by s2-quickstart (the staticRules) with:

    grails.plugin.springsecurity.rejectIfNoRule = true
    grails.plugin.springsecurity.securityConfigType = "InterceptUrlMap"
    grails.plugin.springsecurity.interceptUrlMap = [
        '/':                  ['permitAll'],
        '/index':             ['permitAll'],
        '/index.gsp':         ['permitAll'],
        '/**/js/**':          ['permitAll'],
        '/**/css/**':         ['permitAll'],
        '/**/images/**':      ['permitAll'],
        '/**/favicon.ico':    ['permitAll'],
        '/login/**':          ['permitAll'],
        '/logout/**':         ['permitAll'],
        '/secure/receptor':   ['permitAll'],
        '/showSecure/**':     ['isFullyAuthenticated()'],
        '/finance/**':        ['ROLE_FINANCE', 'isFullyAuthenticated()']
    ]
    

Note: I added "permitAll" to "/secure/receptor" as described in 19710841

  1. Finally we add the CAS configuration. As described in the documentation all the listed parameters have to be defined:

    grails.serverURL = "http://casclient.mydomain.com:9080/testCas"
    grails.serverSecureURL = "https://casclient.mydomain.com:9043/testCas"
    grails.plugin.springsecurity.useCAS = true
    grails.plugin.springsecurity.cas.active = true
    grails.plugin.springsecurity.cas.serverUrlPrefix = 'https://casserver.mydomain.com:10443/sso'
    grails.plugin.springsecurity.cas.serverUrlEncoding = 'UTF-8'
    grails.plugin.springsecurity.cas.loginUri = '/login'
    grails.plugin.springsecurity.cas.sendRenew = false
    grails.plugin.springsecurity.cas.serviceUrl = "${grails.serverURL}/secure/security_check"
    grails.plugin.springsecurity.cas.key ='authentication_provider'
    grails.plugin.springsecurity.cas.artifactParameter = 'ticket'
    grails.plugin.springsecurity.cas.serviceParameter = 'service'
    grails.plugin.springsecurity.cas.filterProcessesUrl = '/secure/security_check'
    grails.plugin.springsecurity.cas.proxyCallbackUrl = "${grails.serverSecureURL}/secure/receptor"
    grails.plugin.springsecurity.cas.proxyReceptorUrl = '/secure/receptor'
    grails.plugin.springsecurity.cas.useSingleSignOut = true
    
    grails.plugin.springsecurity.logoutURL = "${grails.plugin.springsecurity.cas.serverUrlPrefix}/logout"
    grails.plugin.springsecurity.logout.afterLogoutUrl = "${grails.plugin.springsecurity.cas.serverUrlPrefix}/logout?url=${grails.serverURL}"
    

Note: if in grails.plugin.springsecurity.cas.proxyCallbackUrl I define the link as "http" and not as "https" I will have a loop with, at the CAS server side, a "bad credentials" message. Adding the secure link this error disappears.

Now accessing the secure page I see the usual CAS login. If I login correctly I get the "redirect loop" error while in the CAS log I see:

-------------------------2015-03-31 13:33:33,736 ERROR
[org.jasig.cas.web.ServiceValidateController] - <TicketException
generating ticket for: [callbackUrl:
https://casclient.mydomain.com:9043/testCas/secure/receptor]>
org.jasig.cas.ticket.InvalidTicketException
    at org.jasig.cas.CentralAuthenticationServiceImpl.delegateTicketGrantingTicket(CentralAuthenticationServiceImpl.java:268)
    at org.jasig.cas.web.ServiceValidateController.handleRequestInternal(ServiceValidateController.java:126)

EDIT: Some more information. Among the verbose output when you enable all sorts of debug, I realized that there were continuos references to ROLE_ANONYMOUS. The suspect is (was) that the problem is not authentication but rather authorization. Indeed this was the problem. If I modify the Config.groovy:

'/showSecure/**':     ['ROLE_USER', 'isFullyAuthenticated()'],

and I implement the service that have (now) defined in resources.groovy:

// Place your Spring DSL code here
beans = {

    userDetailsService(EsoUserDetailsService)
}

The problem disappears. I have another one though. If in EsoUserDetailsService I try to retrieve the roles from the database in this way:

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 EsoUserDetails(user.username, user.password, user.enabled,
            !user.accountExpired, !user.passwordExpired, !user.accountLocked, authorities,
            user.id, user.getUserRealName())
        }

I have the error:

Method on class [eso.phase3.rm.User] was used outside of a Grails application. If running in the context of a test using the mocking API or bootstrap Grails correctly.

Community
  • 1
  • 1
Fabio Sogni
  • 187
  • 1
  • 11
  • check this answer out: [link](http://stackoverflow.com/a/29732743/1997707) When you set proxyCallbackUrl for the cas plugin it will cause many of these errors. Unless you are using proxyValidate on cas you should not send pgtUrl to serviceValidate. – John Mercier Apr 19 '15 at 16:49
  • No I'm not using proxyValidate, I commented out the two lines you mentioned in the other answer (cas.proxyCallbackUrl and cas.proxyReceptorUrl) and actually I don't see errors anymore in the CAS logs. Unfortunately the "redirect loop" is still there. Following the link you mentioned I could read: "the problem was that i used an older dependency of the CAS Server, while i was using the newest CAS Client dependency." I am using the cas plugin "spring-security-cas:2.0-RC1" against a CAS server 3.3.5 (I edited the post). Is this a problem? I couldn not find anything specific in the docs. – Fabio Sogni Apr 23 '15 at 13:38
  • Edited. Apparently it is an authorisazion problem rather than an authentication one. – Fabio Sogni Apr 28 '15 at 15:24

0 Answers0