Grails 2.3 RC1 and JMS Plugin

Using the JMS 1.2 plugin with Grails 2.3.0.RC1 was producing a number of odd results. Mostly with missing JMS files it turns out that the new spring version didn’t have the needed spring jms included. Just add the following to BuildConfig.groovy

    dependencies {
      compile 'org.springframework:spring-jms:3.2.4.RELEASE'
      ...
    }

Grails Custom UserDetailsService using a Grails Service

Using the Grails Spring Security Core Plugin I found the need to customize the UserDetailsService and use a Grails service. (Part of the roles logic depended on an external API that we already had a service for.) This was easy to accomplish by subclassing the UserDetailsService class I wanted as a base in my case it was actually the SpringSamlUserDetailsService class because I was using the SAML plugin but normally you would subclass GormUserDetailsService. A great starting example is given in the documentation here.

The difference in my case was the need to use the Grails service, I went with providing the service in the resources.groovy file. Below is the example file of what I used.

My resources.groovy

    import com.example.saml.CustomUserDetailsService
    import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils

    beans = {
       	userDetailsService(CustomUserDetailsService) {
    		 grailsApplication = ref('grailsApplication')
    		 myService = ref('myService')  //Here we give the reference to the service we want available.
    		 authorityClassName = SpringSecurityUtils.securityConfig.authority.className
    		 authorityJoinClassName = SpringSecurityUtils.securityConfig.userLookup.authorityJoinClassName
    		 authorityNameField = SpringSecurityUtils.securityConfig.authority.nameField
    		 samlAutoCreateActive = SpringSecurityUtils.securityConfig.saml.autoCreate.active
    		 samlAutoAssignAuthorities = SpringSecurityUtils.securityConfig.saml.autoCreate.assignAuthorities as Boolean
    		 samlAutoCreateKey = SpringSecurityUtils.securityConfig.saml.autoCreate.key as String
    		 samlUserAttributeMappings = SpringSecurityUtils.securityConfig.saml.userAttributeMappings
    		 samlUserGroupAttribute = SpringSecurityUtils.securityConfig.saml.userGroupAttribute as String
    		 samlUserGroupToRoleMapping = SpringSecurityUtils.securityConfig.saml.userGroupToRoleMapping
    		 userDomainClassName = SpringSecurityUtils.securityConfig.userLookup.userDomainClassName
    		 authoritiesPropertyName = SpringSecurityUtils.securityConfig.userLookup.authoritiesPropertyName
    	 }
    }

Snip from CustomUserDetailsService.groovy

    class CustomUserDetailsService extends SpringSamlUserDetailsService {
    	def myService
    ...
    }

SAML Matching Endpoints with Tomcat

Getting, SAML message intended destination endpoint did not match recipient endpoint, errors mean the server itself dosen’t match the urls being given in the SAML messages.

We are using the Grails Spring Security SAML Plugin on a Tomcat server. In my case this was happening because we were doing SSL offloading on the load balancer. So if you look at the logs there should be an error log with the intended destination and the recipient endpoint.

In my case the first error was only different by http vs https. The fix for that was simply to apply the scheme attribute to that connector in tomcat. At which point everything was matching except that the port was now being added as 80 in my endpoint and that wasn’t in the intended endpoint. The fix for this was just to add the proxyPort to the connector as well.

So to fully support the OpenSAML on tomcat with SSL offloading I configured the connector as seen below. Take note of the scheme and proxyPort being set.

    <Connector port="8080" protocol="HTTP/1.1"
                   enableLookups="false"
                   maxThreads="250"
                   connectionTimeout="20000"
                   scheme="https"  
                   proxyPort="443"/>
    3377265 2013-02-01 11:31:30,997 ERROR [http-8080-9] decoding.BaseSAMLMessageDecoder.checkEndpointURI (BaseSAMLMessageDecoder.java:215) - SAML message intended destination endpoint 'https://example.com/app/saml/SSO/alias/https://example.com' did not match the recipient endpoint 'https://example.com:80/app/saml/SSO/alias/https://example.com'
    Feb 1, 2013 11:31:30 AM org.apache.catalina.core.StandardWrapperValve invoke
    SEVERE: Servlet.service() for servlet default threw exception
    org.opensaml.common.SAMLRuntimeException: Incoming SAML message is invalid
            at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:93)
            at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:199)
            at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
            at org.springframework.security.saml.metadata.MetadataDisplayFilter.doFilter(MetadataDisplayFilter.java:83)
            at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
            at org.springframework.security.saml.SAMLEntryPoint.doFilter(SAMLEntryPoint.java:102)
            at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
            at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:79)
            at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:381)
            at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:168)
            at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
            at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
            at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
            at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
            at org.codehaus.groovy.grails.web.servlet.mvc.GrailsWebRequestFilter.doFilterInternal(GrailsWebRequestFilter.java:69)
            at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
            at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
            at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
            at org.codehaus.groovy.grails.web.filters.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:65)
            at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
            at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
            at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
            at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
            at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
            at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
            at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
            at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
            at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
            at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
            at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
            at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
            at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
            at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
            at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
            at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:864)
            at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:579)
            at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1665)
            at java.lang.Thread.run(Thread.java:662)
    Caused by: org.opensaml.xml.security.SecurityException: SAML message intended destination endpoint did not match recipient endpoint
            at org.opensaml.common.binding.decoding.BaseSAMLMessageDecoder.checkEndpointURI(BaseSAMLMessageDecoder.java:217)
            at org.opensaml.saml2.binding.decoding.BaseSAML2MessageDecoder.decode(BaseSAML2MessageDecoder.java:72)
            at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:105)
            at org.springframework.security.saml.processor.SAMLProcessorImpl.retrieveMessage(SAMLProcessorImpl.java:172)
            at org.springframework.security.saml.SAMLProcessingFilter.attemptAuthentication(SAMLProcessingFilter.java:77)
            ... 37 more
    Servlet.service() for servlet default threw exception
    org.opensaml.common.SAMLRuntimeException: Incoming SAML message is invalid
            at java.lang.Thread.run(Thread.java:662)
            Caused by: org.opensaml.xml.security.SecurityException: SAML message intended destination endpoint did not match recipient endpoint
                    at org.opensaml.common.binding.decoding.BaseSAMLMessageDecoder.checkEndpointURI(BaseSAMLMessageDecoder.java:217)
                    at org.opensaml.saml2.binding.decoding.BaseSAML2MessageDecoder.decode(BaseSAML2MessageDecoder.java:72)
            ... 1 more

Grails Display the Current Spring Security Filter Chain

While working with Grails and the Spring Security plugin, the current spring security filter chain is available in the springSecurityFilterChain bean. It is very easy with that to show what the current chain looks like so you can work through filter chain issues. I used the following code in the Grails Console plugin to get the bean:

def filterChain = ctx.getBean('springSecurityFilterChain')

Also if you want to poke around the other beans available this is a great post to check out: Spring Beans from the Grails Console .

Cassandra Not Available After Starting

I’ve been working with the DataStax Enterprise 2.01 install for a bit now and it was working great until one day I was no longer able to get any queries to work using the cqlsh I was just getting the error that one or more nodes was unavailable. I tried restarting and still nothing would work I got a few errors in the logs (shown below).

I was able to quickly fix the error by removing my data directory and starting fresh as this is just my development environment that works great for me. You can find your data directory in the cassandra.yaml file ($DSE_HOME/resources/cassandra/conf/cassandra.yaml), look for the data_file_directories entry. Mine was set to /var/lib/cassandra/data so I just ran the following and started cassandra fresh and everything is back to working order.

rm -r /var/lib/cassandra/data
    INFO [JOB-TRACKER-INIT] 2012-12-28 10:32:32,515 JobTracker.java (line 2427) problem cleaning system directory: cfs:/tmp/hadoop-jeffbeck/mapred/system
    java.io.IOException: UnavailableException()
    	at com.datastax.bdp.hadoop.cfs.CassandraFileSystemThriftStore.listSubPaths(CassandraFileSystemThriftStore.java:1137)
    	at com.datastax.bdp.hadoop.cfs.CassandraFileSystem.listStatus(CassandraFileSystem.java:192)
    	at org.apache.hadoop.mapred.JobTracker.<init>(JobTracker.java:2392)
    	at org.apache.hadoop.mapred.JobTracker.<init>(JobTracker.java:2195)
    	at org.apache.hadoop.mapred.JobTracker.<init>(JobTracker.java:2189)
    	at org.apache.hadoop.mapred.JobTracker.startTracker(JobTracker.java:303)
    	at org.apache.hadoop.mapred.JobTracker.startTracker(JobTracker.java:294)
    	at org.apache.hadoop.mapred.HadoopTrackerPlugin$1.run(HadoopTrackerPlugin.java:230)
    	at java.lang.Thread.run(Thread.java:680)
    Caused by: UnavailableException()
    	at org.apache.cassandra.service.ReadCallback.assureSufficientLiveNodes(ReadCallback.java:212)
    	at org.apache.cassandra.service.StorageProxy.scan(StorageProxy.java:1083)
    	at org.apache.cassandra.thrift.CassandraServer.get_indexed_slices(CassandraServer.java:746)
    	at com.datastax.bdp.hadoop.cfs.CassandraFileSystemThriftStore.listSubPaths(CassandraFileSystemThriftStore.java:1120)
    	... 8 more