Open tucker-bluesage opened 2 years ago
I am able to get the SAML plugin to work under all releases of Grails 5.1.x except v5.1.8. The latest release throws the following exception on startup:
Configuring Spring Security Core ...
... finished configuring Spring Security Core
Configuring Spring Security SAML ...
Importing beans from classpath:security/springSecuritySamlBeans.xml...
2022-06-02 15:47:51.263 [ @ ] ERROR --- org.springframework.boot.SpringApplication : Application run failed
java.lang.NullPointerException: Cannot get property 'idpSelectionPath' on null object
at org.codehaus.groovy.runtime.NullObject.getProperty(NullObject.java:60)
at org.codehaus.groovy.runtime.InvokerHelper.getProperty(InvokerHelper.java:194)
at org.codehaus.groovy.runtime.callsite.NullCallSite.getProperty(NullCallSite.java:46)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGetProperty(AbstractCallSite.java:329)
at org.grails.plugin.springsecurity.saml.SpringSecuritySamlGrailsPlugin$_doWithSpring_closure1.doCall(SpringSecuritySamlGrailsPlugin.groovy:124)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:107)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:323)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:274)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1035)
at groovy.lang.Closure.call(Closure.java:412)
at groovy.lang.Closure.call(Closure.java:406)
at grails.spring.BeanBuilder.invokeBeanDefiningClosure(BeanBuilder.java:759)
at grails.spring.BeanBuilder.beans(BeanBuilder.java:588)
at grails.spring.BeanBuilder.invokeMethod(BeanBuilder.java:531)
at org.grails.plugins.DefaultGrailsPlugin.doWithRuntimeConfiguration(DefaultGrailsPlugin.java:543)
at org.grails.plugins.AbstractGrailsPluginManager.doRuntimeConfiguration(AbstractGrailsPluginManager.java:166)
at grails.boot.config.GrailsApplicationPostProcessor.postProcessBeanDefinitionRegistry(GrailsApplicationPostProcessor.groovy:171)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:142)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:740)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:415)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
at grails.boot.GrailsApp.run(GrailsApp.groovy:99)
at grails.boot.GrailsApp.run(GrailsApp.groovy:485)
at grails.boot.GrailsApp.run(GrailsApp.groovy:472)
at grails.boot.GrailsApp$run.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:148)
at evc.Application.main(Application.groovy:14)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
at org.springframework.boot.loader.WarLauncher.main(WarLauncher.java:59)
@jnunderwood How did you get it working under 5.1.x? Did you at one point see the issue I had, and if you did how did you work around it?
@tucker-bluesage I didn't have to do anything differently going from Grails v4.x to v5.1.x. I never had any issues at all until v5.1.8
I have used the 4.0.4 version of this plugin on Grails 5.1.6 without any problems which is why I haven't bothered to update the plugin. When you consider that this plugin is a wrapper for the deprecated spring-security-saml extension that Spring offers it didn't make any sense to put in any effort and to instead redo the whole thing with Spring Security 5 which has SAML support via spring-security-saml-service-provider.
There is an experimental 5.0.x branch that contains a release candidate that you can build yourself and upload (./gradlew artifactoryPublish
with the artifactory plugin) to a self hosted artifactory instance in the meantime. You can also test it locally by running ./gradlew publishToMavenLocal
which apparently is supposed to be the replacement for grails install
.
Now that Bintray is gone, the alternative, i.e. uploading to maven central appears to be a chore so I haven't published anything yet.
I moved the repository to https://github.com/grails-spring-security-saml/grails-spring-security-saml because I needed to verify ownership of the reverse domain name for maven central on sonatype.
The latest version of the plugin is available on maven central via
implementation 'io.github.grails-spring-security-saml:spring-security-saml:5.0.0-RC2'
I tried the new repo on a project using Grails 5.1.8, running inside a Linux Docker container via JDK 11, but I got this exception on start up:
groovy.lang.MissingMethodException: No signature of method: java.util.LinkedHashMap.getProperty() is applicable for argument types: (String) values: [cleanhandsdev]
Possible solutions: hasProperty(java.lang.String), getProperties()
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:70)
at org.codehaus.groovy.runtime.callsite.PojoMetaClassSite.call(PojoMetaClassSite.java:46)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:139)
at org.grails.plugin.springsecurity.saml.SpringSecuritySamlGrailsPlugin.registrationFromMetadata(SpringSecuritySamlGrailsPlugin.groovy:378)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:107)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:323)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:362)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:61)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:51)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:171)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:212)
at org.grails.plugin.springsecurity.saml.SpringSecuritySamlGrailsPlugin$_doWithSpring_closure1$_closure7.doCall(SpringSecuritySamlGrailsPlugin.groovy:143)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:107)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:323)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:274)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1035)
at groovy.lang.Closure.call(Closure.java:412)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.callClosureForMapEntry(DefaultGroovyMethods.java:6083)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2436)
at org.codehaus.groovy.runtime.dgm$203.invoke(Unknown Source)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:247)
at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:139)
at org.grails.plugin.springsecurity.saml.SpringSecuritySamlGrailsPlugin$_doWithSpring_closure1.doCall(SpringSecuritySamlGrailsPlugin.groovy:141)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:107)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:323)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:274)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1035)
at groovy.lang.Closure.call(Closure.java:412)
at groovy.lang.Closure.call(Closure.java:406)
at grails.spring.BeanBuilder.invokeBeanDefiningClosure(BeanBuilder.java:759)
at grails.spring.BeanBuilder.beans(BeanBuilder.java:588)
at grails.spring.BeanBuilder.invokeMethod(BeanBuilder.java:531)
at org.grails.plugins.DefaultGrailsPlugin.doWithRuntimeConfiguration(DefaultGrailsPlugin.java:543)
at org.grails.plugins.AbstractGrailsPluginManager.doRuntimeConfiguration(AbstractGrailsPluginManager.java:166)
at grails.boot.config.GrailsApplicationPostProcessor.postProcessBeanDefinitionRegistry(GrailsApplicationPostProcessor.groovy:171)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:142)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:145)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:740)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:415)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
at grails.boot.GrailsApp.run(GrailsApp.groovy:99)
at grails.boot.GrailsApp.run(GrailsApp.groovy:485)
at grails.boot.GrailsApp.run(GrailsApp.groovy:472)
at grails.boot.GrailsApp$run.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:148)
at cleanhands.Application.main(Application.groovy:14)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:49)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:108)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:58)
at org.springframework.boot.loader.WarLauncher.main(WarLauncher.java:59)
@jnunderwood
groovy.lang.MissingMethodException: No signature of method: java.util.LinkedHashMap.getProperty() is applicable for argument types: (String) values: [cleanhandsdev]
The plugin is looking for a password for the key 'cleanhandsdev' in your keystore. I had to abandon the KeyManager and it is now always asking for a password. The plugin doesn't complain much on the happy path but it apparently breaks in unexpected places.
You need
grails:
plugin:
springsecurity:
saml:
keyManager:
passwords:
cleanhandsdev: <password>
and if necessary, you need to add the password to the keystore entry too. Use e.g. Keystore explorer to add the password.
@valentingoebel I had already included a password in the SAML section as well as added a password to the keystore. Here is my SAML config:
grails:
plugin:
springsecurity:
providerNames: [ 'samlAuthenticationProvider', 'anonymousAuthenticationProvider' ]
saml:
active: true
afterLoginUrl: '/'
afterLogoutUrl: '/'
responseSkew: 300
signatureAlgorithm: 'rsa-sha256'
digestAlgorithm: 'sha256'
userGroupAttribute: 'memberOf'
autoCreate:
active: true
key: 'username'
assignAuthorities: false
metadata:
defaultIdp: 'IDP_SERVER.saml2'
url: '/saml/metadata'
providers:
ping: 'security/MYAPP.idp.xml'
sp:
file: 'security/MYAPP.sp.xml'
defaults:
local: true
entityId: 'MYAPP'
alias: 'MYAPP'
securityProfile: 'metaiop'
signingKey: 'MYAPP'
encryptionKey: 'MYAPP'
tlsKey: 'MYAPP'
requireArtifactResolveSigned: false
requireLogoutRequestSigned: false
requireLogoutResponseSigned: false
keyManager:
storeFile: 'classpath:security/MYAPP.jks'
storePass: 'MY_PASSWORD'
passwords:
'MYAPP': 'MY_PASSWORD'
defaultKey: ''
This hasn't changed since moving from Grails 5.1.7 to 5.1.8. Any insight you have would be greatly appreciated. Thanks.
@jnunderwood I have released a new version:
implementation 'io.github.grails-spring-security-saml:spring-security-saml:5.0.0-RC3'
This version contains warning and error messages for common configuration errors.
There is a catch though, metadata.url has must now contain {registrationId} to identify the registrations. You can leave it out, then it is the same as the Spring Security defaults:
https://docs.spring.io/spring-security/site/docs/5.2.1.RELEASE/reference/htmlsingle/#saml2 https://docs.spring.io/spring-security/reference/servlet/saml2/metadata.html
Thank you for the new release. I have upgraded to SAML plugin v5.0.0-RC3, while remaining on Grails v5.1.7, and changed my saml.metadata.url to: /saml/metadata/{registrationId}
. However, when I visit the web app, I am presented with the standard authentication page (from Spring Security?) at this URL: /login/auth
. Based on the logs, it looks like SAML is configured but is not being invoked. I have turned on SAML logging, but there are no SAML-related messages (other than the configuration statement) and there are no exceptions.
There are two options: You can use the taglib of the plugin:
<sec:loginLink class="nav-link"><g:message code="index.login.label"/></sec:loginLink>
<sec:logoutLink class="nav-link"><g:message code="index.logout.label"/></sec:logoutLink>
or you can just go to the path /saml2/authenticate/{registrationId}
For logout you can optionally to check if the current authentication is using SAML:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication instanceof Saml2Authentication) {
url = "/logout/saml2"
}
then you can go to /logout/saml2
Hmmm. I'm going directly to the path you suggested and it still takes me back to the standard /login/auth
authentication page. The logs do not indicate that the SAML plugin is being invoked. I'm using the same config that worked in the previous version of the plugin, except that I updated the saml.metadata.url to include /{registrationId}
.
I can't pinpoint the problem. Try setting /saml2/**
to permitAll` to get access to that path.
/login/auth
is configured by loginFormUrl. You may need this:
grails:
plugin:
springsecurity:
auth:
loginFormUrl: '/saml2/authenticate/<insert_registrationId>'
@valentingoebel I finally got everything to work. Reading the index.md
file in https://github.com/grails-spring-security-saml/grails-spring-security-saml
helped as well as all of your comments here. Thank you so much for you assistance. Much appreciated!
I've been doing an upgrade and found that one of my apps which uses optionally uses this plugin for security breaks with the following error:
The same app will run just fine if I remove this plugin and a small amount of related code. Just taking a glance at it all the depdencies probably need to be updated in build.gradle, and I'm not sure what else that might cause in terms of updates.
Updated with the right stack trace. I've been chasing a couple of different errors in my upgrades.