mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-06-01 09:42:13 +00:00
Add AuthorizationManager
Closes gh-8900
This commit is contained in:
parent
5306d4c4d5
commit
34b4b1054f
@ -25,6 +25,7 @@ import javax.servlet.Filter;
|
|||||||
|
|
||||||
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
import org.springframework.security.web.access.ExceptionTranslationFilter;
|
||||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
||||||
|
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
@ -111,6 +112,7 @@ final class FilterComparator implements Comparator<Filter>, Serializable {
|
|||||||
put(SessionManagementFilter.class, order.next());
|
put(SessionManagementFilter.class, order.next());
|
||||||
put(ExceptionTranslationFilter.class, order.next());
|
put(ExceptionTranslationFilter.class, order.next());
|
||||||
put(FilterSecurityInterceptor.class, order.next());
|
put(FilterSecurityInterceptor.class, order.next());
|
||||||
|
put(AuthorizationFilter.class, order.next());
|
||||||
put(SwitchUserFilter.class, order.next());
|
put(SwitchUserFilter.class, order.next());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2019 the original author or authors.
|
* Copyright 2002-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -40,6 +40,8 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
|||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
|
||||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||||
import org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
|
||||||
|
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry;
|
||||||
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configurers.CorsConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.CorsConfigurer;
|
||||||
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
|
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
|
||||||
@ -1254,6 +1256,90 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
|||||||
return HttpSecurity.this;
|
return HttpSecurity.this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows restricting access based upon the {@link HttpServletRequest} using
|
||||||
|
* {@link RequestMatcher} implementations (i.e. via URL patterns).
|
||||||
|
*
|
||||||
|
* <h2>Example Configurations</h2>
|
||||||
|
*
|
||||||
|
* The most basic example is to configure all URLs to require the role "ROLE_USER".
|
||||||
|
* The configuration below requires authentication to every URL and will grant access
|
||||||
|
* to both the user "admin" and "user".
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @Configuration
|
||||||
|
* @EnableWebSecurity
|
||||||
|
* public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .authorizeHttpRequests((authorizeHttpRequests) ->
|
||||||
|
* authorizeHttpRequests
|
||||||
|
* .antMatchers("/**").hasRole("USER")
|
||||||
|
* )
|
||||||
|
* .formLogin(withDefaults());
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* We can also configure multiple URLs. The configuration below requires
|
||||||
|
* authentication to every URL and will grant access to URLs starting with /admin/ to
|
||||||
|
* only the "admin" user. All other URLs either user can access.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @Configuration
|
||||||
|
* @EnableWebSecurity
|
||||||
|
* public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .authorizeHttpRequests((authorizeHttpRequests) ->
|
||||||
|
* authorizeHttpRequests
|
||||||
|
* .antMatchers("/admin/**").hasRole("ADMIN")
|
||||||
|
* .antMatchers("/**").hasRole("USER")
|
||||||
|
* )
|
||||||
|
* .formLogin(withDefaults());
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Note that the matchers are considered in order. Therefore, the following is invalid
|
||||||
|
* because the first matcher matches every request and will never get to the second
|
||||||
|
* mapping:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* @Configuration
|
||||||
|
* @EnableWebSecurity
|
||||||
|
* public class AuthorizeUrlsSecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
*
|
||||||
|
* @Override
|
||||||
|
* protected void configure(HttpSecurity http) throws Exception {
|
||||||
|
* http
|
||||||
|
* .authorizeHttpRequests((authorizeHttpRequests) ->
|
||||||
|
* authorizeHttpRequests
|
||||||
|
* .antMatchers("/**").hasRole("USER")
|
||||||
|
* .antMatchers("/admin/**").hasRole("ADMIN")
|
||||||
|
* );
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* @param authorizeHttpRequestsCustomizer the {@link Customizer} to provide more
|
||||||
|
* options for the {@link AuthorizationManagerRequestMatcherRegistry}
|
||||||
|
* @return the {@link HttpSecurity} for further customizations
|
||||||
|
* @throws Exception
|
||||||
|
* @see #requestMatcher(RequestMatcher)
|
||||||
|
*/
|
||||||
|
public HttpSecurity authorizeHttpRequests(
|
||||||
|
Customizer<AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry> authorizeHttpRequestsCustomizer)
|
||||||
|
throws Exception {
|
||||||
|
ApplicationContext context = getContext();
|
||||||
|
authorizeHttpRequestsCustomizer
|
||||||
|
.customize(getOrApply(new AuthorizeHttpRequestsConfigurer<>(context)).getRegistry());
|
||||||
|
return HttpSecurity.this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows configuring the Request Cache. For example, a protected page (/protected)
|
* Allows configuring the Request Cache. For example, a protected page (/protected)
|
||||||
* may be requested prior to authentication. The application will redirect the user to
|
* may be requested prior to authentication. The application will redirect the user to
|
||||||
|
@ -0,0 +1,289 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.config.annotation.web.configurers;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.AuthorityAuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||||
|
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||||
|
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||||
|
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||||
|
import org.springframework.security.web.access.intercept.DelegatingAuthorizationManager;
|
||||||
|
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||||
|
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a URL based authorization using {@link AuthorizationManager}.
|
||||||
|
*
|
||||||
|
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured.
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>
|
||||||
|
extends AbstractHttpConfigurer<AuthorizeHttpRequestsConfigurer<H>, H> {
|
||||||
|
|
||||||
|
private final AuthorizationManagerRequestMatcherRegistry registry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance.
|
||||||
|
* @param context the {@link ApplicationContext} to use
|
||||||
|
*/
|
||||||
|
public AuthorizeHttpRequestsConfigurer(ApplicationContext context) {
|
||||||
|
this.registry = new AuthorizationManagerRequestMatcherRegistry(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link AuthorizationManagerRequestMatcherRegistry} is what users will interact
|
||||||
|
* with after applying the {@link AuthorizeHttpRequestsConfigurer}.
|
||||||
|
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||||
|
* customizations
|
||||||
|
*/
|
||||||
|
public AuthorizationManagerRequestMatcherRegistry getRegistry() {
|
||||||
|
return this.registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(H http) {
|
||||||
|
AuthorizationManager<HttpServletRequest> authorizationManager = this.registry.createAuthorizationManager();
|
||||||
|
AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);
|
||||||
|
http.addFilter(postProcess(authorizationFilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuthorizationManagerRequestMatcherRegistry addMapping(List<? extends RequestMatcher> matchers,
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> manager) {
|
||||||
|
for (RequestMatcher matcher : matchers) {
|
||||||
|
this.registry.addMapping(matcher, manager);
|
||||||
|
}
|
||||||
|
return this.registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registry for mapping a {@link RequestMatcher} to an {@link AuthorizationManager}.
|
||||||
|
*
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
public final class AuthorizationManagerRequestMatcherRegistry
|
||||||
|
extends AbstractRequestMatcherRegistry<AuthorizedUrl> {
|
||||||
|
|
||||||
|
private final DelegatingAuthorizationManager.Builder managerBuilder = DelegatingAuthorizationManager.builder();
|
||||||
|
|
||||||
|
private List<RequestMatcher> unmappedMatchers;
|
||||||
|
|
||||||
|
private int mappingCount;
|
||||||
|
|
||||||
|
private AuthorizationManagerRequestMatcherRegistry(ApplicationContext context) {
|
||||||
|
setApplicationContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addMapping(RequestMatcher matcher, AuthorizationManager<RequestAuthorizationContext> manager) {
|
||||||
|
this.unmappedMatchers = null;
|
||||||
|
this.managerBuilder.add(matcher, manager);
|
||||||
|
this.mappingCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuthorizationManager<HttpServletRequest> createAuthorizationManager() {
|
||||||
|
Assert.state(this.unmappedMatchers == null,
|
||||||
|
() -> "An incomplete mapping was found for " + this.unmappedMatchers
|
||||||
|
+ ". Try completing it with something like requestUrls().<something>.hasRole('USER')");
|
||||||
|
Assert.state(this.mappingCount > 0,
|
||||||
|
"At least one mapping is required (for example, authorizeHttpRequests().anyRequest().authenticated())");
|
||||||
|
return postProcess(this.managerBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MvcMatchersAuthorizedUrl mvcMatchers(String... mvcPatterns) {
|
||||||
|
return mvcMatchers(null, mvcPatterns);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MvcMatchersAuthorizedUrl mvcMatchers(HttpMethod method, String... mvcPatterns) {
|
||||||
|
return new MvcMatchersAuthorizedUrl(createMvcMatchers(method, mvcPatterns));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AuthorizedUrl chainRequestMatchers(List<RequestMatcher> requestMatchers) {
|
||||||
|
this.unmappedMatchers = requestMatchers;
|
||||||
|
return new AuthorizedUrl(requestMatchers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an {@link ObjectPostProcessor} for this class.
|
||||||
|
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
|
||||||
|
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||||
|
* customizations
|
||||||
|
*/
|
||||||
|
public AuthorizationManagerRequestMatcherRegistry withObjectPostProcessor(
|
||||||
|
ObjectPostProcessor<?> objectPostProcessor) {
|
||||||
|
addObjectPostProcessor(objectPostProcessor);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the {@link HttpSecurityBuilder} when done using the
|
||||||
|
* {@link AuthorizeHttpRequestsConfigurer}. This is useful for method chaining.
|
||||||
|
* @return the {@link HttpSecurityBuilder} for further customizations
|
||||||
|
*/
|
||||||
|
public H and() {
|
||||||
|
return AuthorizeHttpRequestsConfigurer.this.and();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link AuthorizeHttpRequestsConfigurer.AuthorizedUrl} that allows optionally
|
||||||
|
* configuring the {@link MvcRequestMatcher#setServletPath(String)}.
|
||||||
|
*
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
public final class MvcMatchersAuthorizedUrl extends AuthorizedUrl {
|
||||||
|
|
||||||
|
private MvcMatchersAuthorizedUrl(List<MvcRequestMatcher> matchers) {
|
||||||
|
super(matchers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures <code>servletPath</code> to {@link MvcRequestMatcher}s.
|
||||||
|
* @param servletPath the servlet path
|
||||||
|
* @return the {@link MvcMatchersAuthorizedUrl} for further customizations
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public MvcMatchersAuthorizedUrl servletPath(String servletPath) {
|
||||||
|
for (MvcRequestMatcher matcher : (List<MvcRequestMatcher>) getMatchers()) {
|
||||||
|
matcher.setServletPath(servletPath);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object that allows configuring the {@link AuthorizationManager} for
|
||||||
|
* {@link RequestMatcher}s.
|
||||||
|
*
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
public class AuthorizedUrl {
|
||||||
|
|
||||||
|
private final List<? extends RequestMatcher> matchers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance.
|
||||||
|
* @param matchers the {@link RequestMatcher} instances to map
|
||||||
|
*/
|
||||||
|
AuthorizedUrl(List<? extends RequestMatcher> matchers) {
|
||||||
|
this.matchers = matchers;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<? extends RequestMatcher> getMatchers() {
|
||||||
|
return this.matchers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify that URLs are allowed by anyone.
|
||||||
|
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||||
|
* customizations
|
||||||
|
*/
|
||||||
|
public AuthorizationManagerRequestMatcherRegistry permitAll() {
|
||||||
|
return access((a, o) -> new AuthorizationDecision(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify that URLs are not allowed by anyone.
|
||||||
|
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||||
|
* customizations
|
||||||
|
*/
|
||||||
|
public AuthorizationManagerRequestMatcherRegistry denyAll() {
|
||||||
|
return access((a, o) -> new AuthorizationDecision(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies a user requires a role.
|
||||||
|
* @param role the role that should be required which is prepended with ROLE_
|
||||||
|
* automatically (i.e. USER, ADMIN, etc). It should not start with ROLE_
|
||||||
|
* @return {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||||
|
* customizations
|
||||||
|
*/
|
||||||
|
public AuthorizationManagerRequestMatcherRegistry hasRole(String role) {
|
||||||
|
return access(AuthorityAuthorizationManager.hasRole(role));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies that a user requires one of many roles.
|
||||||
|
* @param roles the roles that the user should have at least one of (i.e. ADMIN,
|
||||||
|
* USER, etc). Each role should not start with ROLE_ since it is automatically
|
||||||
|
* prepended already
|
||||||
|
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||||
|
* customizations
|
||||||
|
*/
|
||||||
|
public AuthorizationManagerRequestMatcherRegistry hasAnyRole(String... roles) {
|
||||||
|
return access(AuthorityAuthorizationManager.hasAnyRole(roles));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies a user requires an authority.
|
||||||
|
* @param authority the authority that should be required
|
||||||
|
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||||
|
* customizations
|
||||||
|
*/
|
||||||
|
public AuthorizationManagerRequestMatcherRegistry hasAuthority(String authority) {
|
||||||
|
return access(AuthorityAuthorizationManager.hasAuthority(authority));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies that a user requires one of many authorities.
|
||||||
|
* @param authorities the authorities that the user should have at least one of
|
||||||
|
* (i.e. ROLE_USER, ROLE_ADMIN, etc)
|
||||||
|
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||||
|
* customizations
|
||||||
|
*/
|
||||||
|
public AuthorizationManagerRequestMatcherRegistry hasAnyAuthority(String... authorities) {
|
||||||
|
return access(AuthorityAuthorizationManager.hasAnyAuthority(authorities));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify that URLs are allowed by any authenticated user.
|
||||||
|
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||||
|
* customizations
|
||||||
|
*/
|
||||||
|
public AuthorizationManagerRequestMatcherRegistry authenticated() {
|
||||||
|
return access(AuthenticatedAuthorizationManager.authenticated());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows specifying a custom {@link AuthorizationManager}.
|
||||||
|
* @param manager the {@link AuthorizationManager} to use
|
||||||
|
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||||
|
* customizations
|
||||||
|
*/
|
||||||
|
public AuthorizationManagerRequestMatcherRegistry access(
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> manager) {
|
||||||
|
Assert.notNull(manager, "manager cannot be null");
|
||||||
|
return AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,628 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.config.annotation.web.configurers;
|
||||||
|
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||||
|
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||||
|
import org.springframework.security.config.test.SpringTestRule;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||||
|
import org.springframework.security.web.access.intercept.DelegatingAuthorizationManager;
|
||||||
|
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||||
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.mockito.Mockito.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.springframework.security.config.Customizer.withDefaults;
|
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
|
||||||
|
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||||
|
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
|
||||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link AuthorizeHttpRequestsConfigurer}.
|
||||||
|
*
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
public class AuthorizeHttpRequestsConfigurerTests {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final SpringTestRule spring = new SpringTestRule();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
MockMvc mvc;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenAuthorizedHttpRequestsAndNoRequestsThenException() {
|
||||||
|
assertThatExceptionOfType(BeanCreationException.class)
|
||||||
|
.isThrownBy(() -> this.spring.register(NoRequestsConfig.class).autowire()).withMessageContaining(
|
||||||
|
"At least one mapping is required (for example, authorizeHttpRequests().anyRequest().authenticated())");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenAnyRequestIncompleteMappingThenException() {
|
||||||
|
assertThatExceptionOfType(BeanCreationException.class)
|
||||||
|
.isThrownBy(() -> this.spring.register(IncompleteMappingConfig.class).autowire())
|
||||||
|
.withMessageContaining("An incomplete mapping was found for ");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenMvcMatcherAfterAnyRequestThenException() {
|
||||||
|
assertThatExceptionOfType(BeanCreationException.class)
|
||||||
|
.isThrownBy(() -> this.spring.register(AfterAnyRequestConfig.class).autowire())
|
||||||
|
.withMessageContaining("Can't configure mvcMatchers after anyRequest");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureMvcMatcherAccessAuthorizationManagerWhenNotNullThenVerifyUse() throws Exception {
|
||||||
|
CustomAuthorizationManagerConfig.authorizationManager = mock(AuthorizationManager.class);
|
||||||
|
this.spring.register(CustomAuthorizationManagerConfig.class, BasicController.class).autowire();
|
||||||
|
this.mvc.perform(get("/")).andExpect(status().isOk());
|
||||||
|
verify(CustomAuthorizationManagerConfig.authorizationManager).check(any(), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureMvcMatcherAccessAuthorizationManagerWhenNullThenException() {
|
||||||
|
CustomAuthorizationManagerConfig.authorizationManager = null;
|
||||||
|
assertThatExceptionOfType(BeanCreationException.class)
|
||||||
|
.isThrownBy(() -> this.spring.register(CustomAuthorizationManagerConfig.class).autowire())
|
||||||
|
.withMessageContaining("manager cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenObjectPostProcessorRegisteredThenInvokedOnAuthorizationManagerAndAuthorizationFilter() {
|
||||||
|
this.spring.register(ObjectPostProcessorConfig.class).autowire();
|
||||||
|
verify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(DelegatingAuthorizationManager.class));
|
||||||
|
verify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(AuthorizationFilter.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenHasAnyAuthorityRoleUserConfiguredAndAuthorityIsRoleUserThenRespondsWithOk() throws Exception {
|
||||||
|
this.spring.register(RoleUserAnyAuthorityConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||||
|
.with(user("user")
|
||||||
|
.authorities(new SimpleGrantedAuthority("ROLE_USER")));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenHasAnyAuthorityRoleUserConfiguredAndAuthorityIsRoleAdminThenRespondsWithForbidden()
|
||||||
|
throws Exception {
|
||||||
|
this.spring.register(RoleUserAnyAuthorityConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithAdmin = get("/")
|
||||||
|
.with(user("user")
|
||||||
|
.authorities(new SimpleGrantedAuthority("ROLE_ADMIN")));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenHasAnyAuthorityRoleUserConfiguredAndNoAuthorityThenRespondsWithUnauthorized() throws Exception {
|
||||||
|
this.spring.register(RoleUserAnyAuthorityConfig.class, BasicController.class).autowire();
|
||||||
|
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenHasAuthorityRoleUserConfiguredAndAuthorityIsRoleUserThenRespondsWithOk() throws Exception {
|
||||||
|
this.spring.register(RoleUserAuthorityConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||||
|
.with(user("user")
|
||||||
|
.authorities(new SimpleGrantedAuthority("ROLE_USER")));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenHasAuthorityRoleUserConfiguredAndAuthorityIsRoleAdminThenRespondsWithForbidden()
|
||||||
|
throws Exception {
|
||||||
|
this.spring.register(RoleUserAuthorityConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithAdmin = get("/")
|
||||||
|
.with(user("user")
|
||||||
|
.authorities(new SimpleGrantedAuthority("ROLE_ADMIN")));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenHasAuthorityRoleUserConfiguredAndNoAuthorityThenRespondsWithUnauthorized() throws Exception {
|
||||||
|
this.spring.register(RoleUserAuthorityConfig.class, BasicController.class).autowire();
|
||||||
|
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenAuthorityRoleUserOrAdminRequiredAndAuthorityIsRoleUserThenRespondsWithOk() throws Exception {
|
||||||
|
this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||||
|
.with(user("user")
|
||||||
|
.authorities(new SimpleGrantedAuthority("ROLE_USER")));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenAuthorityRoleUserOrAdminRequiredAndAuthorityIsRoleAdminThenRespondsWithOk() throws Exception {
|
||||||
|
this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithAdmin = get("/")
|
||||||
|
.with(user("user")
|
||||||
|
.authorities(new SimpleGrantedAuthority("ROLE_ADMIN")));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithAdmin).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenAuthorityRoleUserOrAdminRequiredAndAuthorityIsRoleOtherThenRespondsWithForbidden()
|
||||||
|
throws Exception {
|
||||||
|
this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithOther = get("/")
|
||||||
|
.with(user("user")
|
||||||
|
.authorities(new SimpleGrantedAuthority("ROLE_OTHER")));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithOther).andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenAuthorityRoleUserOrAdminAuthRequiredAndNoUserThenRespondsWithUnauthorized() throws Exception {
|
||||||
|
this.spring.register(RoleUserOrRoleAdminAuthorityConfig.class, BasicController.class).autowire();
|
||||||
|
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenHasRoleUserConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
|
||||||
|
this.spring.register(RoleUserConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||||
|
.with(user("user")
|
||||||
|
.roles("USER"));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenHasRoleUserConfiguredAndRoleIsAdminThenRespondsWithForbidden() throws Exception {
|
||||||
|
this.spring.register(RoleUserConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithAdmin = get("/")
|
||||||
|
.with(user("user")
|
||||||
|
.roles("ADMIN"));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenRoleUserOrAdminConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
|
||||||
|
this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||||
|
.with(user("user")
|
||||||
|
.roles("USER"));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenRoleUserOrAdminConfiguredAndRoleIsAdminThenRespondsWithOk() throws Exception {
|
||||||
|
this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithAdmin = get("/")
|
||||||
|
.with(user("user")
|
||||||
|
.roles("ADMIN"));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithAdmin).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenRoleUserOrAdminConfiguredAndRoleIsOtherThenRespondsWithForbidden() throws Exception {
|
||||||
|
this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithRoleOther = get("/")
|
||||||
|
.with(user("user")
|
||||||
|
.roles("OTHER"));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithRoleOther).andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenDenyAllConfiguredAndNoUserThenRespondsWithUnauthorized() throws Exception {
|
||||||
|
this.spring.register(DenyAllConfig.class, BasicController.class).autowire();
|
||||||
|
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenDenyAllConfiguredAndUserLoggedInThenRespondsWithForbidden() throws Exception {
|
||||||
|
this.spring.register(DenyAllConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||||
|
.with(user("user")
|
||||||
|
.roles("USER"));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithUser).andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenPermitAllConfiguredAndNoUserThenRespondsWithOk() throws Exception {
|
||||||
|
this.spring.register(PermitAllConfig.class, BasicController.class).autowire();
|
||||||
|
this.mvc.perform(get("/")).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenPermitAllConfiguredAndUserLoggedInThenRespondsWithOk() throws Exception {
|
||||||
|
this.spring.register(PermitAllConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||||
|
.with(user("user")
|
||||||
|
.roles("USER"));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void authorizeHttpRequestsWhenInvokedTwiceThenUsesOriginalConfiguration() throws Exception {
|
||||||
|
this.spring.register(InvokeTwiceDoesNotResetConfig.class, BasicController.class).autowire();
|
||||||
|
this.mvc.perform(post("/").with(csrf())).andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenServletPathRoleAdminConfiguredAndRoleIsUserThenRespondsWithForbidden() throws Exception {
|
||||||
|
this.spring.register(ServletPathConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/spring/")
|
||||||
|
.servletPath("/spring")
|
||||||
|
.with(user("user")
|
||||||
|
.roles("USER"));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithUser).andExpect(status().isForbidden());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenServletPathRoleAdminConfiguredAndRoleIsAdminThenRespondsWithOk() throws Exception {
|
||||||
|
this.spring.register(ServletPathConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithAdmin = get("/spring/")
|
||||||
|
.servletPath("/spring")
|
||||||
|
.with(user("user")
|
||||||
|
.roles("ADMIN"));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithAdmin).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenAnyRequestAuthenticatedConfiguredAndNoUserThenRespondsWithUnauthorized() throws Exception {
|
||||||
|
this.spring.register(AuthenticatedConfig.class, BasicController.class).autowire();
|
||||||
|
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenAnyRequestAuthenticatedConfiguredAndUserLoggedInThenRespondsWithOk() throws Exception {
|
||||||
|
this.spring.register(AuthenticatedConfig.class, BasicController.class).autowire();
|
||||||
|
// @formatter:off
|
||||||
|
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||||
|
.with(user("user")
|
||||||
|
.roles("USER"));
|
||||||
|
// @formatter:on
|
||||||
|
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class NoRequestsConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http
|
||||||
|
.authorizeHttpRequests(withDefaults())
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class IncompleteMappingConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http
|
||||||
|
.authorizeHttpRequests(AbstractRequestMatcherRegistry::anyRequest)
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class AfterAnyRequestConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http
|
||||||
|
.authorizeHttpRequests((requests) -> requests
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
.mvcMatchers("/path").hasRole("USER")
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class CustomAuthorizationManagerConfig {
|
||||||
|
|
||||||
|
static AuthorizationManager<RequestAuthorizationContext> authorizationManager;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http
|
||||||
|
.authorizeHttpRequests((requests) -> requests
|
||||||
|
.anyRequest().access(authorizationManager)
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class ObjectPostProcessorConfig {
|
||||||
|
|
||||||
|
static ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http
|
||||||
|
.authorizeHttpRequests((requests) -> requests
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
static ObjectPostProcessor<Object> objectPostProcessor() {
|
||||||
|
return objectPostProcessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ReflectingObjectPostProcessor implements ObjectPostProcessor<Object> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <O> O postProcess(O object) {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class RoleUserAnyAuthorityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http
|
||||||
|
.httpBasic()
|
||||||
|
.and()
|
||||||
|
.authorizeHttpRequests((requests) -> requests
|
||||||
|
.anyRequest().hasAnyAuthority("ROLE_USER")
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class RoleUserAuthorityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http
|
||||||
|
.httpBasic()
|
||||||
|
.and()
|
||||||
|
.authorizeHttpRequests((requests) -> requests
|
||||||
|
.anyRequest().hasAuthority("ROLE_USER")
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class RoleUserOrRoleAdminAuthorityConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http
|
||||||
|
.httpBasic()
|
||||||
|
.and()
|
||||||
|
.authorizeHttpRequests((requests) -> requests
|
||||||
|
.anyRequest().hasAnyAuthority("ROLE_USER", "ROLE_ADMIN")
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class RoleUserConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http
|
||||||
|
.authorizeHttpRequests((requests) -> requests
|
||||||
|
.anyRequest().hasRole("USER")
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class RoleUserOrAdminConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http
|
||||||
|
.authorizeHttpRequests((requests) -> requests
|
||||||
|
.anyRequest().hasAnyRole("USER", "ADMIN")
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class DenyAllConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http
|
||||||
|
.httpBasic()
|
||||||
|
.and()
|
||||||
|
.authorizeHttpRequests((requests) -> requests
|
||||||
|
.anyRequest().denyAll()
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class PermitAllConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http
|
||||||
|
.authorizeHttpRequests((requests) -> requests
|
||||||
|
.anyRequest().permitAll()
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class InvokeTwiceDoesNotResetConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http
|
||||||
|
.httpBasic()
|
||||||
|
.and()
|
||||||
|
.authorizeHttpRequests((requests) -> requests
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
)
|
||||||
|
.authorizeHttpRequests(withDefaults())
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebMvc
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class ServletPathConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http
|
||||||
|
.authorizeHttpRequests((requests) -> requests
|
||||||
|
.mvcMatchers("/").servletPath("/spring").hasRole("ADMIN")
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class AuthenticatedConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
return http
|
||||||
|
.httpBasic()
|
||||||
|
.and()
|
||||||
|
.authorizeHttpRequests((requests) -> requests
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
// @formatter:on
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
static class BasicController {
|
||||||
|
|
||||||
|
@GetMapping("/")
|
||||||
|
void rootGet() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/")
|
||||||
|
void rootPost() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.authorization;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||||
|
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link AuthorizationManager} that determines if the current user is authenticated.
|
||||||
|
*
|
||||||
|
* @param <T> the type of object authorization is being performed against. This does not.
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
public final class AuthenticatedAuthorizationManager<T> implements AuthorizationManager<T> {
|
||||||
|
|
||||||
|
private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link AuthenticatedAuthorizationManager}.
|
||||||
|
* @param <T> the type of object being authorized
|
||||||
|
* @return the new instance
|
||||||
|
*/
|
||||||
|
public static <T> AuthenticatedAuthorizationManager<T> authenticated() {
|
||||||
|
return new AuthenticatedAuthorizationManager<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the current user is authorized by evaluating if the
|
||||||
|
* {@link Authentication} is not anonymous and authenticated.
|
||||||
|
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
||||||
|
* @param object the {@link T} object to check
|
||||||
|
* @return an {@link AuthorizationDecision}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
|
||||||
|
boolean granted = isGranted(authentication.get());
|
||||||
|
return new AuthorizationDecision(granted);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isGranted(Authentication authentication) {
|
||||||
|
return authentication != null && isNotAnonymous(authentication) && authentication.isAuthenticated();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isNotAnonymous(Authentication authentication) {
|
||||||
|
return !this.trustResolver.isAnonymous(authentication);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,135 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.authorization;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link AuthorizationManager} that determines if the current user is authorized by
|
||||||
|
* evaluating if the {@link Authentication} contains a specified authority.
|
||||||
|
*
|
||||||
|
* @param <T> the type of object being authorized.
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
public final class AuthorityAuthorizationManager<T> implements AuthorizationManager<T> {
|
||||||
|
|
||||||
|
private static final String ROLE_PREFIX = "ROLE_";
|
||||||
|
|
||||||
|
private final Set<String> authorities;
|
||||||
|
|
||||||
|
private AuthorityAuthorizationManager(String... authorities) {
|
||||||
|
this.authorities = new HashSet<>(Arrays.asList(authorities));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link AuthorityAuthorizationManager} with the provided
|
||||||
|
* authority.
|
||||||
|
* @param role the authority to check for prefixed with "ROLE_"
|
||||||
|
* @param <T> the type of object being authorized
|
||||||
|
* @return the new instance
|
||||||
|
*/
|
||||||
|
public static <T> AuthorityAuthorizationManager<T> hasRole(String role) {
|
||||||
|
Assert.notNull(role, "role cannot be null");
|
||||||
|
return hasAuthority(ROLE_PREFIX + role);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link AuthorityAuthorizationManager} with the provided
|
||||||
|
* authority.
|
||||||
|
* @param authority the authority to check for
|
||||||
|
* @param <T> the type of object being authorized
|
||||||
|
* @return the new instance
|
||||||
|
*/
|
||||||
|
public static <T> AuthorityAuthorizationManager<T> hasAuthority(String authority) {
|
||||||
|
Assert.notNull(authority, "authority cannot be null");
|
||||||
|
return new AuthorityAuthorizationManager<>(authority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link AuthorityAuthorizationManager} with the provided
|
||||||
|
* authorities.
|
||||||
|
* @param roles the authorities to check for prefixed with "ROLE_"
|
||||||
|
* @param <T> the type of object being authorized
|
||||||
|
* @return the new instance
|
||||||
|
*/
|
||||||
|
public static <T> AuthorityAuthorizationManager<T> hasAnyRole(String... roles) {
|
||||||
|
Assert.notEmpty(roles, "roles cannot be empty");
|
||||||
|
Assert.noNullElements(roles, "roles cannot contain null values");
|
||||||
|
return hasAnyAuthority(toNamedRolesArray(roles));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of {@link AuthorityAuthorizationManager} with the provided
|
||||||
|
* authorities.
|
||||||
|
* @param authorities the authorities to check for
|
||||||
|
* @param <T> the type of object being authorized
|
||||||
|
* @return the new instance
|
||||||
|
*/
|
||||||
|
public static <T> AuthorityAuthorizationManager<T> hasAnyAuthority(String... authorities) {
|
||||||
|
Assert.notEmpty(authorities, "authorities cannot be empty");
|
||||||
|
Assert.noNullElements(authorities, "authorities cannot contain null values");
|
||||||
|
return new AuthorityAuthorizationManager<>(authorities);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] toNamedRolesArray(String... roles) {
|
||||||
|
String[] result = new String[roles.length];
|
||||||
|
for (int i = 0; i < roles.length; i++) {
|
||||||
|
result[i] = ROLE_PREFIX + roles[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the current user is authorized by evaluating if the
|
||||||
|
* {@link Authentication} contains a specified authority.
|
||||||
|
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
||||||
|
* @param object the {@link T} object to check
|
||||||
|
* @return an {@link AuthorizationDecision}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AuthorizationDecision check(Supplier<Authentication> authentication, T object) {
|
||||||
|
boolean granted = isGranted(authentication.get());
|
||||||
|
return new AuthorizationDecision(granted);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isGranted(Authentication authentication) {
|
||||||
|
return authentication != null && authentication.isAuthenticated() && isAuthorized(authentication);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAuthorized(Authentication authentication) {
|
||||||
|
for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
|
||||||
|
String authority = grantedAuthority.getAuthority();
|
||||||
|
if (this.authorities.contains(authority)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "AuthorityAuthorizationManager[authorities=" + this.authorities + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.authorization;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Authorization manager which can determine if an {@link Authentication} has access to
|
||||||
|
* a specific object.
|
||||||
|
*
|
||||||
|
* @param <T> the type of object that the authorization check is being done one.
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface AuthorizationManager<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if access should be granted for a specific authentication and object.
|
||||||
|
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
||||||
|
* @param object the {@link T} object to check
|
||||||
|
* @throws AccessDeniedException if access is not granted
|
||||||
|
*/
|
||||||
|
default void verify(Supplier<Authentication> authentication, T object) {
|
||||||
|
AuthorizationDecision decision = check(authentication, object);
|
||||||
|
if (decision != null && !decision.isGranted()) {
|
||||||
|
throw new AccessDeniedException("Access Denied");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if access is granted for a specific authentication and object.
|
||||||
|
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
||||||
|
* @param object the {@link T} object to check
|
||||||
|
* @return an {@link AuthorizationDecision} or null if no decision could be made
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
AuthorizationDecision check(Supplier<Authentication> authentication, T object);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.authorization;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.authority.AuthorityUtils;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link AuthenticatedAuthorizationManager}.
|
||||||
|
*
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
public class AuthenticatedAuthorizationManagerTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void authenticatedWhenUserNotAnonymousAndAuthenticatedThenGrantedDecision() {
|
||||||
|
AuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();
|
||||||
|
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN",
|
||||||
|
"ROLE_USER");
|
||||||
|
Object object = new Object();
|
||||||
|
|
||||||
|
assertThat(manager.check(authentication, object).isGranted()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void authenticatedWhenUserNullThenDeniedDecision() {
|
||||||
|
AuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();
|
||||||
|
Supplier<Authentication> authentication = () -> null;
|
||||||
|
Object object = new Object();
|
||||||
|
|
||||||
|
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void authenticatedWhenUserAnonymousThenDeniedDecision() {
|
||||||
|
AuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();
|
||||||
|
Supplier<Authentication> authentication = () -> new AnonymousAuthenticationToken("key", "principal",
|
||||||
|
AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
|
||||||
|
Object object = new Object();
|
||||||
|
|
||||||
|
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void authenticatedWhenUserNotAuthenticatedThenDeniedDecision() {
|
||||||
|
AuthenticatedAuthorizationManager<Object> manager = AuthenticatedAuthorizationManager.authenticated();
|
||||||
|
TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", "password", "ROLE_ADMIN",
|
||||||
|
"ROLE_USER");
|
||||||
|
authentication.setAuthenticated(false);
|
||||||
|
Object object = new Object();
|
||||||
|
|
||||||
|
assertThat(manager.check(() -> authentication, object).isGranted()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.authorization;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link AuthorityAuthorizationManager}.
|
||||||
|
*
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
public class AuthorityAuthorizationManagerTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasRoleWhenNullThenException() {
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasRole(null))
|
||||||
|
.withMessage("role cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAuthorityWhenNullThenException() {
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAuthority(null))
|
||||||
|
.withMessage("authority cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAnyRoleWhenNullThenException() {
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole(null))
|
||||||
|
.withMessage("roles cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAnyRoleWhenEmptyThenException() {
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole(new String[] {}))
|
||||||
|
.withMessage("roles cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAnyRoleWhenContainNullThenException() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyRole("ADMIN", null, "USER"))
|
||||||
|
.withMessage("roles cannot contain null values");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAnyAuthorityWhenNullThenException() {
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority(null))
|
||||||
|
.withMessage("authorities cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAnyAuthorityWhenEmptyThenException() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority(new String[] {}))
|
||||||
|
.withMessage("authorities cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAnyAuthorityWhenContainNullThenException() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> AuthorityAuthorizationManager.hasAnyAuthority("ADMIN", null, "USER"))
|
||||||
|
.withMessage("authorities cannot contain null values");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasRoleWhenUserHasRoleThenGrantedDecision() {
|
||||||
|
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasRole("ADMIN");
|
||||||
|
|
||||||
|
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN",
|
||||||
|
"ROLE_USER");
|
||||||
|
Object object = new Object();
|
||||||
|
|
||||||
|
assertThat(manager.check(authentication, object).isGranted()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasRoleWhenUserHasNotRoleThenDeniedDecision() {
|
||||||
|
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasRole("ADMIN");
|
||||||
|
|
||||||
|
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||||
|
Object object = new Object();
|
||||||
|
|
||||||
|
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAuthorityWhenUserHasAuthorityThenGrantedDecision() {
|
||||||
|
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAuthority("ADMIN");
|
||||||
|
|
||||||
|
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ADMIN",
|
||||||
|
"USER");
|
||||||
|
Object object = new Object();
|
||||||
|
|
||||||
|
assertThat(manager.check(authentication, object).isGranted()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAuthorityWhenUserHasNotAuthorityThenDeniedDecision() {
|
||||||
|
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAuthority("ADMIN");
|
||||||
|
|
||||||
|
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "USER");
|
||||||
|
Object object = new Object();
|
||||||
|
|
||||||
|
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAnyRoleWhenUserHasAnyRoleThenGrantedDecision() {
|
||||||
|
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyRole("ADMIN", "USER");
|
||||||
|
|
||||||
|
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||||
|
Object object = new Object();
|
||||||
|
|
||||||
|
assertThat(manager.check(authentication, object).isGranted()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAnyRoleWhenUserHasNotAnyRoleThenDeniedDecision() {
|
||||||
|
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyRole("ADMIN", "USER");
|
||||||
|
|
||||||
|
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password",
|
||||||
|
"ROLE_ANONYMOUS");
|
||||||
|
Object object = new Object();
|
||||||
|
|
||||||
|
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAnyAuthorityWhenUserHasAnyAuthorityThenGrantedDecision() {
|
||||||
|
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyAuthority("ADMIN", "USER");
|
||||||
|
|
||||||
|
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "USER");
|
||||||
|
Object object = new Object();
|
||||||
|
|
||||||
|
assertThat(manager.check(authentication, object).isGranted()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hasAnyAuthorityWhenUserHasNotAnyAuthorityThenDeniedDecision() {
|
||||||
|
AuthorityAuthorizationManager<Object> manager = AuthorityAuthorizationManager.hasAnyAuthority("ADMIN", "USER");
|
||||||
|
|
||||||
|
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ANONYMOUS");
|
||||||
|
Object object = new Object();
|
||||||
|
|
||||||
|
assertThat(manager.check(authentication, object).isGranted()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.authorization;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link AuthorizationManager}.
|
||||||
|
*
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
public class AuthorizationManagerTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void verifyWhenCheckReturnedGrantedDecisionThenPasses() {
|
||||||
|
AuthorizationManager<Object> manager = (a, o) -> new AuthorizationDecision(true);
|
||||||
|
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_1", "ROLE_2");
|
||||||
|
Object object = new Object();
|
||||||
|
|
||||||
|
manager.verify(() -> authentication, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void verifyWhenCheckReturnedNullThenPasses() {
|
||||||
|
AuthorizationManager<Object> manager = (a, o) -> null;
|
||||||
|
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_1", "ROLE_2");
|
||||||
|
Object object = new Object();
|
||||||
|
|
||||||
|
manager.verify(() -> authentication, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void verifyWhenCheckReturnedDeniedDecisionThenAccessDeniedException() {
|
||||||
|
AuthorizationManager<Object> manager = (a, o) -> new AuthorizationDecision(false);
|
||||||
|
|
||||||
|
Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_1", "ROLE_2");
|
||||||
|
Object object = new Object();
|
||||||
|
|
||||||
|
assertThatExceptionOfType(AccessDeniedException.class)
|
||||||
|
.isThrownBy(() -> manager.verify(() -> authentication, object)).withMessage("Access Denied");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.web.access.intercept;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.web.filter.OncePerRequestFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An authorization filter that restricts access to the URL using
|
||||||
|
* {@link AuthorizationManager}.
|
||||||
|
*
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
public class AuthorizationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private final AuthorizationManager<HttpServletRequest> authorizationManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance.
|
||||||
|
* @param authorizationManager the {@link AuthorizationManager} to use
|
||||||
|
*/
|
||||||
|
public AuthorizationFilter(AuthorizationManager<HttpServletRequest> authorizationManager) {
|
||||||
|
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
|
||||||
|
this.authorizationManager = authorizationManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
|
throws ServletException, IOException {
|
||||||
|
|
||||||
|
this.authorizationManager.verify(this::getAuthentication, request);
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Authentication getAuthentication() {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (authentication == null) {
|
||||||
|
throw new AuthenticationCredentialsNotFoundException(
|
||||||
|
"An Authentication object was not found in the SecurityContext");
|
||||||
|
}
|
||||||
|
return authentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.web.access.intercept;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import org.springframework.core.log.LogMessage;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcher.MatchResult;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link AuthorizationManager} which delegates to a specific
|
||||||
|
* {@link AuthorizationManager} based on a {@link RequestMatcher} evaluation.
|
||||||
|
*
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
public final class DelegatingAuthorizationManager implements AuthorizationManager<HttpServletRequest> {
|
||||||
|
|
||||||
|
private final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
|
private final Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings;
|
||||||
|
|
||||||
|
private DelegatingAuthorizationManager(
|
||||||
|
Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings) {
|
||||||
|
Assert.notEmpty(mappings, "mappings cannot be empty");
|
||||||
|
this.mappings = mappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegates to a specific {@link AuthorizationManager} based on a
|
||||||
|
* {@link RequestMatcher} evaluation.
|
||||||
|
* @param authentication the {@link Supplier} of the {@link Authentication} to check
|
||||||
|
* @param request the {@link HttpServletRequest} to check
|
||||||
|
* @return an {@link AuthorizationDecision}. If there is no {@link RequestMatcher}
|
||||||
|
* matching the request, or the {@link AuthorizationManager} could not decide, then
|
||||||
|
* null is returned
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest request) {
|
||||||
|
if (this.logger.isTraceEnabled()) {
|
||||||
|
this.logger.trace(LogMessage.format("Authorizing %s", request));
|
||||||
|
}
|
||||||
|
for (Map.Entry<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mapping : this.mappings
|
||||||
|
.entrySet()) {
|
||||||
|
|
||||||
|
RequestMatcher matcher = mapping.getKey();
|
||||||
|
MatchResult matchResult = matcher.matcher(request);
|
||||||
|
if (matchResult.isMatch()) {
|
||||||
|
AuthorizationManager<RequestAuthorizationContext> manager = mapping.getValue();
|
||||||
|
if (this.logger.isTraceEnabled()) {
|
||||||
|
this.logger.trace(LogMessage.format("Checking authorization on %s using %s", request, manager));
|
||||||
|
}
|
||||||
|
return manager.check(authentication,
|
||||||
|
new RequestAuthorizationContext(request, matchResult.getVariables()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.logger.trace("Abstaining since did not find matching RequestMatcher");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a builder for {@link DelegatingAuthorizationManager}.
|
||||||
|
* @return the new {@link Builder} instance
|
||||||
|
*/
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A builder for {@link DelegatingAuthorizationManager}.
|
||||||
|
*/
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
private final Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps a {@link RequestMatcher} to an {@link AuthorizationManager}.
|
||||||
|
* @param matcher the {@link RequestMatcher} to use
|
||||||
|
* @param manager the {@link AuthorizationManager} to use
|
||||||
|
* @return the {@link Builder} for further customizations
|
||||||
|
*/
|
||||||
|
public Builder add(RequestMatcher matcher, AuthorizationManager<RequestAuthorizationContext> manager) {
|
||||||
|
Assert.notNull(matcher, "matcher cannot be null");
|
||||||
|
Assert.notNull(manager, "manager cannot be null");
|
||||||
|
this.mappings.put(matcher, manager);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link DelegatingAuthorizationManager} instance.
|
||||||
|
* @return the {@link DelegatingAuthorizationManager} instance
|
||||||
|
*/
|
||||||
|
public DelegatingAuthorizationManager build() {
|
||||||
|
return new DelegatingAuthorizationManager(this.mappings);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.web.access.intercept;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link HttpServletRequest} authorization context.
|
||||||
|
*
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
public final class RequestAuthorizationContext {
|
||||||
|
|
||||||
|
private final HttpServletRequest request;
|
||||||
|
|
||||||
|
private final Map<String, String> variables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance.
|
||||||
|
* @param request the {@link HttpServletRequest} to use
|
||||||
|
*/
|
||||||
|
public RequestAuthorizationContext(HttpServletRequest request) {
|
||||||
|
this(request, Collections.emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance.
|
||||||
|
* @param request the {@link HttpServletRequest} to use
|
||||||
|
* @param variables a map containing key-value pairs representing extracted variable
|
||||||
|
* names and variable values
|
||||||
|
*/
|
||||||
|
public RequestAuthorizationContext(HttpServletRequest request, Map<String, String> variables) {
|
||||||
|
this.request = request;
|
||||||
|
this.variables = variables;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link HttpServletRequest}.
|
||||||
|
* @return the {@link HttpServletRequest} to use
|
||||||
|
*/
|
||||||
|
public HttpServletRequest getRequest() {
|
||||||
|
return this.request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the extracted variable values where the key is the variable name and the
|
||||||
|
* value is the variable value.
|
||||||
|
* @return a map containing key-value pairs representing extracted variable names and
|
||||||
|
* variable values
|
||||||
|
*/
|
||||||
|
public Map<String, String> getVariables() {
|
||||||
|
return this.variables;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.web.access.intercept;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.mock.web.MockHttpServletResponse;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
|
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.context.SecurityContextImpl;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.BDDMockito.willThrow;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link AuthorizationFilter}.
|
||||||
|
*
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
public class AuthorizationFilterTests {
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void filterWhenAuthorizationManagerVerifyPassesThenNextFilter() throws Exception {
|
||||||
|
AuthorizationManager<HttpServletRequest> mockAuthorizationManager = mock(AuthorizationManager.class);
|
||||||
|
AuthorizationFilter filter = new AuthorizationFilter(mockAuthorizationManager);
|
||||||
|
TestingAuthenticationToken authenticationToken = new TestingAuthenticationToken("user", "password");
|
||||||
|
|
||||||
|
SecurityContext securityContext = new SecurityContextImpl();
|
||||||
|
securityContext.setAuthentication(authenticationToken);
|
||||||
|
SecurityContextHolder.setContext(securityContext);
|
||||||
|
|
||||||
|
MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path");
|
||||||
|
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
|
||||||
|
FilterChain mockFilterChain = mock(FilterChain.class);
|
||||||
|
|
||||||
|
filter.doFilter(mockRequest, mockResponse, mockFilterChain);
|
||||||
|
|
||||||
|
ArgumentCaptor<Supplier<Authentication>> authenticationCaptor = ArgumentCaptor.forClass(Supplier.class);
|
||||||
|
verify(mockAuthorizationManager).verify(authenticationCaptor.capture(), eq(mockRequest));
|
||||||
|
Supplier<Authentication> authentication = authenticationCaptor.getValue();
|
||||||
|
assertThat(authentication.get()).isEqualTo(authenticationToken);
|
||||||
|
|
||||||
|
verify(mockFilterChain).doFilter(mockRequest, mockResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void filterWhenAuthorizationManagerVerifyThrowsAccessDeniedExceptionThenStopFilterChain() {
|
||||||
|
AuthorizationManager<HttpServletRequest> mockAuthorizationManager = mock(AuthorizationManager.class);
|
||||||
|
AuthorizationFilter filter = new AuthorizationFilter(mockAuthorizationManager);
|
||||||
|
TestingAuthenticationToken authenticationToken = new TestingAuthenticationToken("user", "password");
|
||||||
|
|
||||||
|
SecurityContext securityContext = new SecurityContextImpl();
|
||||||
|
securityContext.setAuthentication(authenticationToken);
|
||||||
|
SecurityContextHolder.setContext(securityContext);
|
||||||
|
|
||||||
|
MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path");
|
||||||
|
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
|
||||||
|
FilterChain mockFilterChain = mock(FilterChain.class);
|
||||||
|
|
||||||
|
willThrow(new AccessDeniedException("Access Denied")).given(mockAuthorizationManager).verify(any(),
|
||||||
|
eq(mockRequest));
|
||||||
|
|
||||||
|
assertThatExceptionOfType(AccessDeniedException.class)
|
||||||
|
.isThrownBy(() -> filter.doFilter(mockRequest, mockResponse, mockFilterChain))
|
||||||
|
.withMessage("Access Denied");
|
||||||
|
|
||||||
|
ArgumentCaptor<Supplier<Authentication>> authenticationCaptor = ArgumentCaptor.forClass(Supplier.class);
|
||||||
|
verify(mockAuthorizationManager).verify(authenticationCaptor.capture(), eq(mockRequest));
|
||||||
|
Supplier<Authentication> authentication = authenticationCaptor.getValue();
|
||||||
|
assertThat(authentication.get()).isEqualTo(authenticationToken);
|
||||||
|
|
||||||
|
verifyNoInteractions(mockFilterChain);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void filterWhenAuthenticationNullThenAuthenticationCredentialsNotFoundException() {
|
||||||
|
AuthorizationFilter filter = new AuthorizationFilter(AuthenticatedAuthorizationManager.authenticated());
|
||||||
|
MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path");
|
||||||
|
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
|
||||||
|
FilterChain mockFilterChain = mock(FilterChain.class);
|
||||||
|
|
||||||
|
assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
|
||||||
|
.isThrownBy(() -> filter.doFilter(mockRequest, mockResponse, mockFilterChain))
|
||||||
|
.withMessage("An Authentication object was not found in the SecurityContext");
|
||||||
|
|
||||||
|
verifyNoInteractions(mockFilterChain);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2002-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.security.web.access.intercept;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link DelegatingAuthorizationManager}.
|
||||||
|
*
|
||||||
|
* @author Evgeniy Cheban
|
||||||
|
*/
|
||||||
|
public class DelegatingAuthorizationManagerTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildWhenMappingsEmptyThenException() {
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> DelegatingAuthorizationManager.builder().build())
|
||||||
|
.withMessage("mappings cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addWhenMatcherNullThenException() {
|
||||||
|
assertThatIllegalArgumentException()
|
||||||
|
.isThrownBy(() -> DelegatingAuthorizationManager.builder()
|
||||||
|
.add(null, (a, o) -> new AuthorizationDecision(true)).build())
|
||||||
|
.withMessage("matcher cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void addWhenManagerNullThenException() {
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(
|
||||||
|
() -> DelegatingAuthorizationManager.builder().add(new MvcRequestMatcher(null, "/grant"), null).build())
|
||||||
|
.withMessage("manager cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void checkWhenMultipleMappingsConfiguredThenDelegatesMatchingManager() {
|
||||||
|
DelegatingAuthorizationManager manager = DelegatingAuthorizationManager.builder()
|
||||||
|
.add(new MvcRequestMatcher(null, "/grant"), (a, o) -> new AuthorizationDecision(true))
|
||||||
|
.add(new MvcRequestMatcher(null, "/deny"), (a, o) -> new AuthorizationDecision(false))
|
||||||
|
.add(new MvcRequestMatcher(null, "/neutral"), (a, o) -> null).build();
|
||||||
|
|
||||||
|
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||||
|
|
||||||
|
AuthorizationDecision grant = manager.check(authentication, new MockHttpServletRequest(null, "/grant"));
|
||||||
|
assertThat(grant).isNotNull();
|
||||||
|
assertThat(grant.isGranted()).isTrue();
|
||||||
|
|
||||||
|
AuthorizationDecision deny = manager.check(authentication, new MockHttpServletRequest(null, "/deny"));
|
||||||
|
assertThat(deny).isNotNull();
|
||||||
|
assertThat(deny.isGranted()).isFalse();
|
||||||
|
|
||||||
|
AuthorizationDecision neutral = manager.check(authentication, new MockHttpServletRequest(null, "/neutral"));
|
||||||
|
assertThat(neutral).isNull();
|
||||||
|
|
||||||
|
AuthorizationDecision abstain = manager.check(authentication, new MockHttpServletRequest(null, "/abstain"));
|
||||||
|
assertThat(abstain).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user