I have a project in Grails 2.2.3 using Groovy 2.0. I have it set up with Spring Security to use CAS for authentication and LDAP for user roles. When I run the app, everything works as it should: accessing /appcontext/ is allowed by anyone and anything under /appcontext/admin/ is secured by CAS and an admin role from LDAP. I am trying to use the newest versions of Grails and Groovy now. I installed GGTS 3.4.0.RELEASE and am using Grails 2.3.0 and Groovy 2.1. I created a new project, made a simple domain class and controller and added in the security settings.
Here is my output when running the app with GGTS 3.3.0.RELEASE, using Grails 2.2.3 and Groovy 2.0: (note the location of the "Server Running" message)
| Loading Grails 2.2.3
| Configuring classpath.
| Environment set to development.....
| Packaging Grails application.....
| Running Grails application
Configuring Spring Security Core ...
... finished configuring Spring Security Core
Configuring Spring Security CAS ...
... finished configuring Spring Security CAS
Configuring Spring Security LDAP ...
... finished configuring Spring Security LDAP
| Server running. Browse to http://localhost:8080/appcontext
Here is my output when running the app with GGTS 3.4.0.RELEASE, using Grails 2.3.0 and Groovy 2.1 (note the location of the "Server Running" message):
| Loading Grails 2.3.0
| Configuring classpath.
| Environment set to development.....
| Packaging Grails application.....
| Compiling 1 source files.....
| Running Grails application
| Server running. Browse to http://localhost:8080/appcontext
Configuring Spring Security Core ...
... finished configuring Spring Security Core
Configuring Spring Security LDAP ...
... finished configuring Spring Security LDAP
Error initializing the application: No bean named 'casAuthenticationProvider' is defined
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'casAuthenticationProvider' is defined
at SpringSecurityCoreGrailsPlugin$_createBeanList_closure22.doCall(SpringSecurityCoreGrailsPlugin.groovy:686)
at SpringSecurityCoreGrailsPlugin.createBeanList(SpringSecurityCoreGrailsPlugin.groovy:686)
at SpringSecurityCoreGrailsPlugin$_closure4.doCall(SpringSecurityCoreGrailsPlugin.groovy:615)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
at java.util.concurrent.FutureTask.run(FutureTask.java:138)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
| Error 2013-10-15 11:33:02,925 [localhost-startStop-1] ERROR context.GrailsContextLoader - Error initializing the application: No bean named 'casAuthenticationProvider' is defined
Message: No bean named 'casAuthenticationProvider' is defined
Line | Method
->> 686 | doCall in SpringSecurityCoreGrailsPlugin$_createBeanList_closure22
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 615 | doCall in SpringSecurityCoreGrailsPlugin$_closure4
| 303 | innerRun . . . in java.util.concurrent.FutureTask$Sync
| 138 | run in java.util.concurrent.FutureTask
| 886 | runTask . . . in java.util.concurrent.ThreadPoolExecutor$Worker
| 908 | run in ''
^ 662 | run . . . . . in java.lang.Thread
schema export unsuccessful
org.h2.jdbc.JdbcSQLException: Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT=FALSE" to the db URL) [90121-170]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
at org.h2.message.DbException.get(DbException.java:169)
at org.h2.message.DbException.get(DbException.java:146)
at org.h2.message.DbException.get(DbException.java:135)
at org.h2.jdbc.JdbcConnection.checkClosed(JdbcConnection.java:1391)
at org.h2.jdbc.JdbcConnection.checkClosed(JdbcConnection.java:1366)
at org.h2.jdbc.JdbcConnection.getAutoCommit(JdbcConnection.java:424)
at java.lang.Thread.run(Thread.java:662)
| Error 2013-10-15 11:33:03,071 [Thread-9] ERROR hbm2ddl.SchemaExport - schema export unsuccessful
Message: Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT=FALSE" to the db URL) [90121-170]
Line | Method
->> 329 | getJdbcSQLException in org.h2.message.DbException
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 169 | get in ''
| 146 | get . . . . . . . . in ''
| 135 | get in ''
| 1391 | checkClosed . . . . in org.h2.jdbc.JdbcConnection
| 1366 | checkClosed in ''
| 424 | getAutoCommit . . . in ''
^ 662 | run in java.lang.Thread
| Error Forked Grails VM exited with error
Here is my basic security setup:
conf/spring/resources.groovy
import org.apache.commons.lang.StringEscapeUtils
// Place your Spring DSL code here
beans = {
// load ldap roles from spring security
def ldapUrl = StringEscapeUtils.escapeJava('${ldap.defaultUrl}')
def ldapUser = StringEscapeUtils.escapeJava('${ldap.username}')
def ldapPassword = StringEscapeUtils.escapeJava('${ldap.password}')
def ldapBase = StringEscapeUtils.escapeJava('${ldap.base}')
def ldapRoleSearchBase = StringEscapeUtils.escapeJava('${ldap.roleSearchBase}')
initialDirContextFactory(org.springframework.security.ldap.DefaultSpringSecurityContextSource, ldapUrl){
userDn = ldapUser
password = ldapPassword
}
ldapUserSearch(org.springframework.security.ldap.search.FilterBasedLdapUserSearch,
ldapBase, 'sAMAccountName={0}', initialDirContextFactory){ }
ldapAuthoritiesPopulator(org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator,
initialDirContextFactory, ldapRoleSearchBase){
groupRoleAttribute = 'cn'
groupSearchFilter = 'member={0}'
searchSubtree = true
rolePrefix = 'ROLE_'
convertToUpperCase = true
ignorePartialResultException = true
}
userDetailsService(org.springframework.security.ldap.userdetails.LdapUserDetailsService,ldapUserSearch,ldapAuthoritiesPopulator){ }
}
conf/Config.groovy
def appName = grails.util.Metadata.current.getApplicationName()
environments {
development {
grails.logging.jul.usebridge = true
host.ip = "12.34.56.78"
host.port = "8080"
host.securePort = "8080"
ldap.username = "ldapUsername"
ldap.password = "ldapPassword"
ldap.base = "DC=foo,DC=company,DC=com"
ldap.roleSearchBase = "OU=bar,DC=foo,DC=company,DC=com"
ldap.defaultUrl = "ldap://123.45.67.89:389"
ldap.urls = "ldap://123.45.67.89:389 ldap://123.45.67.89:389"
cas.url = "https://sso.company.com/cas/"
cas.loginUrl = "https://sso.company.com/cas/login"
cas.logoutUrl = "https://sso.company.com/cas/logout"
grails.plugins.springsecurity.cas.serviceUrl = 'http://${host.ip}:${host.securePort}/' + appName +'/j_spring_cas_security_check'
grails.plugins.springsecurity.cas.proxyCallbackUrl = 'http://${host.ip}:${host.securePort}/' + appName +'/secure/receptor'
}
production {
grails.logging.jul.usebridge = false
grails.plugins.springsecurity.cas.serviceUrl = 'https://${host.ip}:${host.securePort}/' + appName +'/j_spring_cas_security_check'
grails.plugins.springsecurity.cas.proxyCallbackUrl = 'https://${host.ip}:${host.securePort}/' + appName +'/secure/receptor'
}
}
//spring security core config
grails.plugins.springsecurity.providerNames = ['casAuthenticationProvider']
grails.plugins.springsecurity.rejectIfNoRule = true
grails.plugins.springsecurity.securityConfigType = "InterceptUrlMap"
grails.plugins.springsecurity.interceptUrlMap = [
'/js/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
'/css/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
'/images/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
'/admin/login/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
'/admin/logout/**': ['IS_AUTHENTICATED_ANONYMOUSLY'],
'/admin/**': ['hasAnyRole("ROLE_ADMIN")'],
'/**': ['IS_AUTHENTICATED_ANONYMOUSLY']
]
//cas config
grails.plugins.springsecurity.cas.loginUri = 'login'
grails.plugins.springsecurity.cas.serverUrlPrefix = '${cas.url}'
grails.plugins.springsecurity.cas.proxyReceptorUrl = '/secure/receptor'
conf/BuildConfig.groovy
compile ":spring-security-core:1.2.7.3"
compile ":spring-security-cas:1.0.5"
compile ":spring-security-ldap:1.0.6"
EDIT Using the advice of the accepted answer below, I was able to get Spring Security CAS to configure correctly, but my controllers were still unsecured. I figured that it had to do with that weird load order where the app server says it's running and THEN it loads Spring Security, LDAP, and CAS. A coworker suggested taking out my InterceptUrlMap and using @Secured annotations to see if it was the loading order (since InterceptUrlMap can't be updated after everything is up and running). I got rid of the rejectIfNoRule, securityConfigType, and interceptUrlMap settings and added a @Secured(['ROLE_ADMIN']) to the controller. The app now works as expected and that controller is secured.
So, there is still an issue with the order of events with Grails 2.3.0 and Spring Security, but this is a workaround.
Related question: https://stackguides.com/questions/19411102/grails-2-3-0-spring-security-ldap-and-cas-load-after-server-starts