Customizing OpenSaml4AuthenticationProvider in Spring Security SAML2 - spring-security

I need to use a legacy UserDetailsService with Spring Security SAML2, so I'm following these instructions from Spring. However, I get an error when I just try to replace the AuthenticationProvider with the supposedly "default" one according to that documentation:
public class WigWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
OpenSaml4AuthenticationProvider authenticationProvider = new OpenSaml4AuthenticationProvider();
// I've tried removing these 2 lines and I get the same error
authenticationProvider.setAssertionValidator(OpenSaml4AuthenticationProvider.createDefaultAssertionValidator());
authenticationProvider.setResponseAuthenticationConverter(OpenSaml4AuthenticationProvider.createDefaultResponseAuthenticationConverter());
httpSecurity.authorizeRequests(authz -> authz.anyRequest().authenticated())
.saml2Login(saml2 -> saml2.authenticationManager(new ProviderManager(authenticationProvider)));
}
}
When I do this, I get the following error when I try to authenticate:
java.lang.NoSuchMethodError: org.opensaml.saml.saml2.assertion.SAML20AssertionValidator.<init>(Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;Lorg/opensaml/saml/saml2/assertion/AssertionValidator;Lorg/opensaml/xmlsec/signature/support/SignatureTrustEngine;Lorg/opensaml/xmlsec/signature/support/SignaturePrevalidator;)V
at org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider$SAML20AssertionValidators$3.<init>(OpenSaml4AuthenticationProvider.java:732)
at org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider$SAML20AssertionValidators.<clinit>(OpenSaml4AuthenticationProvider.java:731)
at org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.lambda$createDefaultAssertionSignatureValidator$8(OpenSaml4AuthenticationProvider.java:572)
at org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.lambda$createAssertionValidator$11(OpenSaml4AuthenticationProvider.java:654)
at org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.process(OpenSaml4AuthenticationProvider.java:495)
at org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider.authenticate(OpenSaml4AuthenticationProvider.java:448)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:182)
at org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter.attemptAuthentication(Saml2WebSsoAuthenticationFilter.java:113)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:222)
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:212)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
But when I use the same code without setting the authenticationManager, then the SAML authentication works fine. (Any page that wants to use my custom UserDetails fails of course, because it's not being populated, but all the SAML authentication steps are working fine.):
public class WigWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests(authz -> authz.anyRequest().authenticated())
.saml2Login();
}
}

It turns out that I was using org.opensaml:opensaml-api 3.4.6, and you need to be using 4.x to use the class OpenSaml4AuthenticationProvider. If you're using 3.x you need to use the deprecated class OpenSamlAuthenticationProvider. I wasn't able to upgrade the opensaml dependency because I'm using Java 8, so this is the code that works for me:
public class WigWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
// This class is deprecated, but you have to use it if you're using OpenSAML < 4.0
OpenSamlAuthenticationProvider authenticationProvider = new OpenSamlAuthenticationProvider();
authenticationProvider.setAssertionValidator(OpenSamlAuthenticationProvider.createDefaultAssertionValidator());
authenticationProvider.setResponseAuthenticationConverter(OpenSamlAuthenticationProvider.createDefaultResponseAuthenticationConverter());
httpSecurity.authorizeRequests(authz -> authz.anyRequest().authenticated())
.saml2Login(saml2 -> saml2.authenticationManager(new ProviderManager(authenticationProvider)));
}
}
I finally found the answer when I discovered that that is what Saml2LoginConfigurer does internally.

Related

Can't inject the guice dependency in the jersey filter

In the process of setup a bridge between guice and jersey, I ran into one problem.
When trying to create a jersey filter, I was unable to inject guice dependencies into it.
I found a duplicate, however there is no solution to the problem there.
Everything is exactly the same.
The only difference is that I don't get a startup error. The filter works, but my dependencies are null.
Interestingly, Filter and HttpFilter work fine. But it doesn't really work for me.
There's another thing that's interesting. In the resource, which I understand is an HK2 dependency, I can inject guice bean.
#ApplicationPath("/test")
private static class TestApplicationConfig extends ResourceConfig
{
public TestApplicationConfig()
{
register(JacksonFeature.class);
register(AuthFilter.class);
register(new ContainerLifecycleListener()
{
public void onStartup(Container container)
{
ServletContainer servletContainer = (ServletContainer) container;
ServiceLocator serviceLocator = container.getApplicationHandler().getServiceLocator();
GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
Injector injector = (Injector) servletContainer
.getServletContext()
.getAttribute(Injector.class.getName());
guiceBridge.bridgeGuiceInjector(injector);
}
public void onReload(Container container)
{
}
public void onShutdown(Container container)
{
}
});
}
}
In ServletModule child.
serve(path).with(ServletContainer.class, ImmutableMap.of(
"javax.ws.rs.Application", TestApplicationConfig.class.getName(),
"jersey.config.server.provider.packages", sb.toString()));
I trying with register(AuthFilter.class) and #Provider
#Singleton
#Provider
public class AuthFilter implements ContainerRequestFilter
{
#Inject
private SomeInjectedService someInjectedService; **// null here**
#Context
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
// some code
}
}
SomeInjectedService I register by guice
bind(SomeInjectedService.class).asEagerSingleton();
Where can I start diagnosing and what can I do?
UPD:
I noticed different behavior when using different annotations.
If I use javax.inject.Inject, I get the following error message.
org.glassfish.hk2.api.MultiException: A MultiException has 3 exceptions. They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=SomeInjectedService,parent=AuthFilter,qualifiers={},position=-1,optional=false,self=false,unqualified=null,1496814489)
2. java.lang.IllegalArgumentException: While attempting to resolve the dependencies of some.package.AuthFilter errors were found
3. java.lang.IllegalStateException: Unable to perform operation: resolve on some.package.AuthFilter
If com.google.inject.Inject, just null. As I understand this method is not correct.
Considering that javax Inject is trying to inject the service but can't find it. Can we conclude that the bridge is not working correctly? But if it's not working correctly, why can I inject this service into my resource?
#Path("/test")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class SomeResource
{
private final SomeInjectedService someInjectedResource;
#Inject // here I use javax annotation and this code working correctry
public SomeResource(SomeInjectedService someInjectedResource)
{
this.someInjectedResource = someInjectedResource;
}
#GET
#Path("/{user}")
public Response returnSomeResponse(#PathParam("user") String user) throws Exception
{
// some code
}
}

AbstractSecurityInterceptor must provide a non-null AccessDecisionManager

Since upgrade of Spring Security to 5.6.2 I have issues running my application as I keep getting:
Caused by: java.lang.IllegalArgumentException: AbstractSecurityInterceptor must provide a non-null AccessDecisionManager
at org.springframework.util.Assert.notNull(Assert.java:201) ~[spring-core-5.3.16.jar:5.3.16]
at org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator.<init>(DefaultWebInvocationPrivilegeEvaluator.java:54) ~[spring-security-web-5.6.2.jar:5.6.2]
at org.springframework.security.config.annotation.web.builders.WebSecurity.getRequestMatcherPrivilegeEvaluatorsEntry(WebSecurity.java:338) ~[spring-security-config-5.6.2.jar:5.6.2]
at org.springframework.security.config.annotation.web.builders.WebSecurity.performBuild(WebSecurity.java:305) ~[spring-security-config-5.6.2.jar:5.6.2]
at org.springframework.security.config.annotation.web.builders.WebSecurity.performBuild(WebSecurity.java:90) ~[spring-security-config-5.6.2.jar:5.6.2]
at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.doBuild(AbstractConfiguredSecurityBuilder.java:305) ~[spring-security-config-5.6.2.jar:5.6.2]
at org.springframework.security.config.annotation.AbstractSecurityBuilder.build(AbstractSecurityBuilder.java:38) ~[spring-security-config-5.6.2.jar:5.6.2]
Until now I did not need the AccessDecisionManager bean and everything worked like a charm like this:
#Configuration
#EnableWebSecurity
open class OpenApiSecurityConfig() : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http.requestMatchers()
.antMatchers("/docs")
.and()
.addFilter(OpenApiFilter(authService))
}
open class OpenApiFilter(private val authService: AuthService) : FilterSecurityInterceptor() {
override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
if (userAuthorized()) {
chain.doFilter(request, response)
} else {
throw AccessDeniedException("Forbidden.")
}
}
}
}
So I guess this is just a some kind of new requirement. I added the configuration as:
#Configuration
#Import(AccessManager::class)
#EnableWebSecurity
open class OpenApiSecurityConfig() : WebSecurityConfigurerAdapter() { … }
…
#Configuration
open class AccessManager : AccessDecisionManager {
override fun decide(authentication: Authentication, `object`: Any?, configAttributes: MutableCollection<ConfigAttribute>?) {}
override fun supports(attribute: ConfigAttribute?): Boolean = false
override fun supports(clazz: Class<*>?): Boolean = false
}
However with no effect.
Is it possible to avoid the need for AccessManager?
What is the correct way of instantiating it?
This issue stems from creating a custom FilterSecurityInterceptor.
This filter is not meant to be replaced in the filter chain.
It would be best to create a different type of custom filter and insert it before the FilterSecurityInterceptor. For example, it could extend OncePerRequestFilter and instead of throwing an AccessDeniedException if the user is unauthorized it could simply return.

Integrated Swagger and previously working code breaks: cannot deserialize from Object value (no delegate- or property-based Creator

I successfully integrated swagger to several spring boot services.
Had to allow the endpoints to bypass authentication by adding in respective #EnableWebSecurity class that extends WebSecurityConfigurerAdapter (this had worked for other services fine) :
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Order(1)
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
...
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.antMatcher("/**")
...
.antMatchers("/actuator/**").permitAll()
.antMatchers("/v2/api-docs", "/configuration/**", "/webjars/**","/swagger*/**") // ADDED THIS for swagger
.permitAll() // ADDED THIS for swagger
.antMatchers("/challenge").permitAll()
.antMatchers("/token").permitAll() // ENDPOINT with complaint now, that was previously ok.
.anyRequest()
.authenticated()
.and()
.cors();
}
...
}
For a specific one, however, once I added the relevant swagger code & dependencies, it seems to have broken and complains on what was working initially.
this is endpoint with the complaint :
#PostMapping("/token")
public ResponseDto token(#Valid #RequestBody TokenRequest request) {
try {
return service.generateJwtFromCode(request.getId(), request.getCode());
}
...
catch (Exception exception) {..
}
}
nested exception is on no constructor found for this class:
#AllArgsConstructor
public class TokenRequest {
#NotEmpty
#JsonProperty
private final String id;
#NotEmpty
#Getter
private final String code;
public UUID getId() {
return UUID.fromString(id);
}
}
Could not resolve parameter [0] in responseDTO Controller.token(Service.TokenRequest): Type definition error: [simple type, class TokenRequest]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `service.TokenRequest` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (PushbackInputStream); line: 1, column: 2]
o.s.web.servlet.DispatcherServlet : Failed to complete request: org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class service.TokenRequest]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `service.TokenRequest` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (PushbackInputStream); line: 1, column: 2]
o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in …
Not sure what it has to do with swagger integration. If i remove swagger integration code, it works fine with the same code, and doesn't complain about the type-conversion failure.
For resolving this, I also took someone's suggestion of
upgrading dependency for com.fasterxml.jackson.core
and rebuilding the code. But still no success.
compileOnly 'com.fasterxml.jackson.core:jackson-databind:2.11.2'
Things I have tried but didn't resolve:
Added a default/empty constructor
(for most others with similar problem it worked by that, for me it complaint thereafter on
error: variable id might not have been initialized
}
Added this to the tokenRequest class:
#Value
#AllArgsConstructor(onConstructor = #__(#JsonCreator(mode = JsonCreator.Mode.PROPERTIES))
Had a different error:
c.j.MappingJackson2HttpMessageConverter : Failed to evaluate Jackson deserialization for type [[simple type, class TokenRequest]]
InvalidDefinitionException: Invalid type definition for type `TokenRequest`: More than one argument (#0 and #1) left as delegating for Creator [constructor for TokenRequest, annotations: {interface com.fasterxml.jackson.annotation.JsonCreator=#com.fasterxml.jackson.annotation.JsonCreator(mode=DELEGATING)}]: only one allowed
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:62)...
Solution was to add a default constructor AND also remove final variables.
#AllArgsConstructor
public class TokenRequest {
#NotEmpty
#JsonProperty
private String id; // code fixed issue
#NotEmpty
#Getter
private String code; // code fixed issue
public TokenRequest(){} // code fixed issue
public UUID getId() {
return UUID.fromString(id);
}
}

Circular reference trying to configure custom permission evaluator in spring security

I'm trying to configure a custom permission evaluator but whenever the app starts up it's complaining of a circular reference.
The configuration code is:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
private static Logger logger = LogManager.getLogger(MethodSecurityConfig.class.getName());
#Autowired
DataSource dataSource;
#Autowired
Environment env;
#Autowired
AuthenticationManager auth;
public MethodSecurityConfig() {
logger.debug("Loading method security config.");
}
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return auth;
}
/**
* Override to set up the custom expression handler.
* #return The custom expression handler
*/
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return expressionHandler();
}
/**
* Defines a custom permission evaluator to evaluate the access permissions for Spring security.
* #return The default expression handler configured with a custom permission evaluator.
*/
#Bean
public DefaultMethodSecurityExpressionHandler expressionHandler(){
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
handler.setPermissionEvaluator(permissionEvaluator());
return handler;
}
/**
* Custom implementation of a permission evaluator
* #return An instance of {#link BasePermissionEvaluator}
*/
#Bean
public PermissionEvaluator permissionEvaluator() {
return new BasePermissionEvaluator();
}
}
When the app start up this exception occurs:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'methodSecurityConfig': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration.setPermissionEvaluator(java.util.List); nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'permissionEvaluator': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) ~[AutowiredAnnotationBeanPostProcessor.class:4.2.1.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) ~[AbstractAutowireCapableBeanFactory.class:4.2.1.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543) ~[AbstractAutowireCapableBeanFactory.class:4.2.1.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[AbstractAutowireCapableBeanFactory.class:4.2.1.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:305) ~[AbstractBeanFactory$1.class:4.2.1.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[DefaultSingletonBeanRegistry.class:4.2.1.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:301) ~[AbstractBeanFactory.class:4.2.1.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:196) ~[AbstractBeanFactory.class:4.2.1.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:368) ~[ConstructorResolver.class:4.2.1.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(A
What am I doing wrong here? I tried various things like adding the AuthenticationManager. Is there something else I need to override?
I had faced the same issue. In my case the reason was there was another security configuration class which has #EnableGlobalMethodSecurity annotation. Removing that annotation solved the issue.
The reason to have two security configuration classes is described here.
To my understanding this happens when Spring first processes the other configuration class, because of #EnableGlobalMethodSecurity permissionEvaluator and similar beans are registered that time and later when this configuration class is processed we got this error.
Also looks like this behaviour is arbitrary, depends on which configuration class is first processed based on classpath ordering.

problem with overriding autologin in spring security?

greetings everybody
iam using spring security 3 remember me service as follows
<http>
<remember-me/>
....</http>
and i want to perform some logic in the autologin
so i tried to override the AbstractRememberMeServices as follows:
package com.foo;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.RememberMeServices;
public abstract class AbstractRememberMeServices implements RememberMeServices{
#Override
public Authentication autoLogin(HttpServletRequest arg0,
HttpServletResponse arg1) {
System.out.println("Auto Login");
return null;
}
#Override
public void loginSuccess(HttpServletRequest arg0, HttpServletResponse arg1,
Authentication arg2) {
System.out.println("Login Success");
}
}
but the autologin occurs with no action,the user auto login but the print statement is not printed?
what's wrong?
The fact that you have named your class AbstractRememberMeServices does not mean that every other class which was previously extending now extends your com.foo.AbstractRememberMeServices. I don't mean to be impolite, but you need to review your knowledge of Java basics.
Concerning you question, you need to write a custom org.springframework.security.web.authentication.RememberMeService implementation, configure it in Spring and register it using the services-ref attribute:
<security:remember-me services-ref="myRememberMeServices"/>

Resources