0

I'm working with a Grails 2 application and a Spring Security Saml Grails Plugin 2.0.0.

Purely for testing purposes I host the app on a local Tomcat and initially everything was working fine. However after some time, my loadUserBySAML method stopped being called, and so I was unable to login with SSO. There was a period of time where it was inconsistent - sometimes it would use the method, many times not. Now it seems like it will not work at all.

I am getting the SAML token back from the Idp properly, with the correct information and everything. However, it just gives me my default login failure message and doesn't show anything happening with the token nor any error messages.

I had 'solved' this issue before, and it was resolved by following the answer to this, so I added these beans to my resources.groovy file:

      emptyStorageFactory(EmptyStorageFactory)

        contextProvider(SAMLContextProviderImpl) {
            storageFactory = ref('emptyStorageFactory')
        }

Everything started working properly; the method in my custom User Service was being called and users were logged in. Leading me to believe the issue was due to the app being hosted on localhost and solved by using the EmptyStorageFactory. However, after a few weeks the issue came back, loadUserBySAML no longer being called. Even when reverting all changes back to the time it was working, it would no longer work. I would greatly appreciate any knowledge on how to fix this, especially as I am a novice to SAML and SSO. Everything else I've tried had little to no effect and only broke things further before reverting them.

Here is the entirety of my resources file:

customPropertyEditorRegistrar(util.CustomPropertyEditorRegistrar)
        saltSource(util.UserSaltSource)
                {userPropertyToUse = application.config.grails.plugin.springsecurity.dao.reflectionSaltSourceProperty}

        sessionRegistry(SessionRegistryImpl)

        sessionAuthenticationStrategy(ConcurrentSessionControlStrategy, sessionRegistry) {
            maximumSessions = -1
        }

        concurrentSessionFilter(ConcurrentSessionFilter){
            sessionRegistry = sessionRegistry
            expiredUrl = '/login/concurrentSession'
        }


        def conf = SpringSecurityUtils.securityConfig
        if (!conf || !conf.active) { return }

        SpringSecurityUtils.loadSecondaryConfig 'SamlSecurityConfig'
        conf = SpringSecurityUtils.securityConfig
        if (!conf.saml.active) { return }

//        Due to Spring DSL limitations, need to import these beans as XML definitions
        def beansFile = "classpath:security/springSecuritySamlBeans.xml"
        log.debug "Importing beans from ${beansFile}..."
        delegate.importBeans beansFile

        xmlns context:"http://www.springframework.org/schema/context"
        context.'annotation-config'()
        context.'component-scan'('base-package': "org.springframework.security.saml")

        SpringSecurityUtils.registerProvider 'samlAuthenticationProvider'
        SpringSecurityUtils.registerLogoutHandler 'successLogoutHandler'
        SpringSecurityUtils.registerLogoutHandler 'logoutHandler'
        SpringSecurityUtils.registerFilter 'samlEntryPoint', SecurityFilterPosition.SECURITY_CONTEXT_FILTER.order + 1
        SpringSecurityUtils.registerFilter 'metadataFilter', SecurityFilterPosition.SECURITY_CONTEXT_FILTER.order + 2
        SpringSecurityUtils.registerFilter 'samlProcessingFilter', SecurityFilterPosition.SECURITY_CONTEXT_FILTER.order + 3
        SpringSecurityUtils.registerFilter 'samlLogoutFilter', SecurityFilterPosition.SECURITY_CONTEXT_FILTER.order + 4
        SpringSecurityUtils.registerFilter 'samlLogoutProcessingFilter', SecurityFilterPosition.SECURITY_CONTEXT_FILTER.order + 5

        successRedirectHandler(SavedRequestAwareAuthenticationSuccessHandler) {
            alwaysUseDefaultTargetUrl = conf.saml.alwaysUseAfterLoginUrl ?: false
            defaultTargetUrl = conf.saml.afterLoginUrl
        }

        successLogoutHandler(SimpleUrlLogoutSuccessHandler) {
            defaultTargetUrl = conf.saml.afterLogoutUrl
        }

        samlLogger(SAMLDefaultLogger)

        keyManager(JKSKeyManager,
                conf.saml.keyManager.storeFile, conf.saml.keyManager.storePass, conf.saml.keyManager.passwords, conf.saml.keyManager.defaultKey)

        def idpSelectionPath = conf.saml.entryPoint.idpSelectionPath
        samlEntryPoint(SAMLEntryPoint) {
            filterProcessesUrl = conf.saml.loginFormUrl                         // '/saml/login'
//            contextProvider = ref('contextProvider')
            if (idpSelectionPath) {
                idpSelectionPath = idpSelectionPath                     // '/index.gsp'
            }
            defaultProfileOptions = ref('webProfileOptions')
        }

        webProfileOptions(WebSSOProfileOptions) {
            includeScoping = false
        }

        metadataFilter(MetadataDisplayFilter) {
            filterProcessesUrl = conf.saml.metadata.url                         // '/saml/metadata'
        }

        metadataGenerator(MetadataGenerator)

        log.debug "Defining bean metadata providers... "
        def providerBeanName = "extendedMetadataDelegate"

        def idpResource
        def idpFile = conf.saml.metadata.idp.file
        if(idpFile){
            idpResource = new ClassPathResource(idpFile)
            defaultIdpMetadata(ExtendedMetadataDelegate) { extMetaDataDelegateBean ->
                idpMetadataProvider(FilesystemMetadataProvider) { bean ->
                    bean.constructorArgs = [idpResource.getFile()]
                    parserPool = ref('parserPool')
                }
                extMetaDataDelegateBean.constructorArgs = [ref('idpMetadataProvider')]
            }
        }

        def spFile = conf.saml.metadata.sp.file
        if (spFile) {
            def spResource = new ClassPathResource(spFile)
            spMetadata(ExtendedMetadataDelegate) { spMetadataBean ->
                spMetadataProvider(FilesystemMetadataProvider) { spMetadataProviderBean ->
                    spMetadataProviderBean.constructorArgs = [spResource.getFile()]
                    parserPool = ref('parserPool')
                }

                def spDefaults = conf.saml.metadata.sp.defaults
                spMetadataDefaults(ExtendedMetadata) { extMetadata ->
                    local = spDefaults.local
                    alias = spDefaults.alias
                    signingKey = spDefaults.signingKey
                    encryptionKey = spDefaults.encryptionKey
                    tlsKey = spDefaults.tlsKey
                    requireArtifactResolveSigned = spDefaults.requireArtifactResolveSigned
                    requireLogoutRequestSigned = spDefaults.requireLogoutRequestSigned
                    requireLogoutResponseSigned = spDefaults.requireLogoutResponseSigned
                    idpDiscoveryEnabled = spDefaults.idpDiscoveryEnabled
                }

                spMetadataBean.constructorArgs = [ref('spMetadataProvider'), ref('spMetadataDefaults')]
            }
        }

        metadata(CachingMetadataManager,[ref('spMetadata'), ref('defaultIdpMetadata')]){
            hostedSPName = conf.saml.metadata.sp?."alias"
            defaultIDP = conf.saml.metadata.defaultIdp
        }

        userDetailsService(SamlUserService) {
            grailsApplication = ref('grailsApplication')
            authorityClassName = conf.authority.className
            authorityJoinClassName = conf.userLookup.authorityJoinClassName
            authorityNameField = conf.authority.nameField
            samlAutoCreateActive = conf.saml.autoCreate.active
            samlAutoAssignAuthorities = conf.saml.autoCreate.assignAuthorities
            samlAutoCreateKey = conf.saml.autoCreate.key
            samlUserAttributeMappings = conf.saml.userAttributeMappings
            samlUserGroupAttribute = conf.saml.userGroupAttribute
            samlUserGroupToRoleMapping = conf.saml.userGroupToRoleMapping
            userDomainClassName = conf.userLookup.userDomainClassName
        }

        samlAuthenticationProvider(GrailsSAMLAuthenticationProvider) {
            userDetails = ref('userDetailsService')
            hokConsumer = ref('webSSOprofileConsumer')
        }

        emptyStorageFactory(EmptyStorageFactory)

        contextProvider(SAMLContextProviderImpl) {
            storageFactory = ref('emptyStorageFactory')
        }

        samlProcessingFilter(SAMLProcessingFilter) {
            authenticationManager = ref('authenticationManager')
            authenticationSuccessHandler = ref('successRedirectHandler')
            sessionAuthenticationStrategy = ref('sessionFixationProtectionStrategy')
            authenticationFailureHandler = ref('authenticationFailureHandler')
        }

        authenticationFailureHandler(AjaxAwareAuthenticationFailureHandler) {
            redirectStrategy = ref('redirectStrategy')
            defaultFailureUrl = conf.failureHandler.defaultFailureUrl //'/login/authfail?login_error=1'
            useForward = conf.failureHandler.useForward // false
            ajaxAuthenticationFailureUrl = conf.failureHandler.ajaxAuthFailUrl // '/login/authfail?ajax=true'
            exceptionMappings = conf.failureHandler.exceptionMappings // [:]
        }

        redirectStrategy(DefaultRedirectStrategy) {
            contextRelative = conf.redirectStrategy.contextRelative // false
        }

        sessionFixationProtectionStrategy(SessionFixationProtectionStrategy)

        logoutHandler(SecurityContextLogoutHandler) {
            invalidateHttpSession = true
        }

        samlLogoutFilter(SAMLLogoutFilter,
                ref('successLogoutHandler'), ref('logoutHandler'), ref('logoutHandler'))

        samlLogoutProcessingFilter(SAMLLogoutProcessingFilter,
                ref('successLogoutHandler'), ref('logoutHandler'))

        webSSOprofileConsumer(WebSSOProfileConsumerImpl){
            responseSkew = conf.saml.responseSkew
        }

        webSSOprofile(WebSSOProfileImpl)

        ecpprofile(WebSSOProfileECPImpl)

        logoutprofile(SingleLogoutProfileImpl)

        postBinding(HTTPPostBinding, ref('parserPool'), ref('velocityEngine'))

        redirectBinding(HTTPRedirectDeflateBinding, ref('parserPool'))

        artifactBinding(HTTPArtifactBinding,
                ref('parserPool'),
                ref('velocityEngine'),
                ref('artifactResolutionProfile')
        )

        artifactResolutionProfile(ArtifactResolutionProfileImpl, ref('httpClient')) {
            processor = ref('soapProcessor')
        }

        httpClient(HttpClient)

        soapProcessor(SAMLProcessorImpl, ref('soapBinding'))

        soapBinding(HTTPSOAP11Binding, ref('parserPool'))

        paosBinding(HTTPPAOS11Binding, ref('parserPool'))

        bootStrap(SAMLBootstrap)

        velocityEngine(VelocityFactory) { bean ->
            bean.factoryMethod = "getEngine"
        }

        parserPool(BasicParserPool)

        securityTagLib(SamlTagLib) {
            springSecurityService = ref('springSecurityService')
            webExpressionHandler = ref('webExpressionHandler')
            webInvocationPrivilegeEvaluator = ref('webInvocationPrivilegeEvaluator')
        }

        springSecurityService(SamlSecurityService) {
            config = conf
            authenticationTrustResolver = ref('authenticationTrustResolver')
            grailsApplication = ref('grailsApplication')
            passwordEncoder = ref('passwordEncoder')
            objectDefinitionSource = ref('objectDefinitionSource')
            userDetailsService = ref('userDetailsService')
            userCache = ref('userCache')
        }

and a snippet of my SamlSecurityConfig:

security {
    saml {
        userAttributeMappings = [:]
        userGroupToRoleMapping = [:]
        active = true
        afterLoginUrl = '/'
        afterLogoutUrl = '/'
        loginFormUrl = '/saml/login'
        userGroupAttribute = "memberOf"
        responseSkew = 60
        idpSelectionPath = '/'
        autoCreate {
            active =  true
            key = 'username'
            assignAuthorities = true
        }
   ...
Dragon
  • 1
  • 1

0 Answers0