I'm currently migrating from old deprecated Spring Security SAML Extension 1.0.10 to the SAML implementation in Spring Security 5.6.1.
In the old extension there was the possibility to disable the signature verification of the SAML response (property wantAssertionSigned in Spring Security SAML Extension documentation). This was very helpful for me during testing.
I wonder if this is also possible in Spring Security 5.6.1?
I searched in the source code and found the class OpenSamlMetadataResolver where it seems to me that this is hard-coded and cannot be changed:
private SPSSODescriptor buildSpSsoDescriptor(RelyingPartyRegistration registration) {
SPSSODescriptor spSsoDescriptor = build(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
(...)
spSsoDescriptor.setWantAssertionsSigned(true);
(...)
return spSsoDescriptor;
}
Also the code in OpenSaml4AuthenticationProvider doesn't seem to offer an easy way to configure private variable assertionSignatureValidator to override validation behaviour.
Any help is appreciated.
In Spring Security 5.7.0, which will be released this Monday, May 16, 2022, the hard-coded line is removed. Therefore no more signature verification by default.
You will also be able to customize the EntityDescriptor if you want, something like this:
openSamlMetadataResolver.setEntityDescriptorCustomizer(
(parameters) -> parameters.getEntityDescriptor().setEntityID("overriddenEntityId"));
You can always try the milestone releases before the GA.
Related
I have one service and Keycloak 11 as Authentication server. Now I want to write tests. To mock the accesstoken, I use #WithMockKeycloakAuth. This works well and I get an unauthorized when I pass a bad role for example. Now I want to document it with spring rest docs the therefor I have to add the acesstoken as header field ( Bearer tokenAsBearerString ). Because of the annotation, the mocked token is added to the SecurityContext and I can extract it before doing the mvc.perform.
#Test
#Order(5)
#WithMockKeycloakAuth(authorities = "ROLE_owner")
void createProduct_RealmRoleOwner_HttpStatusCreated() throws Exception {
SecurityContext context = SecurityContextHolder.getContext();
KeycloakAuthenticationToken authentication =(KeycloakAuthenticationToken) context.getAuthentication();
AccessToken token = authentication.getAccount().getKeycloakSecurityContext().getToken();
The problem is that I need the accesstoken as Bearer string representation. I'm not yet very familiar with the jwt topic but I expected that if I use the mocked acces token and convert it to a jwt format / Base 64 encoded String the header should be correct.
In addition: I'm running a Keycloak container via docker in a seperate network so it is not reachable while I run my automated test. So mocking would be the only solution.
This question was also asked (and answered) here with a little more context.
The code snippet provided above doesn't show that test class is decorated with #AutoConfigureMockMvc(addFilters = false), reason why the security context is not attached to the MockMvc HTTP request (this is normally done by a spring-security filter).
The complete stack trace isn't provided neither, but it's very likely that the exception occurring when filters are enabled is due to JwtDecoder wiring from Keycloak boot lib. #MockBean JwtDecoder jwtDecoder; should be enough to fix it.
Finally, it is one of main features of the lib #WithMockKeycloakAuth is taken from to skip fetching, decoding and parsing an actual JWT from Keycloak instance. Trying to build an authorization header with valid JWT from mocked spring authentication is ...
Changing the Order of the Spring Security WebFilter
I have an API Gateway implemented using Spring Cloud Gateway that uses Spring Security. Spring Security for WebFlux is implemented as a WebFilter right at the beginning of the filter chain. So after successful authentication the request would be forwarded to Spring Cloud Gateway's RoutePredicateHandlerMapping, which would try to deduce the destination based on the URL pattern, and then it would go to a FilteringWebHandler to execute the other filters of Spring Cloud Gateway.
My problem is the following: I have implemented a customized authentication algorithm which uses query string and header variables as credentials for authentication according to the requirements of the project, an this is working without any problem. The problem occurred when we needed to add a small customization for the authentication algorithm that is path independent. When the request reaches the WebFilter of Spring Security, pattern matching is not yet done so I do not know which application does it point to, for example:
app1:
-Path: /app1/**
app2:
-Path: /app2/**
Which means that instead of having authentication -> route mapping -> filtering web handler I should do route mapping -> authentication -> filtering web handler. Not that these three components are not similar, one of them is a filter another is a mapper and the last one is web handler. Now I know how to customize them but the problem is that I do not know how to intercept the Netty server building process in order to change the order of these operations. I need to wait for the building process to end and alter the content of the server before it starts. How can I do that?
EDIT: here is the final solution:
So here is how I did it:
Goal: removing the WebFilter of Spring Security from the default HttpHandler, and inserting it between RoutePredicateRouteMapping and the FilteringWebHandler of Spring Cloud Gateway
Why: Because I need to know the Application ID while carrying on my customized authentication process. This Application ID is attached to the request by the RoutePredicateRouteMapping by matching the request's URL to a predefined list.
How did I do it:
1- Removing the WebFilter of Spring Security
I created an HttpHandler bean that invokes the default WebHttpHandlerBuilder and then customize the filters. As a bonus, I removed unneeded filters in order to increase the performance of my API Gateway
#Bean
public HttpHandler httpHandler() {
WebHttpHandlerBuilder webHttpHandlerBuilder = WebHttpHandlerBuilder.applicationContext(this.applicationContext);
MyAuthenticationHandlerAdapter myAuthenticationHandlerAdapter = this.applicationContext.getBean(MY_AUTHENTICATED_HANDLER_BEAN_NAME, MyAuthenticationHandlerAdapter.class);
webHttpHandlerBuilder
.filters(filters ->
myAuthenticationHandlerAdapter.setSecurityFilter(
Collections.singletonList(filters.stream().filter(f -> f instanceof WebFilterChainProxy).map(f -> (WebFilterChainProxy) f).findFirst().orElse(null))
)
);
return webHttpHandlerBuilder.filters(filters -> filters
.removeIf(f -> f instanceof WebFilterChainProxy || f instanceof WeightCalculatorWebFilter || f instanceof OrderedHiddenHttpMethodFilter))
.build();
}
2- Wrapping Spring Cloud Gateway's FilteringWebHandler with Spring Web's FilteringWebHandler with the added WebFilter
I created my own HandlerAdapter which would match against Spring Cloud Gateway's FilteringWebHandler and wrap it with Spring Web's FilteringWebHandler plus the security filter I extracted in the first step
#Bean
public MyAuthenticationHandlerAdapter myAuthenticationHandlerAdapter() {
return new MyAuthenticationHandlerAdapter();
}
public class MyAuthenticationHandlerAdapter implements HandlerAdapter {
#Setter
private List<WebFilter> securityFilter = new ArrayList<>();
#Override
public boolean supports(Object handler) {
return handler instanceof FilteringWebHandler;
}
#Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
org.springframework.web.server.handler.FilteringWebHandler filteringWebHandler = new org.springframework.web.server.handler.FilteringWebHandler((WebHandler) handler, securityFilter);
Mono<Void> mono = filteringWebHandler.handle(exchange);
return mono.then(Mono.empty());
}
}
This way I could achieve better performance with highly customized HttpHandler pipeline that I suppose to be future-proof
END EDIT
Spring Security for WebFlux is implemented as a WebFilter which is executed almost as soon as a request is received. I have implemented custom authentication converter and authentication manager which would extract some variables from the header and URL and use them for authentication. This is working without any problem.
Now I needed to add another variable taken from RoutePredicateRouteMapping before authentication is done. What I want exactly is to remove the WebFilter (called WebFilterChainProxy) from its current position and put it between the RoutePredicateRouteMapping and the FilteringWeHandler.
Here is how the default process goes:
ChannelOperations calls ReactorHttpHandlerAdapter which calls HttpWebHandlerAdapter, ExceptionHandlingWebHandler, and then org.springframework.web.server.handler.FilterWebHandler.
This WebHandler would invoke its filters and then call the DispatchHandler. One of those filters is the WebFilterChainProxy that does the authentication for Spring Security. So first step is removing the filter from here.
Now the DispatchHandler which is called after the filters would invoke RoutePredicateHandlerMapping, which would analyze the routes and give me the route ID that I need, and then it would call the org.springframework.cloud.gateway.handler.FilteringHandler (this is not the same FilteringHandler above), and that in turn would call the other filters of the Spring Cloud Gateway. What I want here is to invoke the filter after RoutePredicatehandlerMapping and before org.springframework.cloud.gateway.handler.FilteringHandler.
What I ended doing was the following:
I created and WebHttpHandlerBuilder that would remove WebFilterChainProxy and pass it as a parameter to a customized DispatcherHandler. Now that the filter is removed the request would pass the first layers without requiring authentication. In my customized DispatcherHandler I would invoke the RoutePredicateHandlerMapping and then pass the exchange variable to the WebFilterChainProxy to do the authentication before passing it to the org.springframework.cloud.gateway.handler.FilteringHandler, which worked perfectly!
I still think that I'm over engineering it and I hope that there is a way to do it using annotations and configuration beans instead of all these customized classes (WebHttpHandlerBuilder and DispatcherHandler).
You should probably implement that security filter as a proper GatewayFilter, since only those are aware of the other GatewayFilter instances and can be ordered accordingly. In your case, you probably want to order it after the routing one.
Also, please don't cross-post, the Spring team is actively monitoring StackOverflow.
I had a similar problem. The accepted solution, while interesting, was a bit drastic for me. I was able to make it work simply by adding my custom filter before SecurityWebFiltersOrder.AUTHENTICATION in the security configuration. This is similar to what I've done with success in a regular Spring mvc application.
Here's an example using oauth authentication. tokenIntrospector is my custom introspector, and requestInitializationFilter is the filter that grabs the tenant id and stashes it in the context.
#AllArgsConstructor
#Configuration
#EnableWebFluxSecurity
public class WebApiGatewaySecurityConfiguration {
private final GatewayTokenIntrospector tokenIntrospector;
private final GatewayRequestInitializationFilter requestInitializationFilter;
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
// #formatter:off
http
.formLogin().disable()
.csrf().disable()
.oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer.opaqueToken(c -> c.introspector(tokenIntrospector)))
.addFilterBefore(requestInitializationFilter, SecurityWebFiltersOrder.AUTHENTICATION);
return http.build();
// #formatter:on
}
}
I'm trying to secure endpoints Actuators inside Spring Boot project. However instead using ready-to-run Spring Security configuration for Actuators:
management:
security:
enabled: true
role: ADMINISTRATOR
That too easy I need to plug Actuators with our custom security (here CAS SSO).
First try it was to add context-path for Actuators:
management:
security:
enabled: true
role: ADMINISTRATOR
context-path: /management
And update my WebSecurityConfigurerAdapter configuration
#Override
protected void configure(HttpSecurity http) throws Exception {
...
http.authorizeRequests()..antMatchers("/management/**").hasRole(Role.ADMINISTRATOR.toString());
...
}
It works but I must hardcode Actuators context-path, so when I want to update management.context-path I have to update my security.
I know it's possible to retrieve value of management.context-path but how to manage it when value equals ""?
You can answer me to #Autowired EndpointHandlerMapping and retrieve list of Actuators endpoints... Finally I will copy-past same logic as ManagementSecurityAutoConfiguration.ManagementWebSecurityConfigurerAdapter.
Furthermore ManagementSecurityAutoConfiguration.ManagementWebSecurityConfigurerAdapter #ConditionalOnMissingBean is pointing itself but ManagementSecurityAutoConfiguration.ManagementWebSecurityConfigurerAdapter is inner-static protected class so not possible to disable it without passing parameter management.security.enabled=false and that can be strange because your configuration said management.security.enabled=false but in reality endpoints are secured...
Conclusion
Is there a way to override (just a part of) properly Actuators security
May I miss something and I'm totally wrong?
There is already a pending Issue on Github. For the moment Dave Syer proposes:
I think copy-paste of all the code in there is actually the best
solution for now (and set management.security.enabled=false to let
Boot know you want to do it yourself).
I have not tested whether a runtime exception will be thrown but I think that you can reuse ManagementWebSecurityConfigurerAdapter and save a lot of copy-paste action. At least compiler doesn't complain.
Put your configuration class under package org.springframework.boot.actuate.autoconfigure in your project and extend from ManagementWebSecurityAutoConfiguration.ManagementWebSecurityConfigurerAdapter. Don't miss all the annotations from ManagementWebSecurityConfigurerAdapter. That is the only copy-paste action here because class annotations can not be inherited by subclass.
package org.springframework.boot.actuate.autoconfigure;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
#Configuration
#ConditionalOnProperty(prefix = "management.security", name = "enabled", matchIfMissing = true)
#Order(ManagementServerProperties.BASIC_AUTH_ORDER)
public class SsoManagementWebSecurityConfigurerAdapter extends ManagementWebSecurityAutoConfiguration.ManagementWebSecurityConfigurerAdapter {
//TODO your SSO configuration
}
Don't forget to #Import your configuration in your #SpringBootApplication.
How can I configure Spring SAML to send "assertionConsumerServiceIndex" instead of "assertionConsumerServiceUrl" in the SAML Authentication Requests?
Another question:
Where can I report an issue of the Spring security SAML project?
Thanks
Got it working but not through config file. I found following comment in the Spring SAML source code:
// AssertionConsumerServiceURL + ProtocolBinding is mutually exclusive with AssertionConsumerServiceIndex, we use first one here
I had to modify buildReturnAddress method of WebSSOProfileImpl class as below to insert AssertionConsumerServiceIndex instead of AssertionConsumerServiceURL + ProtocolBinding when creating new AuthNRequest:
if (service != null) {
request.setAssertionConsumerServiceIndex(service.getIndex());
}
Yes, We need to alter the way buildReturnAddress() behaves in f/w. We can do this simply by extending WebSSOProfileImpl and overriding buildReturnAddress() method in our service provider code.
I'm getting ready to implement the Spring Security UI plugin (we've already implemented the Spring Security Core plugin). I know the core plugin has support for users, roles, and groups, however, I don't see any mention of groups in the Spring Security UI plugin's documentation. Does Spring Security UI plugin not support creating, edition, etc. groups? Anyone tried adding this functionality?
A late response, but I had the same question so I thought I would just try it.
I have just attempted this myself and I believe the the answer is No. (out of the box)
The spring security ui plugin doesn't take the groups into consideration. If you try to edit a user
myapp/user/edit/1
you will recieve some sort of error like:
Class groovy.lang.MissingPropertyException
Message
No such property: authority for class: com.myapp.security.SecGroup Possible solutions: authorities
I'm curious if you found a way around this? Or we will have to customize the the plugin.
As Julian noted the UI doesn't provide support for groups out of the box. To avoid the error you can do the following (customize the plugin):
Copy the User controller into your project to override the plugin's controller:
grails s2ui-override user <your-package-for-controller>
Copy the "buildUserModel" from the plugin code in UserController and edit the userRoleNames field:
import grails.plugin.springsecurity.SpringSecurityUtils
class UserController extends grails.plugin.springsecurity.ui.UserController {
protected Map buildUserModel(user) {
...
// Added so that when using groups doesn't cause an error
Set userRoleNames
if (SpringSecurityUtils.securityConfig.useRoleGroups) {
String groupAuthorityFieldName = SpringSecurityUtils.securityConfig.authority.groupAuthorityNameField
userRoleNames = user[authoritiesPropertyName].collect { it[groupAuthorityFieldName].collect { it[authorityFieldName] } }
} else {
userRoleNames = user[authoritiesPropertyName].collect { it[authorityFieldName] }
}
...
}