Configure WebInvocationPrivilegeEvaluator bean for multiple filter chains
Closes gh-10554
This commit is contained in:
parent
51b4bd67c9
commit
d884d9a461
|
@ -20,6 +20,7 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jakarta.servlet.Filter;
|
import jakarta.servlet.Filter;
|
||||||
|
import jakarta.servlet.ServletContext;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
|
@ -33,6 +34,7 @@ import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.security.access.PermissionEvaluator;
|
import org.springframework.security.access.PermissionEvaluator;
|
||||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||||
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
|
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
|
||||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||||
|
@ -47,9 +49,12 @@ import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||||
import org.springframework.security.web.FilterChainProxy;
|
import org.springframework.security.web.FilterChainProxy;
|
||||||
import org.springframework.security.web.FilterInvocation;
|
import org.springframework.security.web.FilterInvocation;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;
|
||||||
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
|
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
|
||||||
|
import org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;
|
||||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||||
|
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.debug.DebugFilter;
|
import org.springframework.security.web.debug.DebugFilter;
|
||||||
import org.springframework.security.web.firewall.HttpFirewall;
|
import org.springframework.security.web.firewall.HttpFirewall;
|
||||||
|
@ -57,7 +62,9 @@ import org.springframework.security.web.firewall.RequestRejectedHandler;
|
||||||
import org.springframework.security.web.firewall.StrictHttpFirewall;
|
import org.springframework.security.web.firewall.StrictHttpFirewall;
|
||||||
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.web.context.ServletContextAware;
|
||||||
import org.springframework.web.filter.DelegatingFilterProxy;
|
import org.springframework.web.filter.DelegatingFilterProxy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -81,7 +88,7 @@ import org.springframework.web.filter.DelegatingFilterProxy;
|
||||||
* @see WebSecurityConfiguration
|
* @see WebSecurityConfiguration
|
||||||
*/
|
*/
|
||||||
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
|
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
|
||||||
implements SecurityBuilder<Filter>, ApplicationContextAware {
|
implements SecurityBuilder<Filter>, ApplicationContextAware, ServletContextAware {
|
||||||
|
|
||||||
private final Log logger = LogFactory.getLog(getClass());
|
private final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
||||||
|
@ -108,6 +115,8 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||||
private Runnable postBuildAction = () -> {
|
private Runnable postBuildAction = () -> {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private ServletContext servletContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance
|
* Creates a new instance
|
||||||
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
|
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
|
||||||
|
@ -252,6 +261,8 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||||
* {@link WebSecurityConfigurerAdapter}.
|
* {@link WebSecurityConfigurerAdapter}.
|
||||||
* @param securityInterceptor the {@link FilterSecurityInterceptor} to use
|
* @param securityInterceptor the {@link FilterSecurityInterceptor} to use
|
||||||
* @return the {@link WebSecurity} for further customizations
|
* @return the {@link WebSecurity} for further customizations
|
||||||
|
* @deprecated Use {@link #privilegeEvaluator(WebInvocationPrivilegeEvaluator)}
|
||||||
|
* instead
|
||||||
*/
|
*/
|
||||||
public WebSecurity securityInterceptor(FilterSecurityInterceptor securityInterceptor) {
|
public WebSecurity securityInterceptor(FilterSecurityInterceptor securityInterceptor) {
|
||||||
this.filterSecurityInterceptor = securityInterceptor;
|
this.filterSecurityInterceptor = securityInterceptor;
|
||||||
|
@ -278,11 +289,22 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||||
+ ".addSecurityFilterChainBuilder directly");
|
+ ".addSecurityFilterChainBuilder directly");
|
||||||
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
|
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
|
||||||
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
|
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
|
||||||
|
List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();
|
||||||
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
|
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
|
||||||
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
|
SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
|
||||||
|
securityFilterChains.add(securityFilterChain);
|
||||||
|
requestMatcherPrivilegeEvaluatorsEntries
|
||||||
|
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
|
||||||
}
|
}
|
||||||
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
|
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
|
||||||
securityFilterChains.add(securityFilterChainBuilder.build());
|
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
|
||||||
|
securityFilterChains.add(securityFilterChain);
|
||||||
|
requestMatcherPrivilegeEvaluatorsEntries
|
||||||
|
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
|
||||||
|
}
|
||||||
|
if (this.privilegeEvaluator == null) {
|
||||||
|
this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
|
||||||
|
requestMatcherPrivilegeEvaluatorsEntries);
|
||||||
}
|
}
|
||||||
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
|
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
|
||||||
if (this.httpFirewall != null) {
|
if (this.httpFirewall != null) {
|
||||||
|
@ -306,6 +328,26 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> getRequestMatcherPrivilegeEvaluatorsEntry(
|
||||||
|
SecurityFilterChain securityFilterChain) {
|
||||||
|
List<WebInvocationPrivilegeEvaluator> privilegeEvaluators = new ArrayList<>();
|
||||||
|
for (Filter filter : securityFilterChain.getFilters()) {
|
||||||
|
if (filter instanceof FilterSecurityInterceptor) {
|
||||||
|
DefaultWebInvocationPrivilegeEvaluator defaultWebInvocationPrivilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
|
||||||
|
(FilterSecurityInterceptor) filter);
|
||||||
|
defaultWebInvocationPrivilegeEvaluator.setServletContext(this.servletContext);
|
||||||
|
privilegeEvaluators.add(defaultWebInvocationPrivilegeEvaluator);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (filter instanceof AuthorizationFilter) {
|
||||||
|
AuthorizationManager<HttpServletRequest> authorizationManager = ((AuthorizationFilter) filter)
|
||||||
|
.getAuthorizationManager();
|
||||||
|
privilegeEvaluators.add(new AuthorizationManagerWebInvocationPrivilegeEvaluator(authorizationManager));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new RequestMatcherEntry<>(securityFilterChain::matches, privilegeEvaluators);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||||
this.defaultWebSecurityExpressionHandler.setApplicationContext(applicationContext);
|
this.defaultWebSecurityExpressionHandler.setApplicationContext(applicationContext);
|
||||||
|
@ -333,6 +375,11 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setServletContext(ServletContext servletContext) {
|
||||||
|
this.servletContext = servletContext;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link IgnoredRequestConfigurer} that allows optionally configuring the
|
* An {@link IgnoredRequestConfigurer} that allows optionally configuring the
|
||||||
* {@link MvcRequestMatcher#setMethod(HttpMethod)}
|
* {@link MvcRequestMatcher#setMethod(HttpMethod)}
|
||||||
|
|
|
@ -127,8 +127,8 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary for the JSP
|
* Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary to evaluate
|
||||||
* tag support.
|
* privileges for a given web URI
|
||||||
* @return the {@link WebInvocationPrivilegeEvaluator}
|
* @return the {@link WebInvocationPrivilegeEvaluator}
|
||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2021 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.
|
||||||
|
@ -33,6 +33,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.expression.EvaluationContext;
|
import org.springframework.expression.EvaluationContext;
|
||||||
import org.springframework.expression.Expression;
|
import org.springframework.expression.Expression;
|
||||||
|
@ -62,7 +63,7 @@ import org.springframework.security.core.AuthenticationException;
|
||||||
import org.springframework.security.web.FilterChainProxy;
|
import org.springframework.security.web.FilterChainProxy;
|
||||||
import org.springframework.security.web.FilterInvocation;
|
import org.springframework.security.web.FilterInvocation;
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
|
import org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;
|
||||||
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
|
||||||
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
@ -84,6 +85,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
* @author Rob Winch
|
* @author Rob Winch
|
||||||
* @author Joe Grandja
|
* @author Joe Grandja
|
||||||
* @author Evgeniy Cheban
|
* @author Evgeniy Cheban
|
||||||
|
* @author Marcus Da Coregio
|
||||||
*/
|
*/
|
||||||
@ExtendWith(SpringTestContextExtension.class)
|
@ExtendWith(SpringTestContextExtension.class)
|
||||||
public class WebSecurityConfigurationTests {
|
public class WebSecurityConfigurationTests {
|
||||||
|
@ -218,10 +220,10 @@ public class WebSecurityConfigurationTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loadConfigWhenDefaultWebInvocationPrivilegeEvaluatorThenDefaultIsRegistered() {
|
public void loadConfigWhenDefaultWebInvocationPrivilegeEvaluatorThenRequestMatcherIsRegistered() {
|
||||||
this.spring.register(WebInvocationPrivilegeEvaluatorDefaultsConfig.class).autowire();
|
this.spring.register(WebInvocationPrivilegeEvaluatorDefaultsConfig.class).autowire();
|
||||||
assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
|
assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
|
||||||
.isInstanceOf(DefaultWebInvocationPrivilegeEvaluator.class);
|
.isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -229,7 +231,7 @@ public class WebSecurityConfigurationTests {
|
||||||
this.spring.register(AuthorizeRequestsFilterChainConfig.class).autowire();
|
this.spring.register(AuthorizeRequestsFilterChainConfig.class).autowire();
|
||||||
|
|
||||||
assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
|
assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
|
||||||
.isInstanceOf(DefaultWebInvocationPrivilegeEvaluator.class);
|
.isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SEC-2303
|
// SEC-2303
|
||||||
|
@ -375,6 +377,69 @@ public class WebSecurityConfigurationTests {
|
||||||
assertThat(filterChains.get(1).matches(request)).isTrue();
|
assertThat(filterChains.get(1).matches(request)).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loadConfigWhenTwoSecurityFilterChainsThenRequestMatcherDelegatingWebInvocationPrivilegeEvaluator() {
|
||||||
|
this.spring.register(TwoSecurityFilterChainConfig.class).autowire();
|
||||||
|
assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
|
||||||
|
.isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loadConfigWhenTwoSecurityFilterChainDebugThenRequestMatcherDelegatingWebInvocationPrivilegeEvaluator() {
|
||||||
|
this.spring.register(TwoSecurityFilterChainConfig.class).autowire();
|
||||||
|
assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
|
||||||
|
.isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// gh-10554
|
||||||
|
@Test
|
||||||
|
public void loadConfigWhenMultipleSecurityFilterChainsThenWebInvocationPrivilegeEvaluatorApplySecurity() {
|
||||||
|
this.spring.register(MultipleSecurityFilterChainConfig.class).autowire();
|
||||||
|
WebInvocationPrivilegeEvaluator privilegeEvaluator = this.spring.getContext()
|
||||||
|
.getBean(WebInvocationPrivilegeEvaluator.class);
|
||||||
|
assertUserPermissions(privilegeEvaluator);
|
||||||
|
assertAdminPermissions(privilegeEvaluator);
|
||||||
|
assertAnotherUserPermission(privilegeEvaluator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// gh-10554
|
||||||
|
@Test
|
||||||
|
public void loadConfigWhenMultipleSecurityFilterChainAndIgnoringThenWebInvocationPrivilegeEvaluatorAcceptsNullAuthenticationOnIgnored() {
|
||||||
|
this.spring.register(MultipleSecurityFilterChainIgnoringConfig.class).autowire();
|
||||||
|
WebInvocationPrivilegeEvaluator privilegeEvaluator = this.spring.getContext()
|
||||||
|
.getBean(WebInvocationPrivilegeEvaluator.class);
|
||||||
|
assertUserPermissions(privilegeEvaluator);
|
||||||
|
assertAdminPermissions(privilegeEvaluator);
|
||||||
|
assertAnotherUserPermission(privilegeEvaluator);
|
||||||
|
// null authentication
|
||||||
|
assertThat(privilegeEvaluator.isAllowed("/user", null)).isFalse();
|
||||||
|
assertThat(privilegeEvaluator.isAllowed("/admin", null)).isFalse();
|
||||||
|
assertThat(privilegeEvaluator.isAllowed("/another", null)).isFalse();
|
||||||
|
assertThat(privilegeEvaluator.isAllowed("/ignoring1", null)).isTrue();
|
||||||
|
assertThat(privilegeEvaluator.isAllowed("/ignoring1/child", null)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertAnotherUserPermission(WebInvocationPrivilegeEvaluator privilegeEvaluator) {
|
||||||
|
Authentication anotherUser = new TestingAuthenticationToken("anotherUser", "password", "ROLE_ANOTHER");
|
||||||
|
assertThat(privilegeEvaluator.isAllowed("/user", anotherUser)).isFalse();
|
||||||
|
assertThat(privilegeEvaluator.isAllowed("/admin", anotherUser)).isFalse();
|
||||||
|
assertThat(privilegeEvaluator.isAllowed("/another", anotherUser)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertAdminPermissions(WebInvocationPrivilegeEvaluator privilegeEvaluator) {
|
||||||
|
Authentication admin = new TestingAuthenticationToken("admin", "password", "ROLE_ADMIN");
|
||||||
|
assertThat(privilegeEvaluator.isAllowed("/user", admin)).isFalse();
|
||||||
|
assertThat(privilegeEvaluator.isAllowed("/admin", admin)).isTrue();
|
||||||
|
assertThat(privilegeEvaluator.isAllowed("/another", admin)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertUserPermissions(WebInvocationPrivilegeEvaluator privilegeEvaluator) {
|
||||||
|
Authentication user = new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||||
|
assertThat(privilegeEvaluator.isAllowed("/user", user)).isTrue();
|
||||||
|
assertThat(privilegeEvaluator.isAllowed("/admin", user)).isFalse();
|
||||||
|
assertThat(privilegeEvaluator.isAllowed("/another", user)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
@Import(AuthenticationTestConfiguration.class)
|
@Import(AuthenticationTestConfiguration.class)
|
||||||
static class SortedWebSecurityConfigurerAdaptersConfig {
|
static class SortedWebSecurityConfigurerAdaptersConfig {
|
||||||
|
@ -1008,4 +1073,125 @@ public class WebSecurityConfigurationTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
static class TwoSecurityFilterChainConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
public SecurityFilterChain path1(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.requestMatchers((requests) -> requests.antMatchers("/path1/**"))
|
||||||
|
.authorizeRequests((requests) -> requests.anyRequest().authenticated());
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||||
|
public SecurityFilterChain permitAll(HttpSecurity http) throws Exception {
|
||||||
|
http.authorizeRequests((requests) -> requests.anyRequest().permitAll());
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity(debug = true)
|
||||||
|
static class TwoSecurityFilterChainDebugConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
public SecurityFilterChain path1(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.requestMatchers((requests) -> requests.antMatchers("/path1/**"))
|
||||||
|
.authorizeRequests((requests) -> requests.anyRequest().authenticated());
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||||
|
public SecurityFilterChain permitAll(HttpSecurity http) throws Exception {
|
||||||
|
http.authorizeRequests((requests) -> requests.anyRequest().permitAll());
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Import(AuthenticationTestConfiguration.class)
|
||||||
|
static class MultipleSecurityFilterChainConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
public SecurityFilterChain notAuthorized(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.requestMatchers((requests) -> requests.antMatchers("/user"))
|
||||||
|
.authorizeRequests((requests) -> requests.anyRequest().hasRole("USER"));
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
|
||||||
|
public SecurityFilterChain path1(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.requestMatchers((requests) -> requests.antMatchers("/admin"))
|
||||||
|
.authorizeRequests((requests) -> requests.anyRequest().hasRole("ADMIN"));
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||||
|
public SecurityFilterChain permitAll(HttpSecurity http) throws Exception {
|
||||||
|
http.authorizeRequests((requests) -> requests.anyRequest().permitAll());
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnableWebSecurity
|
||||||
|
@Import(AuthenticationTestConfiguration.class)
|
||||||
|
static class MultipleSecurityFilterChainIgnoringConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public WebSecurityCustomizer webSecurityCustomizer() {
|
||||||
|
return (web) -> web.ignoring().antMatchers("/ignoring1/**");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||||
|
public SecurityFilterChain notAuthorized(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.requestMatchers((requests) -> requests.antMatchers("/user"))
|
||||||
|
.authorizeRequests((requests) -> requests.anyRequest().hasRole("USER"));
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
|
||||||
|
public SecurityFilterChain admin(HttpSecurity http) throws Exception {
|
||||||
|
// @formatter:off
|
||||||
|
http
|
||||||
|
.requestMatchers((requests) -> requests.antMatchers("/admin"))
|
||||||
|
.authorizeRequests((requests) -> requests.anyRequest().hasRole("ADMIN"));
|
||||||
|
// @formatter:on
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||||
|
public SecurityFilterChain permitAll(HttpSecurity http) throws Exception {
|
||||||
|
http.authorizeRequests((requests) -> requests.anyRequest().permitAll());
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2021 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;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.FilterInvocation;
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link WebInvocationPrivilegeEvaluator} which delegates to a list of
|
||||||
|
* {@link WebInvocationPrivilegeEvaluator} based on a
|
||||||
|
* {@link org.springframework.security.web.util.matcher.RequestMatcher} evaluation
|
||||||
|
*
|
||||||
|
* @author Marcus Da Coregio
|
||||||
|
* @since 5.7
|
||||||
|
*/
|
||||||
|
public final class RequestMatcherDelegatingWebInvocationPrivilegeEvaluator implements WebInvocationPrivilegeEvaluator {
|
||||||
|
|
||||||
|
private final List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> delegates;
|
||||||
|
|
||||||
|
public RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
|
||||||
|
List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries) {
|
||||||
|
Assert.notNull(requestMatcherPrivilegeEvaluatorsEntries, "requestMatcherPrivilegeEvaluators cannot be null");
|
||||||
|
for (RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> entry : requestMatcherPrivilegeEvaluatorsEntries) {
|
||||||
|
Assert.notNull(entry.getRequestMatcher(), "requestMatcher cannot be null");
|
||||||
|
Assert.notNull(entry.getEntry(), "webInvocationPrivilegeEvaluators cannot be null");
|
||||||
|
}
|
||||||
|
this.delegates = requestMatcherPrivilegeEvaluatorsEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the user represented by the supplied <tt>Authentication</tt>
|
||||||
|
* object is allowed to invoke the supplied URI.
|
||||||
|
* <p>
|
||||||
|
* Uses the provided URI in the
|
||||||
|
* {@link org.springframework.security.web.util.matcher.RequestMatcher#matches(HttpServletRequest)}
|
||||||
|
* for every {@code RequestMatcher} configured. If no {@code RequestMatcher} is
|
||||||
|
* matched, or if there is not an available {@code WebInvocationPrivilegeEvaluator},
|
||||||
|
* returns {@code true}.
|
||||||
|
* @param uri the URI excluding the context path (a default context path setting will
|
||||||
|
* be used)
|
||||||
|
* @return true if access is allowed, false if denied
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(String uri, Authentication authentication) {
|
||||||
|
List<WebInvocationPrivilegeEvaluator> privilegeEvaluators = getDelegate(null, uri, null);
|
||||||
|
if (privilegeEvaluators.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (WebInvocationPrivilegeEvaluator evaluator : privilegeEvaluators) {
|
||||||
|
boolean isAllowed = evaluator.isAllowed(uri, authentication);
|
||||||
|
if (!isAllowed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether the user represented by the supplied <tt>Authentication</tt>
|
||||||
|
* object is allowed to invoke the supplied URI.
|
||||||
|
* <p>
|
||||||
|
* Uses the provided URI in the
|
||||||
|
* {@link org.springframework.security.web.util.matcher.RequestMatcher#matches(HttpServletRequest)}
|
||||||
|
* for every {@code RequestMatcher} configured. If no {@code RequestMatcher} is
|
||||||
|
* matched, or if there is not an available {@code WebInvocationPrivilegeEvaluator},
|
||||||
|
* returns {@code true}.
|
||||||
|
* @param uri the URI excluding the context path (a default context path setting will
|
||||||
|
* be used)
|
||||||
|
* @param contextPath the context path (may be null, in which case a default value
|
||||||
|
* will be used).
|
||||||
|
* @param method the HTTP method (or null, for any method)
|
||||||
|
* @param authentication the <tt>Authentication</tt> instance whose authorities should
|
||||||
|
* be used in evaluation whether access should be granted.
|
||||||
|
* @return true if access is allowed, false if denied
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {
|
||||||
|
List<WebInvocationPrivilegeEvaluator> privilegeEvaluators = getDelegate(contextPath, uri, method);
|
||||||
|
if (privilegeEvaluators.isEmpty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (WebInvocationPrivilegeEvaluator evaluator : privilegeEvaluators) {
|
||||||
|
boolean isAllowed = evaluator.isAllowed(contextPath, uri, method, authentication);
|
||||||
|
if (!isAllowed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<WebInvocationPrivilegeEvaluator> getDelegate(String contextPath, String uri, String method) {
|
||||||
|
FilterInvocation filterInvocation = new FilterInvocation(contextPath, uri, method);
|
||||||
|
for (RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate : this.delegates) {
|
||||||
|
if (delegate.getRequestMatcher().matches(filterInvocation.getHttpRequest())) {
|
||||||
|
return delegate.getEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2021 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.
|
||||||
|
@ -67,4 +67,12 @@ public class AuthorizationFilter extends OncePerRequestFilter {
|
||||||
return authentication;
|
return authentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link AuthorizationManager} used by this filter
|
||||||
|
* @return the {@link AuthorizationManager}
|
||||||
|
*/
|
||||||
|
public AuthorizationManager<HttpServletRequest> getAuthorizationManager() {
|
||||||
|
return this.authorizationManager;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2016 the original author or authors.
|
* Copyright 2002-2021 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.
|
||||||
|
@ -151,6 +151,10 @@ public final class DebugFilter implements Filter {
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FilterChainProxy getFilterChainProxy() {
|
||||||
|
return this.filterChainProxy;
|
||||||
|
}
|
||||||
|
|
||||||
static class DebugRequestWrapper extends HttpServletRequestWrapper {
|
static class DebugRequestWrapper extends HttpServletRequestWrapper {
|
||||||
|
|
||||||
private static final Logger logger = new Logger();
|
private static final Logger logger = new Logger();
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2021 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;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
|
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link RequestMatcherDelegatingWebInvocationPrivilegeEvaluator}
|
||||||
|
*
|
||||||
|
* @author Marcus Da Coregio
|
||||||
|
*/
|
||||||
|
class RequestMatcherDelegatingWebInvocationPrivilegeEvaluatorTests {
|
||||||
|
|
||||||
|
private final RequestMatcher alwaysMatch = mock(RequestMatcher.class);
|
||||||
|
|
||||||
|
private final RequestMatcher alwaysDeny = mock(RequestMatcher.class);
|
||||||
|
|
||||||
|
private final String uri = "/test";
|
||||||
|
|
||||||
|
private final Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setup() {
|
||||||
|
given(this.alwaysMatch.matches(any())).willReturn(true);
|
||||||
|
given(this.alwaysDeny.matches(any())).willReturn(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isAllowedWhenDelegatesEmptyThenAllowed() {
|
||||||
|
RequestMatcherDelegatingWebInvocationPrivilegeEvaluator delegating = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
|
||||||
|
Collections.emptyList());
|
||||||
|
assertThat(delegating.isAllowed(this.uri, this.authentication)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isAllowedWhenNotMatchThenAllowed() {
|
||||||
|
RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> notMatch = new RequestMatcherEntry<>(this.alwaysDeny,
|
||||||
|
Collections.singletonList(TestWebInvocationPrivilegeEvaluator.alwaysAllow()));
|
||||||
|
RequestMatcherDelegatingWebInvocationPrivilegeEvaluator delegating = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
|
||||||
|
Collections.singletonList(notMatch));
|
||||||
|
assertThat(delegating.isAllowed(this.uri, this.authentication)).isTrue();
|
||||||
|
verify(notMatch.getRequestMatcher()).matches(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isAllowedWhenPrivilegeEvaluatorAllowThenAllowedTrue() {
|
||||||
|
RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate = new RequestMatcherEntry<>(
|
||||||
|
this.alwaysMatch, Collections.singletonList(TestWebInvocationPrivilegeEvaluator.alwaysAllow()));
|
||||||
|
RequestMatcherDelegatingWebInvocationPrivilegeEvaluator delegating = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
|
||||||
|
Collections.singletonList(delegate));
|
||||||
|
assertThat(delegating.isAllowed(this.uri, this.authentication)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isAllowedWhenPrivilegeEvaluatorDenyThenAllowedFalse() {
|
||||||
|
RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate = new RequestMatcherEntry<>(
|
||||||
|
this.alwaysMatch, Collections.singletonList(TestWebInvocationPrivilegeEvaluator.alwaysDeny()));
|
||||||
|
RequestMatcherDelegatingWebInvocationPrivilegeEvaluator delegating = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
|
||||||
|
Collections.singletonList(delegate));
|
||||||
|
assertThat(delegating.isAllowed(this.uri, this.authentication)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isAllowedWhenNotMatchThenMatchThenOnlySecondDelegateInvoked() {
|
||||||
|
RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> notMatchDelegate = new RequestMatcherEntry<>(
|
||||||
|
this.alwaysDeny, Collections.singletonList(TestWebInvocationPrivilegeEvaluator.alwaysAllow()));
|
||||||
|
RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> matchDelegate = new RequestMatcherEntry<>(
|
||||||
|
this.alwaysMatch, Collections.singletonList(TestWebInvocationPrivilegeEvaluator.alwaysAllow()));
|
||||||
|
RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> spyNotMatchDelegate = spy(notMatchDelegate);
|
||||||
|
RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> spyMatchDelegate = spy(matchDelegate);
|
||||||
|
|
||||||
|
RequestMatcherDelegatingWebInvocationPrivilegeEvaluator delegating = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
|
||||||
|
Arrays.asList(notMatchDelegate, spyMatchDelegate));
|
||||||
|
assertThat(delegating.isAllowed(this.uri, this.authentication)).isTrue();
|
||||||
|
verify(spyNotMatchDelegate.getRequestMatcher()).matches(any());
|
||||||
|
verify(spyNotMatchDelegate, never()).getEntry();
|
||||||
|
verify(spyMatchDelegate.getRequestMatcher()).matches(any());
|
||||||
|
verify(spyMatchDelegate, times(2)).getEntry(); // 2 times, one for constructor and
|
||||||
|
// other one in isAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isAllowedWhenDelegatePrivilegeEvaluatorsEmptyThenAllowedTrue() {
|
||||||
|
RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate = new RequestMatcherEntry<>(
|
||||||
|
this.alwaysMatch, Collections.emptyList());
|
||||||
|
RequestMatcherDelegatingWebInvocationPrivilegeEvaluator delegating = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
|
||||||
|
Collections.singletonList(delegate));
|
||||||
|
assertThat(delegating.isAllowed(this.uri, this.authentication)).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isAllowedWhenFirstDelegateDenyThenDoNotInvokeOthers() {
|
||||||
|
WebInvocationPrivilegeEvaluator deny = TestWebInvocationPrivilegeEvaluator.alwaysDeny();
|
||||||
|
WebInvocationPrivilegeEvaluator allow = TestWebInvocationPrivilegeEvaluator.alwaysAllow();
|
||||||
|
WebInvocationPrivilegeEvaluator spyDeny = spy(deny);
|
||||||
|
WebInvocationPrivilegeEvaluator spyAllow = spy(allow);
|
||||||
|
RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate = new RequestMatcherEntry<>(
|
||||||
|
this.alwaysMatch, Arrays.asList(spyDeny, spyAllow));
|
||||||
|
|
||||||
|
RequestMatcherDelegatingWebInvocationPrivilegeEvaluator delegating = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
|
||||||
|
Collections.singletonList(delegate));
|
||||||
|
|
||||||
|
assertThat(delegating.isAllowed(this.uri, this.authentication)).isFalse();
|
||||||
|
verify(spyDeny).isAllowed(any(), any());
|
||||||
|
verifyNoInteractions(spyAllow);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void isAllowedWhenDifferentArgumentsThenCallSpecificIsAllowedInDelegate() {
|
||||||
|
WebInvocationPrivilegeEvaluator deny = TestWebInvocationPrivilegeEvaluator.alwaysDeny();
|
||||||
|
WebInvocationPrivilegeEvaluator spyDeny = spy(deny);
|
||||||
|
RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate = new RequestMatcherEntry<>(
|
||||||
|
this.alwaysMatch, Collections.singletonList(spyDeny));
|
||||||
|
|
||||||
|
RequestMatcherDelegatingWebInvocationPrivilegeEvaluator delegating = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
|
||||||
|
Collections.singletonList(delegate));
|
||||||
|
|
||||||
|
assertThat(delegating.isAllowed(this.uri, this.authentication)).isFalse();
|
||||||
|
assertThat(delegating.isAllowed("/cp", this.uri, "GET", this.authentication)).isFalse();
|
||||||
|
verify(spyDeny).isAllowed(any(), any());
|
||||||
|
verify(spyDeny).isAllowed(any(), any(), any(), any());
|
||||||
|
verifyNoMoreInteractions(spyDeny);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void constructorWhenPrivilegeEvaluatorsNullThenException() {
|
||||||
|
RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> entry = new RequestMatcherEntry<>(this.alwaysMatch,
|
||||||
|
null);
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(
|
||||||
|
() -> new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(Collections.singletonList(entry)))
|
||||||
|
.withMessageContaining("webInvocationPrivilegeEvaluators cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void constructorWhenRequestMatcherNullThenException() {
|
||||||
|
RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> entry = new RequestMatcherEntry<>(null,
|
||||||
|
Collections.singletonList(mock(WebInvocationPrivilegeEvaluator.class)));
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(
|
||||||
|
() -> new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(Collections.singletonList(entry)))
|
||||||
|
.withMessageContaining("requestMatcher cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2021 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;
|
||||||
|
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
|
public final class TestWebInvocationPrivilegeEvaluator {
|
||||||
|
|
||||||
|
private static final AlwaysAllowWebInvocationPrivilegeEvaluator ALWAYS_ALLOW = new AlwaysAllowWebInvocationPrivilegeEvaluator();
|
||||||
|
|
||||||
|
private static final AlwaysDenyWebInvocationPrivilegeEvaluator ALWAYS_DENY = new AlwaysDenyWebInvocationPrivilegeEvaluator();
|
||||||
|
|
||||||
|
private TestWebInvocationPrivilegeEvaluator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WebInvocationPrivilegeEvaluator alwaysAllow() {
|
||||||
|
return ALWAYS_ALLOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WebInvocationPrivilegeEvaluator alwaysDeny() {
|
||||||
|
return ALWAYS_DENY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AlwaysAllowWebInvocationPrivilegeEvaluator implements WebInvocationPrivilegeEvaluator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(String uri, Authentication authentication) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AlwaysDenyWebInvocationPrivilegeEvaluator implements WebInvocationPrivilegeEvaluator {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(String uri, Authentication authentication) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2020 the original author or authors.
|
* Copyright 2002-2021 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.
|
||||||
|
@ -125,4 +125,11 @@ public class AuthorizationFilterTests {
|
||||||
verifyNoInteractions(mockFilterChain);
|
verifyNoInteractions(mockFilterChain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getAuthorizationManager() {
|
||||||
|
AuthorizationManager<HttpServletRequest> authorizationManager = mock(AuthorizationManager.class);
|
||||||
|
AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);
|
||||||
|
assertThat(authorizationFilter.getAuthorizationManager()).isSameAs(authorizationManager);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue