1
0
mirror of synced 2026-07-08 12:20:02 +00:00

Compare commits

...

25 Commits

Author SHA1 Message Date
Eleftheria Stein f2fc2a593f Release 5.5.5 2022-02-21 12:36:32 +01:00
Eleftheria Stein f491cc8938 Update spring-ldap-core to 2.3.6.RELEASE
Closes gh-10863
2022-02-21 12:24:40 +01:00
Eleftheria Stein 22a39545b9 Update org.springframework.data to 2021.0.9
Closes gh-10862
2022-02-21 12:24:38 +01:00
Eleftheria Stein 36d57b597e Update org.springframework to 5.3.16
Closes gh-10861
2022-02-21 12:24:36 +01:00
Eleftheria Stein 02c6b04320 Update org.slf4j to 1.7.36
Closes gh-10860
2022-02-21 12:24:33 +01:00
Eleftheria Stein 2c3522e290 Update org.eclipse.jetty to 9.4.45.v20220203
Closes gh-10859
2022-02-21 12:24:31 +01:00
Eleftheria Stein 01e37feb0d Update org.aspectj to 1.9.8
Closes gh-10858
2022-02-21 12:24:29 +01:00
Eleftheria Stein 6403cba2f5 Update io.spring.javaformat to 0.0.31
Closes gh-10857
2022-02-21 12:24:26 +01:00
Eleftheria Stein d655deb718 Update r2dbc-h2 to 0.8.5.RELEASE
Closes gh-10856
2022-02-21 12:24:24 +01:00
Eleftheria Stein 6156f29c3b Update io.projectreactor to 2020.0.16
Closes gh-10854
2022-02-21 12:24:19 +01:00
Eleftheria Stein 25363ac13e Update logback-classic to 1.2.10
Closes gh-10851
2022-02-21 12:24:12 +01:00
Rob Winch 15d902322f Add .sdkmanrc 2022-02-18 13:49:09 -06:00
Talerngpong Virojwutikul 070b67a9a1 add Kotlin examples for Spring Data Integration of servlet application 2022-02-18 08:50:06 -03:00
giger85 304ef2302c Replace StringUtils class completely
Issue gh-9925
Closes gh-10805
2022-02-10 18:10:53 +01:00
Josh Cummings a09f6e15ad Polish ignoring() log messaging
- Public API remains unchanged

Issue gh-9334
2022-02-07 15:22:49 -07:00
Manuel Jordan 7e0302be5c Print ignore message DefaultSecurityFilterChain
When either `web.ignoring().mvcMatchers(...)` or
`web.ignoring().antMatchers(...)` methods are used, for all their
variations, the DefaultSecurityFilterChain class now indicates
correctly through its ouput what paths are ignored according the
`ignoring()` settings.

Closes gh-9334
2022-02-07 15:22:49 -07:00
Eleftheria Stein 7554ee8fec Fix typo in getting started docs
Closes gh-10736
2022-02-02 11:04:46 +01:00
Marcus Da Coregio 893b651aea RequestMatcherDelegatingWebInvocationPrivilegeEvaluator doesn't provided access to the ServletContext
Closes gh-10779
2022-01-31 09:57:34 -03:00
Josh Cummings ca353d6781 Use noNullElements
Collection#contains(null) does not work for all collection types

Closes gh-10703
2022-01-14 15:19:13 -07:00
Marcus Da Coregio a763382c3e Make source code compatible with JDK 8
Closes gh-10695
2022-01-12 17:26:25 -03:00
Marcus Da Coregio 60595f2801 Fix @since tag
Issue gh-10590, gh-10554
2022-01-06 13:22:58 -03:00
Marcus Da Coregio ba810e468f Configure WebInvocationPrivilegeEvaluator bean for multiple filter chains
Closes gh-10554
2022-01-05 14:01:57 -03:00
Marcus Da Coregio 40dfe8f259 Add RequestMatcherEntry 2022-01-05 14:00:47 -03:00
Marcus Da Coregio b448954f43 Introduce AuthorizationManagerWebInvocationPrivilegeEvaluator
Closes gh-10590
2022-01-05 13:57:36 -03:00
Marcus Da Coregio b0c7d7703b Next development version 2021-12-20 11:02:26 -03:00
32 changed files with 1006 additions and 58 deletions
+6
View File
@@ -0,0 +1,6 @@
# Use sdkman to run "sdk env" to initialize with correct JDK version
# Enable auto-env through the sdkman_auto_env config
# See https://sdkman.io/usage#config
# A summary is to add the following to ~/.sdkman/etc/config
# sdkman_auto_env=true
java=11.0.14-tem
+2
View File
@@ -106,6 +106,8 @@ subprojects {
}
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
options.compilerArgs.add("-parameters")
options.release = 8
}
}
+1 -1
View File
@@ -72,7 +72,7 @@ dependencies {
implementation localGroovy()
implementation 'io.github.gradle-nexus:publish-plugin:1.1.0'
implementation 'io.projectreactor:reactor-core:3.4.13'
implementation 'io.projectreactor:reactor-core:3.4.15'
implementation 'gradle.plugin.org.gretty:gretty:3.0.1'
implementation 'com.apollographql.apollo:apollo-runtime:2.4.5'
implementation 'com.github.ben-manes:gradle-versions-plugin:0.38.0'
@@ -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");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
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.expression.SecurityExpressionHandler;
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.ObjectPostProcessor;
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.FilterInvocation;
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.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
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.debug.DebugFilter;
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.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
import org.springframework.util.Assert;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.filter.DelegatingFilterProxy;
/**
@@ -81,7 +88,7 @@ import org.springframework.web.filter.DelegatingFilterProxy;
* @see WebSecurityConfiguration
*/
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
implements SecurityBuilder<Filter>, ApplicationContextAware {
implements SecurityBuilder<Filter>, ApplicationContextAware, ServletContextAware {
private final Log logger = LogFactory.getLog(getClass());
@@ -108,6 +115,8 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
private Runnable postBuildAction = () -> {
};
private ServletContext servletContext;
/**
* Creates a new instance
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
@@ -252,6 +261,8 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
* {@link WebSecurityConfigurerAdapter}.
* @param securityInterceptor the {@link FilterSecurityInterceptor} to use
* @return the {@link WebSecurity} for further customizations
* @deprecated Use {@link #privilegeEvaluator(WebInvocationPrivilegeEvaluator)}
* instead
*/
public WebSecurity securityInterceptor(FilterSecurityInterceptor securityInterceptor) {
this.filterSecurityInterceptor = securityInterceptor;
@@ -278,11 +289,24 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
+ ".addSecurityFilterChainBuilder directly");
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
WebSecurity.this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest
+ ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");
SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
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);
if (this.httpFirewall != null) {
@@ -306,6 +330,26 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
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
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.defaultWebSecurityExpressionHandler.setApplicationContext(applicationContext);
@@ -333,6 +377,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
* {@link MvcRequestMatcher#setMethod(HttpMethod)}
@@ -128,8 +128,8 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
}
/**
* Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary for the JSP
* tag support.
* Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary to evaluate
* privileges for a given web URI
* @return the {@link WebInvocationPrivilegeEvaluator}
*/
@Bean
@@ -276,8 +276,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
if (version != null) {
return version;
}
return Version.class.getModule().getDescriptor().version().map(Object::toString)
.orElseThrow(() -> new IllegalStateException("cannot determine OpenSAML version"));
return Version.getVersion();
}
private void registerDefaultAuthenticationProvider(B http) {
@@ -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");
* 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.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
@@ -61,7 +62,7 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
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.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.test.web.servlet.MockMvc;
@@ -83,6 +84,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Rob Winch
* @author Joe Grandja
* @author Evgeniy Cheban
* @author Marcus Da Coregio
*/
public class WebSecurityConfigurationTests {
@@ -218,10 +220,10 @@ public class WebSecurityConfigurationTests {
}
@Test
public void loadConfigWhenDefaultWebInvocationPrivilegeEvaluatorThenDefaultIsRegistered() {
public void loadConfigWhenDefaultWebInvocationPrivilegeEvaluatorThenRequestMatcherIsRegistered() {
this.spring.register(WebInvocationPrivilegeEvaluatorDefaultsConfig.class).autowire();
assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
.isInstanceOf(DefaultWebInvocationPrivilegeEvaluator.class);
.isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class);
}
@Test
@@ -229,7 +231,7 @@ public class WebSecurityConfigurationTests {
this.spring.register(AuthorizeRequestsFilterChainConfig.class).autowire();
assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
.isInstanceOf(DefaultWebInvocationPrivilegeEvaluator.class);
.isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class);
}
// SEC-2303
@@ -375,6 +377,69 @@ public class WebSecurityConfigurationTests {
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
@Import(AuthenticationTestConfiguration.class)
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();
}
}
}
@@ -21,11 +21,13 @@ import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.security.auth.x500.X500Principal;
import javax.servlet.http.HttpServletRequest;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.junit.Rule;
import org.junit.Test;
import sun.security.x509.X500Name;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
@@ -239,12 +241,8 @@ public class NamespaceHttpX509Tests {
}
private String extractCommonName(X509Certificate certificate) {
try {
return ((X500Name) certificate.getSubjectDN()).getCommonName();
}
catch (Exception ex) {
throw new IllegalArgumentException(ex);
}
X500Principal principal = certificate.getSubjectX500Principal();
return new X500Name(principal.getName()).getRDNs(BCStyle.CN)[0].getFirst().getValue().toString();
}
}
+9 -9
View File
@@ -8,14 +8,14 @@ javaPlatform {
dependencies {
api platform("org.springframework:spring-framework-bom:$springFrameworkVersion")
api platform("io.projectreactor:reactor-bom:2020.0.14")
api platform("io.projectreactor:reactor-bom:2020.0.16")
api platform("io.rsocket:rsocket-bom:1.1.1")
api platform("org.springframework.data:spring-data-bom:2021.0.7")
api platform("org.springframework.data:spring-data-bom:2021.0.9")
api platform("org.jetbrains.kotlin:kotlin-bom:$kotlinVersion")
api platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2")
api platform("com.fasterxml.jackson:jackson-bom:2.12.6")
constraints {
api "ch.qos.logback:logback-classic:1.2.9"
api "ch.qos.logback:logback-classic:1.2.10"
api "com.google.inject:guice:3.0"
api "com.nimbusds:nimbus-jose-jwt:9.10.1"
api "com.nimbusds:oauth2-oidc-sdk:9.9.1"
@@ -49,8 +49,8 @@ dependencies {
api "org.assertj:assertj-core:3.19.0"
api "org.bouncycastle:bcpkix-jdk15on:1.68"
api "org.bouncycastle:bcprov-jdk15on:1.68"
api "org.eclipse.jetty:jetty-server:9.4.44.v20210927"
api "org.eclipse.jetty:jetty-servlet:9.4.44.v20210927"
api "org.eclipse.jetty:jetty-server:9.4.45.v20220203"
api "org.eclipse.jetty:jetty-servlet:9.4.45.v20220203"
api "org.eclipse.persistence:javax.persistence:2.2.1"
api "org.hibernate:hibernate-entitymanager:5.4.33"
api "org.hsqldb:hsqldb:2.6.1"
@@ -71,10 +71,10 @@ dependencies {
api "org.seleniumhq.selenium:selenium-java:3.141.59"
api "org.seleniumhq.selenium:selenium-support:3.141.59"
api "org.skyscreamer:jsonassert:1.5.0"
api "org.slf4j:jcl-over-slf4j:1.7.32"
api "org.slf4j:log4j-over-slf4j:1.7.32"
api "org.slf4j:slf4j-api:1.7.32"
api "org.springframework.ldap:spring-ldap-core:2.3.5.RELEASE"
api "org.slf4j:jcl-over-slf4j:1.7.36"
api "org.slf4j:log4j-over-slf4j:1.7.36"
api "org.slf4j:slf4j-api:1.7.36"
api "org.springframework.ldap:spring-ldap-core:2.3.6.RELEASE"
api "org.synchronoss.cloud:nio-multipart-parser:1.1.0"
}
}
@@ -53,7 +53,7 @@ If you wish to override the Spring Security version, you may do so by providing
<properties>
<!-- ... -->
<spring-security.version>{spring-security-version}</spring-security.version>
</dependencies>
</properties>
----
====
@@ -68,7 +68,7 @@ You can do so by adding a Maven property, as the following example shows:
<properties>
<!-- ... -->
<spring.version>{spring-core-version}</spring.version>
</dependencies>
</properties>
----
====
@@ -7,10 +7,11 @@ It is not only useful but necessary to include the user in the queries to suppor
[[data-configuration]]
=== Spring Data & Spring Security Configuration
To use this support, add `org.springframework.security:spring-security-data` dependency and provide a bean of type `SecurityEvaluationContextExtension`.
In Java Configuration, this would look like:
To use this support, add `org.springframework.security:spring-security-data` dependency and provide a bean of type `SecurityEvaluationContextExtension`:
[source,java]
====
.Java
[source,java,role="primary"]
----
@Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
@@ -18,6 +19,16 @@ public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
fun securityEvaluationContextExtension(): SecurityEvaluationContextExtension {
return SecurityEvaluationContextExtension()
}
----
====
In XML Configuration, this would look like:
[source,xml]
@@ -31,7 +42,9 @@ In XML Configuration, this would look like:
Now Spring Security can be used within your queries.
For example:
[source,java]
====
.Java
[source,java,role="primary"]
----
@Repository
public interface MessageRepository extends PagingAndSortingRepository<Message,Long> {
@@ -40,6 +53,17 @@ public interface MessageRepository extends PagingAndSortingRepository<Message,Lo
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Repository
interface MessageRepository : PagingAndSortingRepository<Message,Long> {
@Query("select m from Message m where m.to.id = ?#{ principal?.id }")
fun findInbox(pageable: Pageable): Page<Message>
}
----
====
This checks to see if the `Authentication.getPrincipal().getId()` is equal to the recipient of the `Message`.
Note that this example assumes you have customized the principal to be an Object that has an id property.
By exposing the `SecurityEvaluationContextExtension` bean, all of the <<common-expressions,Common Security Expressions>> are available within the Query.
+4 -4
View File
@@ -1,9 +1,9 @@
aspectjVersion=1.9.7
springJavaformatVersion=0.0.29
aspectjVersion=1.9.8
springJavaformatVersion=0.0.31
springBootVersion=2.4.2
springFrameworkVersion=5.3.14
springFrameworkVersion=5.3.16
openSamlVersion=3.4.6
version=5.5.4
version=5.5.5
kotlinVersion=1.5.32
samplesBranch=5.5.x
org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError
@@ -24,7 +24,7 @@ dependencies {
testImplementation 'io.projectreactor:reactor-test'
testImplementation 'io.projectreactor.tools:blockhound'
testImplementation 'org.skyscreamer:jsonassert'
testImplementation 'io.r2dbc:r2dbc-h2:0.8.4.RELEASE'
testImplementation 'io.r2dbc:r2dbc-h2:0.8.5.RELEASE'
testImplementation 'io.r2dbc:r2dbc-spi-test:0.8.6.RELEASE'
testRuntimeOnly 'org.hsqldb:hsqldb'
@@ -41,8 +41,6 @@ import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.nimbusds.oauth2.sdk.util.StringUtils;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
@@ -102,6 +100,7 @@ import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
@@ -1206,7 +1205,7 @@ public final class SecurityMockMvcRequestPostProcessors {
return getAuthorities((Collection) scope);
}
String scopes = scope.toString();
if (StringUtils.isBlank(scopes)) {
if (!StringUtils.hasText(scopes)) {
return Collections.emptyList();
}
return getAuthorities(Arrays.asList(scopes.split(" ")));
@@ -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");
* you may not use this file except in compliance with the License.
@@ -48,7 +48,12 @@ public final class DefaultSecurityFilterChain implements SecurityFilterChain {
}
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
logger.info(LogMessage.format("Will secure %s with %s", requestMatcher, filters));
if (!filters.isEmpty()) {
logger.info(LogMessage.format("Will not secure %s", requestMatcher));
}
else {
logger.info(LogMessage.format("Will secure %s with %s", requestMatcher, filters));
}
this.requestMatcher = requestMatcher;
this.filters = new ArrayList<>(filters);
}
@@ -0,0 +1,57 @@
/*
* 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 javax.servlet.http.HttpServletRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
import org.springframework.util.Assert;
/**
* An implementation of {@link WebInvocationPrivilegeEvaluator} which delegates the checks
* to an instance of {@link AuthorizationManager}
*
* @author Marcus Da Coregio
* @since 5.5.5
*/
public final class AuthorizationManagerWebInvocationPrivilegeEvaluator implements WebInvocationPrivilegeEvaluator {
private final AuthorizationManager<HttpServletRequest> authorizationManager;
public AuthorizationManagerWebInvocationPrivilegeEvaluator(
AuthorizationManager<HttpServletRequest> authorizationManager) {
Assert.notNull(authorizationManager, "authorizationManager cannot be null");
this.authorizationManager = authorizationManager;
}
@Override
public boolean isAllowed(String uri, Authentication authentication) {
return isAllowed(null, uri, null, authentication);
}
@Override
public boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {
FilterInvocation filterInvocation = new FilterInvocation(contextPath, uri, method);
AuthorizationDecision decision = this.authorizationManager.check(() -> authentication,
filterInvocation.getHttpRequest());
return decision != null && decision.isGranted();
}
}
@@ -0,0 +1,132 @@
/*
* Copyright 2002-2022 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 javax.servlet.ServletContext;
import javax.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;
import org.springframework.web.context.ServletContextAware;
/**
* 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.5.5
*/
public final class RequestMatcherDelegatingWebInvocationPrivilegeEvaluator
implements WebInvocationPrivilegeEvaluator, ServletContextAware {
private final List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> delegates;
private ServletContext servletContext;
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, this.servletContext);
for (RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate : this.delegates) {
if (delegate.getRequestMatcher().matches(filterInvocation.getHttpRequest())) {
return delegate.getEntry();
}
}
return Collections.emptyList();
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
}
@@ -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");
* you may not use this file except in compliance with the License.
@@ -67,4 +67,12 @@ public class AuthorizationFilter extends OncePerRequestFilter {
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");
* 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 FilterChainProxy getFilterChainProxy() {
return this.filterChainProxy;
}
static class DebugRequestWrapper extends HttpServletRequestWrapper {
private static final Logger logger = new Logger();
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2022 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.
@@ -66,7 +66,7 @@ public class MediaTypeServerWebExchangeMatcher implements ServerWebExchangeMatch
*/
public MediaTypeServerWebExchangeMatcher(Collection<MediaType> matchingMediaTypes) {
Assert.notEmpty(matchingMediaTypes, "matchingMediaTypes cannot be null");
Assert.isTrue(!matchingMediaTypes.contains(null),
Assert.noNullElements(matchingMediaTypes,
() -> "matchingMediaTypes cannot contain null. Got " + matchingMediaTypes);
this.matchingMediaTypes = matchingMediaTypes;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2022 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.
@@ -45,7 +45,7 @@ public final class AndRequestMatcher implements RequestMatcher {
*/
public AndRequestMatcher(List<RequestMatcher> requestMatchers) {
Assert.notEmpty(requestMatchers, "requestMatchers must contain a value");
Assert.isTrue(!requestMatchers.contains(null), "requestMatchers cannot contain null values");
Assert.noNullElements(requestMatchers, "requestMatchers cannot contain null values");
this.requestMatchers = requestMatchers;
}
@@ -49,6 +49,7 @@ import org.springframework.web.util.UrlPathHelper;
* @author Rob Winch
* @author Eddú Meléndez
* @author Evgeniy Cheban
* @author Manuel Jordan
* @since 3.1
* @see org.springframework.util.AntPathMatcher
*/
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2022 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.
@@ -40,7 +40,7 @@ public final class OrRequestMatcher implements RequestMatcher {
*/
public OrRequestMatcher(List<RequestMatcher> requestMatchers) {
Assert.notEmpty(requestMatchers, "requestMatchers must contain a value");
Assert.isTrue(!requestMatchers.contains(null), "requestMatchers cannot contain null values");
Assert.noNullElements(requestMatchers, "requestMatchers cannot contain null values");
this.requestMatchers = requestMatchers;
}
@@ -0,0 +1,44 @@
/*
* 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.util.matcher;
/**
* A rich object for associating a {@link RequestMatcher} to another object.
*
* @author Marcus Da Coregio
* @since 5.5.5
*/
public class RequestMatcherEntry<T> {
private final RequestMatcher requestMatcher;
private final T entry;
public RequestMatcherEntry(RequestMatcher requestMatcher, T entry) {
this.requestMatcher = requestMatcher;
this.entry = entry;
}
public RequestMatcher getRequestMatcher() {
return this.requestMatcher;
}
public T getEntry() {
return this.entry;
}
}
@@ -0,0 +1,68 @@
/*
* 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 javax.servlet.http.HttpServletRequest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationManager;
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.verify;
@RunWith(MockitoJUnitRunner.class)
public class AuthorizationManagerWebInvocationPrivilegeEvaluatorTests {
@InjectMocks
private AuthorizationManagerWebInvocationPrivilegeEvaluator privilegeEvaluator;
@Mock
private AuthorizationManager<HttpServletRequest> authorizationManager;
@Test
public void constructorWhenAuthorizationManagerNullThenIllegalArgument() {
assertThatIllegalArgumentException()
.isThrownBy(() -> new AuthorizationManagerWebInvocationPrivilegeEvaluator(null))
.withMessage("authorizationManager cannot be null");
}
@Test
public void isAllowedWhenAuthorizationManagerAllowsThenAllowedTrue() {
given(this.authorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(true));
boolean allowed = this.privilegeEvaluator.isAllowed("/test", TestAuthentication.authenticatedUser());
assertThat(allowed).isTrue();
verify(this.authorizationManager).check(any(), any());
}
@Test
public void isAllowedWhenAuthorizationManagerDeniesAllowedFalse() {
given(this.authorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(false));
boolean allowed = this.privilegeEvaluator.isAllowed("/test", TestAuthentication.authenticatedUser());
assertThat(allowed).isFalse();
}
}
@@ -0,0 +1,200 @@
/*
* Copyright 2002-2022 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 javax.servlet.http.HttpServletRequest;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.mock.web.MockServletContext;
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
*/
public 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");
@Before
public void setup() {
given(this.alwaysMatch.matches(any())).willReturn(true);
given(this.alwaysDeny.matches(any())).willReturn(false);
}
@Test
public void isAllowedWhenDelegatesEmptyThenAllowed() {
RequestMatcherDelegatingWebInvocationPrivilegeEvaluator delegating = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
Collections.emptyList());
assertThat(delegating.isAllowed(this.uri, this.authentication)).isTrue();
}
@Test
public 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
public 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
public 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
public 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
public 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
public 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
public 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
public void isAllowedWhenServletContextIsSetThenPassedFilterInvocationHttpServletRequestHasServletContext() {
Authentication token = new TestingAuthenticationToken("test", "Password", "MOCK_INDEX");
MockServletContext servletContext = new MockServletContext();
ArgumentCaptor<HttpServletRequest> argumentCaptor = ArgumentCaptor.forClass(HttpServletRequest.class);
RequestMatcher requestMatcher = mock(RequestMatcher.class);
WebInvocationPrivilegeEvaluator wipe = mock(WebInvocationPrivilegeEvaluator.class);
RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> delegate = new RequestMatcherEntry<>(requestMatcher,
Collections.singletonList(wipe));
RequestMatcherDelegatingWebInvocationPrivilegeEvaluator requestMatcherWipe = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
Collections.singletonList(delegate));
requestMatcherWipe.setServletContext(servletContext);
requestMatcherWipe.isAllowed("/foo/index.jsp", token);
verify(requestMatcher).matches(argumentCaptor.capture());
assertThat(argumentCaptor.getValue().getServletContext()).isNotNull();
}
@Test
public void constructorWhenPrivilegeEvaluatorsNullThenException() {
RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> entry = new RequestMatcherEntry<>(this.alwaysMatch,
null);
assertThatIllegalArgumentException().isThrownBy(
() -> new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(Collections.singletonList(entry)))
.withMessageContaining("webInvocationPrivilegeEvaluators cannot be null");
}
@Test
public 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");
* you may not use this file except in compliance with the License.
@@ -125,4 +125,11 @@ public class AuthorizationFilterTests {
verifyNoInteractions(mockFilterChain);
}
@Test
public void getAuthorizationManager() {
AuthorizationManager<HttpServletRequest> authorizationManager = mock(AuthorizationManager.class);
AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);
assertThat(authorizationFilter.getAuthorizationManager()).isSameAs(authorizationManager);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2022 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.
@@ -16,6 +16,8 @@
package org.springframework.security.web.server.util.matcher;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -43,6 +45,21 @@ public class MediaTypeServerWebExchangeMatcherTests {
assertThatIllegalArgumentException().isThrownBy(() -> new MediaTypeServerWebExchangeMatcher(types));
}
// gh-10703
@Test
public void constructorListOfDoesNotThrowNullPointerException() {
List<MediaType> mediaTypes = new ArrayList<MediaType>(Arrays.asList(MediaType.ALL)) {
@Override
public boolean contains(Object o) {
if (o == null) {
throw new NullPointerException();
}
return super.contains(o);
}
};
new MediaTypeServerWebExchangeMatcher(mediaTypes);
}
@Test
public void constructorMediaTypeArrayWhenContainsNullThenThrowsIllegalArgumentException() {
MediaType[] types = { null };
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2022 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.
@@ -16,6 +16,7 @@
package org.springframework.security.web.util.matcher;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -55,6 +56,22 @@ public class AndRequestMatcherTests {
assertThatNullPointerException().isThrownBy(() -> new AndRequestMatcher((RequestMatcher[]) null));
}
// gh-10703
@Test
public void constructorListOfDoesNotThrowNullPointer() {
List<RequestMatcher> requestMatchers = new ArrayList<RequestMatcher>(
Arrays.asList(AnyRequestMatcher.INSTANCE)) {
@Override
public boolean contains(Object o) {
if (o == null) {
throw new NullPointerException();
}
return super.contains(o);
}
};
new AndRequestMatcher(requestMatchers);
}
@Test
public void constructorArrayContainsNull() {
assertThatIllegalArgumentException().isThrownBy(() -> new AndRequestMatcher((RequestMatcher) null));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2022 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.
@@ -16,6 +16,7 @@
package org.springframework.security.web.util.matcher;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -55,6 +56,23 @@ public class OrRequestMatcherTests {
assertThatNullPointerException().isThrownBy(() -> new OrRequestMatcher((RequestMatcher[]) null));
}
// gh-10703
@Test
public void constructorListOfDoesNotThrowNullPointer() {
// emulate List.of for pre-JDK 9 builds
List<RequestMatcher> requestMatchers = new ArrayList<RequestMatcher>(
Arrays.asList(AnyRequestMatcher.INSTANCE)) {
@Override
public boolean contains(Object o) {
if (o == null) {
throw new NullPointerException();
}
return super.contains(o);
}
};
new OrRequestMatcher(requestMatchers);
}
@Test
public void constructorArrayContainsNull() {
assertThatIllegalArgumentException().isThrownBy(() -> new OrRequestMatcher((RequestMatcher) null));
@@ -0,0 +1,41 @@
/*
* 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.util.matcher;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
public class RequestMatcherEntryTests {
@Test
public void constructWhenGetRequestMatcherAndEntryThenSameRequestMatcherAndEntry() {
RequestMatcher requestMatcher = mock(RequestMatcher.class);
RequestMatcherEntry<String> entry = new RequestMatcherEntry<>(requestMatcher, "entry");
assertThat(entry.getRequestMatcher()).isSameAs(requestMatcher);
assertThat(entry.getEntry()).isEqualTo("entry");
}
@Test
public void constructWhenNullValuesThenNullValues() {
RequestMatcherEntry<String> entry = new RequestMatcherEntry<>(null, null);
assertThat(entry.getRequestMatcher()).isNull();
assertThat(entry.getEntry()).isNull();
}
}