Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa4c5449e7 | |||
| 2a3845a7ed | |||
| ff96a7b5ad | |||
| c37ff42234 | |||
| 0a00d84838 | |||
| 96c6751a1d | |||
| 8ee9c32788 | |||
| 7a5fb9eaf7 | |||
| 8cbb972cef | |||
| 539a11d0a4 | |||
| e97c5a533b | |||
| e0fa644b08 | |||
| 592db9180d | |||
| aca3fc2412 | |||
| 07f9afe057 | |||
| d7077b441a | |||
| 101f11ba94 | |||
| 18b903f6e3 | |||
| 038266a94f | |||
| cf559ab224 | |||
| 649428b49a | |||
| 0355e960d7 | |||
| fdad14af63 | |||
| 077c9e0b3e | |||
| dc648cf79f |
@@ -0,0 +1,26 @@
|
||||
name: Backport Bot
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
push:
|
||||
branches:
|
||||
- '*.x'
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
- name: Download BackportBot
|
||||
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
|
||||
- name: Backport
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_EVENT: ${{ toJSON(github.event) }}
|
||||
run: java -jar backport-bot-0.0.1-SNAPSHOT.jar --github.accessToken="$GITHUB_TOKEN" --github.event_name "$GITHUB_EVENT_NAME" --github.event "$GITHUB_EVENT"
|
||||
@@ -73,8 +73,8 @@ import org.springframework.util.Assert;
|
||||
* one in <tt>lookupObjectIdentities</tt>. These are built from the same select and "order
|
||||
* by" clause, using a different where clause in each case. In order to use custom schema
|
||||
* or column names, each of these SQL clauses can be customized, but they must be
|
||||
* consistent with each other and with the expected result set generated by the the
|
||||
* default values.
|
||||
* consistent with each other and with the expected result set generated by the default
|
||||
* values.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
|
||||
@@ -95,6 +95,18 @@ updateDependenciesSettings {
|
||||
selection.reject("org.opensaml maintains two different versions, so it must be updated manually");
|
||||
}
|
||||
}
|
||||
components.withModule("io.spring.javaformat:spring-javaformat-gradle-plugin") { selection ->
|
||||
ModuleComponentIdentifier candidate = selection.getCandidate();
|
||||
if (!candidate.getVersion().equals(selection.getCurrentVersion())) {
|
||||
selection.reject("spring-javaformat-gradle-plugin updates break checkstyle");
|
||||
}
|
||||
}
|
||||
components.withModule("io.spring.javaformat:spring-javaformat-checkstyle") { selection ->
|
||||
ModuleComponentIdentifier candidate = selection.getCandidate();
|
||||
if (!candidate.getVersion().equals(selection.getCurrentVersion())) {
|
||||
selection.reject("spring-javaformat-checkstyle updates break checkstyle");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ dependencies {
|
||||
implementation localGroovy()
|
||||
|
||||
implementation 'io.github.gradle-nexus:publish-plugin:1.1.0'
|
||||
implementation 'io.projectreactor:reactor-core:3.4.18'
|
||||
implementation 'io.projectreactor:reactor-core:3.4.19'
|
||||
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
-1
@@ -253,7 +253,7 @@ public class Saml2LoginConfigurerTests {
|
||||
public void authenticateWithInvalidDeflatedSAMLResponseThenFailureHandlerUses() throws Exception {
|
||||
this.spring.register(CustomAuthenticationFailureHandler.class).autowire();
|
||||
byte[] invalidDeflated = "invalid".getBytes();
|
||||
String encoded = Saml2Utils.samlEncodeNotRfc2045(invalidDeflated);
|
||||
String encoded = Saml2Utils.samlEncode(invalidDeflated);
|
||||
MockHttpServletRequestBuilder request = get("/login/saml2/sso/registration-id").queryParam("SAMLResponse",
|
||||
encoded);
|
||||
this.mvc.perform(request);
|
||||
|
||||
+22
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -17,7 +17,13 @@
|
||||
package org.springframework.security.data.repository.query;
|
||||
|
||||
import org.springframework.data.spel.spi.EvaluationContextExtension;
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.expression.DenyAllPermissionEvaluator;
|
||||
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
||||
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
@@ -77,12 +83,21 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||
* it.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Evgeniy Cheban
|
||||
* @since 4.0
|
||||
*/
|
||||
public class SecurityEvaluationContextExtension implements EvaluationContextExtension {
|
||||
|
||||
private Authentication authentication;
|
||||
|
||||
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
||||
|
||||
private RoleHierarchy roleHierarchy = new NullRoleHierarchy();
|
||||
|
||||
private PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();
|
||||
|
||||
private String defaultRolePrefix = "ROLE_";
|
||||
|
||||
/**
|
||||
* Creates a new instance that uses the current {@link Authentication} found on the
|
||||
* {@link org.springframework.security.core.context.SecurityContextHolder}.
|
||||
@@ -106,8 +121,13 @@ public class SecurityEvaluationContextExtension implements EvaluationContextExte
|
||||
@Override
|
||||
public SecurityExpressionRoot getRootObject() {
|
||||
Authentication authentication = getAuthentication();
|
||||
return new SecurityExpressionRoot(authentication) {
|
||||
SecurityExpressionRoot root = new SecurityExpressionRoot(authentication) {
|
||||
};
|
||||
root.setTrustResolver(this.trustResolver);
|
||||
root.setRoleHierarchy(this.roleHierarchy);
|
||||
root.setPermissionEvaluator(this.permissionEvaluator);
|
||||
root.setDefaultRolePrefix(this.defaultRolePrefix);
|
||||
return root;
|
||||
}
|
||||
|
||||
private Authentication getAuthentication() {
|
||||
|
||||
+15
-1
@@ -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.
|
||||
@@ -20,7 +20,10 @@ import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.security.access.expression.DenyAllPermissionEvaluator;
|
||||
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
||||
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
@@ -69,6 +72,17 @@ public class SecurityEvaluationContextExtensionTests {
|
||||
assertThat(getRoot().getAuthentication()).isSameAs(explicit);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getRootObjectWhenAdditionalFieldsNotSetThenVerifyDefaults() {
|
||||
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "ROLE_EXPLICIT");
|
||||
this.securityExtension = new SecurityEvaluationContextExtension(explicit);
|
||||
SecurityExpressionRoot root = getRoot();
|
||||
assertThat(root).extracting("trustResolver").isInstanceOf(AuthenticationTrustResolverImpl.class);
|
||||
assertThat(root).extracting("roleHierarchy").isInstanceOf(NullRoleHierarchy.class);
|
||||
assertThat(root).extracting("permissionEvaluator").isInstanceOf(DenyAllPermissionEvaluator.class);
|
||||
assertThat(root).extracting("defaultRolePrefix").isEqualTo("ROLE_");
|
||||
}
|
||||
|
||||
private SecurityExpressionRoot getRoot() {
|
||||
return this.securityExtension.getRootObject();
|
||||
}
|
||||
|
||||
+5
-5
@@ -8,13 +8,13 @@ javaPlatform {
|
||||
|
||||
dependencies {
|
||||
api platform("org.springframework:spring-framework-bom:$springFrameworkVersion")
|
||||
api platform("io.projectreactor:reactor-bom:2020.0.19")
|
||||
api platform("io.projectreactor:reactor-bom:2020.0.20")
|
||||
api platform("io.rsocket:rsocket-bom:1.1.2")
|
||||
api platform("org.junit:junit-bom:5.8.2")
|
||||
api platform("org.springframework.data:spring-data-bom:2021.1.4")
|
||||
api platform("org.springframework.data:spring-data-bom:2021.1.5")
|
||||
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.13.2.20220328")
|
||||
api platform("com.fasterxml.jackson:jackson-bom:2.13.3")
|
||||
constraints {
|
||||
api "ch.qos.logback:logback-classic:1.2.11"
|
||||
api "com.google.inject:guice:3.0"
|
||||
@@ -54,7 +54,7 @@ dependencies {
|
||||
api "org.eclipse.jetty:jetty-servlet:9.4.46.v20220331"
|
||||
api "org.eclipse.persistence:javax.persistence:2.2.1"
|
||||
api "org.hamcrest:hamcrest:2.2"
|
||||
api "org.hibernate:hibernate-entitymanager:5.6.8.Final"
|
||||
api "org.hibernate:hibernate-entitymanager:5.6.9.Final"
|
||||
api "org.hsqldb:hsqldb:2.6.1"
|
||||
api "org.jasig.cas.client:cas-client-core:3.6.4"
|
||||
api "org.mockito:mockito-core:3.12.4"
|
||||
@@ -71,7 +71,7 @@ dependencies {
|
||||
api "org.skyscreamer:jsonassert:1.5.0"
|
||||
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.7.RELEASE"
|
||||
api "org.springframework.ldap:spring-ldap-core:2.3.8.RELEASE"
|
||||
api "org.synchronoss.cloud:nio-multipart-parser:1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
name: ROOT
|
||||
version: '5.6.4'
|
||||
version: '5.6.6'
|
||||
|
||||
@@ -23,13 +23,14 @@ To specify where the introspection endpoint is, simply do:
|
||||
|
||||
[source,yaml]
|
||||
----
|
||||
security:
|
||||
oauth2:
|
||||
resourceserver:
|
||||
opaque-token:
|
||||
introspection-uri: https://idp.example.com/introspect
|
||||
client-id: client
|
||||
client-secret: secret
|
||||
spring:
|
||||
security:
|
||||
oauth2:
|
||||
resourceserver:
|
||||
opaque-token:
|
||||
introspection-uri: https://idp.example.com/introspect
|
||||
client-id: client
|
||||
client-secret: secret
|
||||
----
|
||||
|
||||
Where `https://idp.example.com/introspect` is the introspection endpoint hosted by your authorization server and `client-id` and `client-secret` are the credentials needed to hit that endpoint.
|
||||
|
||||
@@ -8,7 +8,7 @@ Here is a minimal sample of what we can do:
|
||||
.Java
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@RunWith(SpringRunner.class)
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = HelloWebfluxMethodApplication.class)
|
||||
public class HelloWorldMessageServiceTests {
|
||||
@Autowired
|
||||
@@ -42,7 +42,7 @@ public class HelloWorldMessageServiceTests {
|
||||
.Kotlin
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@RunWith(SpringRunner::class)
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = [HelloWebfluxMethodApplication::class])
|
||||
class HelloWorldMessageServiceTests {
|
||||
@Autowired
|
||||
|
||||
@@ -4,7 +4,7 @@ The basic setup looks like this:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@RunWith(SpringRunner.class)
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = HelloWebfluxMethodApplication.class)
|
||||
public class HelloWebfluxMethodApplicationTests {
|
||||
@Autowired
|
||||
@@ -12,7 +12,7 @@ public class HelloWebfluxMethodApplicationTests {
|
||||
|
||||
WebTestClient rest;
|
||||
|
||||
@Before
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.rest = WebTestClient
|
||||
.bindToApplicationContext(this.context)
|
||||
|
||||
@@ -69,7 +69,11 @@ SecurityFilterChain web(HttpSecurity http) throws Exception {
|
||||
.authorizeHttpRequests(authorize -> authorize // <1>
|
||||
.mvcMatchers("/resources/**", "/signup", "/about").permitAll() // <2>
|
||||
.mvcMatchers("/admin/**").hasRole("ADMIN") // <3>
|
||||
.mvcMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") // <4>
|
||||
.mvcMatchers("/db/**").access((authentication, request) ->
|
||||
Optional.of(hasRole("ADMIN").check(authentication, request))
|
||||
.filter((decision) -> !decision.isGranted())
|
||||
.orElseGet(() -> hasRole("DBA").check(authentication, request));
|
||||
) // <4>
|
||||
.anyRequest().denyAll() // <5>
|
||||
);
|
||||
|
||||
|
||||
@@ -24,13 +24,14 @@ To specify where the introspection endpoint is, simply do:
|
||||
|
||||
[source,yaml]
|
||||
----
|
||||
security:
|
||||
oauth2:
|
||||
resourceserver:
|
||||
opaque-token:
|
||||
introspection-uri: https://idp.example.com/introspect
|
||||
client-id: client
|
||||
client-secret: secret
|
||||
spring:
|
||||
security:
|
||||
oauth2:
|
||||
resourceserver:
|
||||
opaque-token:
|
||||
introspection-uri: https://idp.example.com/introspect
|
||||
client-id: client
|
||||
client-secret: secret
|
||||
----
|
||||
|
||||
Where `https://idp.example.com/introspect` is the introspection endpoint hosted by your authorization server and `client-id` and `client-secret` are the credentials needed to hit that endpoint.
|
||||
|
||||
@@ -49,7 +49,7 @@ Before we can use Spring Security Test support, we must perform some setup. An e
|
||||
.Java
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@RunWith(SpringJUnit4ClassRunner.class) // <1>
|
||||
@ExtendWith(SpringExtension.class) // <1>
|
||||
@ContextConfiguration // <2>
|
||||
public class WithMockUserTests {
|
||||
----
|
||||
@@ -57,16 +57,15 @@ public class WithMockUserTests {
|
||||
.Kotlin
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@RunWith(SpringJUnit4ClassRunner::class)
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration
|
||||
class WithMockUserTests {
|
||||
----
|
||||
====
|
||||
|
||||
This is a basic example of how to setup Spring Security Test. The highlights are:
|
||||
|
||||
<1> `@RunWith` instructs the spring-test module that it should create an `ApplicationContext`. This is no different than using the existing Spring Test support. For additional information, refer to the https://docs.spring.io/spring-framework/docs/4.0.x/spring-framework-reference/htmlsingle/#integration-testing-annotations-standard[Spring Reference]
|
||||
<2> `@ContextConfiguration` instructs the spring-test the configuration to use to create the `ApplicationContext`. Since no configuration is specified, the default configuration locations will be tried. This is no different than using the existing Spring Test support. For additional information, refer to the https://docs.spring.io/spring-framework/docs/4.0.x/spring-framework-reference/htmlsingle/#testcontext-ctx-management[Spring Reference]
|
||||
<1> `@ExtendWith` instructs the spring-test module that it should create an `ApplicationContext`. For additional information, refer to the {spring-framework-reference-url}testing.html#testcontext-junit-jupiter-extension[Spring reference].
|
||||
<2> `@ContextConfiguration` instructs the spring-test the configuration to use to create the `ApplicationContext`. Since no configuration is specified, the default configuration locations will be tried. This is no different than using the existing Spring Test support. For additional information, refer to the {spring-framework-reference-url}testing.html#spring-testing-annotation-contextconfiguration[Spring Reference]
|
||||
|
||||
NOTE: Spring Security hooks into Spring Test support using the `WithSecurityContextTestExecutionListener` which will ensure our tests are ran with the correct user.
|
||||
It does this by populating the `SecurityContextHolder` prior to running our tests.
|
||||
@@ -225,7 +224,7 @@ For example, the following would run every test with a user with the username "a
|
||||
.Java
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration
|
||||
@WithMockUser(username="admin",roles={"USER","ADMIN"})
|
||||
public class WithMockUserTests {
|
||||
@@ -234,7 +233,7 @@ public class WithMockUserTests {
|
||||
.Kotlin
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@RunWith(SpringJUnit4ClassRunner::class)
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration
|
||||
@WithMockUser(username="admin",roles=["USER","ADMIN"])
|
||||
class WithMockUserTests {
|
||||
@@ -304,7 +303,7 @@ For example, the following will run withMockUser1 and withMockUser2 using <<test
|
||||
.Java
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@WithMockUser
|
||||
public class WithUserClassLevelAuthenticationTests {
|
||||
|
||||
@@ -327,7 +326,7 @@ public class WithUserClassLevelAuthenticationTests {
|
||||
.Kotlin
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@RunWith(SpringJUnit4ClassRunner::class)
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@WithMockUser
|
||||
class WithUserClassLevelAuthenticationTests {
|
||||
@Test
|
||||
|
||||
@@ -15,7 +15,7 @@ NOTE: Spring Security's testing support requires spring-test-4.1.3.RELEASE or gr
|
||||
|
||||
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = SecurityConfig.class)
|
||||
@WebAppConfiguration
|
||||
public class CsrfShowcaseTests {
|
||||
@@ -25,7 +25,7 @@ public class CsrfShowcaseTests {
|
||||
|
||||
private MockMvc mvc;
|
||||
|
||||
@Before
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
mvc = MockMvcBuilders
|
||||
.webAppContextSetup(context)
|
||||
@@ -39,7 +39,7 @@ public class CsrfShowcaseTests {
|
||||
.Kotlin
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@RunWith(SpringJUnit4ClassRunner::class)
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = [SecurityConfig::class])
|
||||
@WebAppConfiguration
|
||||
class CsrfShowcaseTests {
|
||||
@@ -49,7 +49,7 @@ class CsrfShowcaseTests {
|
||||
|
||||
private var mvc: MockMvc? = null
|
||||
|
||||
@Before
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
mvc = MockMvcBuilders
|
||||
.webAppContextSetup(context)
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
aspectjVersion=1.9.9.1
|
||||
springJavaformatVersion=0.0.31
|
||||
springBootVersion=2.4.2
|
||||
springFrameworkVersion=5.3.20
|
||||
springFrameworkVersion=5.3.21
|
||||
openSamlVersion=3.4.6
|
||||
version=5.6.4
|
||||
version=5.6.6
|
||||
kotlinVersion=1.5.32
|
||||
samplesBranch=5.6.x
|
||||
org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError
|
||||
|
||||
+6
-6
@@ -85,13 +85,13 @@ public class OidcClientInitiatedServerLogoutSuccessHandler implements ServerLogo
|
||||
return Mono.empty();
|
||||
}
|
||||
String idToken = idToken(authentication);
|
||||
URI postLogoutRedirectUri = postLogoutRedirectUri(exchange.getExchange().getRequest());
|
||||
String postLogoutRedirectUri = postLogoutRedirectUri(exchange.getExchange().getRequest());
|
||||
return Mono.just(endpointUri(endSessionEndpoint, idToken, postLogoutRedirectUri));
|
||||
})
|
||||
.switchIfEmpty(
|
||||
this.serverLogoutSuccessHandler.onLogoutSuccess(exchange, authentication).then(Mono.empty())
|
||||
)
|
||||
.flatMap((endpointUri) -> this.redirectStrategy.sendRedirect(exchange.getExchange(), endpointUri));
|
||||
.flatMap((endpointUri) -> this.redirectStrategy.sendRedirect(exchange.getExchange(), URI.create(endpointUri)));
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@@ -106,20 +106,20 @@ public class OidcClientInitiatedServerLogoutSuccessHandler implements ServerLogo
|
||||
return null;
|
||||
}
|
||||
|
||||
private URI endpointUri(URI endSessionEndpoint, String idToken, URI postLogoutRedirectUri) {
|
||||
private String endpointUri(URI endSessionEndpoint, String idToken, String postLogoutRedirectUri) {
|
||||
UriComponentsBuilder builder = UriComponentsBuilder.fromUri(endSessionEndpoint);
|
||||
builder.queryParam("id_token_hint", idToken);
|
||||
if (postLogoutRedirectUri != null) {
|
||||
builder.queryParam("post_logout_redirect_uri", postLogoutRedirectUri);
|
||||
}
|
||||
return builder.encode(StandardCharsets.UTF_8).build().toUri();
|
||||
return builder.encode(StandardCharsets.UTF_8).build().toUriString();
|
||||
}
|
||||
|
||||
private String idToken(Authentication authentication) {
|
||||
return ((OidcUser) authentication.getPrincipal()).getIdToken().getTokenValue();
|
||||
}
|
||||
|
||||
private URI postLogoutRedirectUri(ServerHttpRequest request) {
|
||||
private String postLogoutRedirectUri(ServerHttpRequest request) {
|
||||
if (this.postLogoutRedirectUri == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -131,7 +131,7 @@ public class OidcClientInitiatedServerLogoutSuccessHandler implements ServerLogo
|
||||
.build();
|
||||
return UriComponentsBuilder.fromUriString(this.postLogoutRedirectUri)
|
||||
.buildAndExpand(Collections.singletonMap("baseUrl", uriComponents.toUriString()))
|
||||
.toUri();
|
||||
.toUriString();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
|
||||
+13
@@ -150,6 +150,19 @@ public class OidcClientInitiatedServerLogoutSuccessHandlerTests {
|
||||
"https://endpoint?" + "id_token_hint=id-token&" + "post_logout_redirect_uri=https://rp.example.org");
|
||||
}
|
||||
|
||||
// gh-11379
|
||||
@Test
|
||||
public void logoutWhenUsingPostLogoutRedirectUriWithQueryParametersThenBuildsItForRedirect() {
|
||||
OAuth2AuthenticationToken token = new OAuth2AuthenticationToken(TestOidcUsers.create(),
|
||||
AuthorityUtils.NO_AUTHORITIES, this.registration.getRegistrationId());
|
||||
given(this.exchange.getPrincipal()).willReturn(Mono.just(token));
|
||||
this.handler.setPostLogoutRedirectUri("https://rp.example.org/context?forwardUrl=secured%3Fparam%3Dtrue");
|
||||
WebFilterExchange f = new WebFilterExchange(this.exchange, this.chain);
|
||||
this.handler.onLogoutSuccess(f, token).block();
|
||||
assertThat(redirectedUrl(this.exchange)).isEqualTo("https://endpoint?id_token_hint=id-token&"
|
||||
+ "post_logout_redirect_uri=https://rp.example.org/context?forwardUrl%3Dsecured%253Fparam%253Dtrue");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setPostLogoutRedirectUriWhenGivenNullThenThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.handler.setPostLogoutRedirectUri((URI) null));
|
||||
|
||||
@@ -46,9 +46,9 @@ dependencies {
|
||||
api "org.opensaml:opensaml-core"
|
||||
api "org.opensaml:opensaml-saml-api"
|
||||
api "org.opensaml:opensaml-saml-impl"
|
||||
opensaml4MainImplementation "org.opensaml:opensaml-core:4.1.0"
|
||||
opensaml4MainImplementation "org.opensaml:opensaml-saml-api:4.1.0"
|
||||
opensaml4MainImplementation "org.opensaml:opensaml-saml-impl:4.1.0"
|
||||
opensaml4MainImplementation "org.opensaml:opensaml-core:4.1.1"
|
||||
opensaml4MainImplementation "org.opensaml:opensaml-saml-api:4.1.1"
|
||||
opensaml4MainImplementation "org.opensaml:opensaml-saml-impl:4.1.1"
|
||||
|
||||
provided 'jakarta.servlet:jakarta.servlet-api'
|
||||
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ final class Saml2Utils {
|
||||
}
|
||||
|
||||
static String samlEncode(byte[] b) {
|
||||
return Base64.getMimeEncoder().encodeToString(b);
|
||||
return Base64.getEncoder().encodeToString(b);
|
||||
}
|
||||
|
||||
static byte[] samlDecode(String s) {
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ final class Saml2Utils {
|
||||
}
|
||||
|
||||
static String samlEncode(byte[] b) {
|
||||
return Base64.getMimeEncoder().encodeToString(b);
|
||||
return Base64.getEncoder().encodeToString(b);
|
||||
}
|
||||
|
||||
static byte[] samlDecode(String s) {
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ public final class OpenSamlMetadataResolver implements Saml2MetadataResolver {
|
||||
|
||||
@Override
|
||||
public String resolve(RelyingPartyRegistration relyingPartyRegistration) {
|
||||
EntityDescriptor entityDescriptor = build(EntityDescriptor.ELEMENT_QNAME);
|
||||
EntityDescriptor entityDescriptor = build(EntityDescriptor.DEFAULT_ELEMENT_NAME);
|
||||
entityDescriptor.setEntityID(relyingPartyRegistration.getEntityId());
|
||||
SPSSODescriptor spSsoDescriptor = buildSpSsoDescriptor(relyingPartyRegistration);
|
||||
entityDescriptor.getRoleDescriptors(SPSSODescriptor.DEFAULT_ELEMENT_NAME).add(spSsoDescriptor);
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ final class Saml2Utils {
|
||||
}
|
||||
|
||||
static String samlEncode(byte[] b) {
|
||||
return Base64.getMimeEncoder().encodeToString(b);
|
||||
return Base64.getEncoder().encodeToString(b);
|
||||
}
|
||||
|
||||
static byte[] samlDecode(String s) {
|
||||
|
||||
+1
-6
@@ -32,13 +32,8 @@ public final class Saml2Utils {
|
||||
private Saml2Utils() {
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static String samlEncodeNotRfc2045(byte[] b) {
|
||||
return Base64.getEncoder().encodeToString(b);
|
||||
}
|
||||
|
||||
public static String samlEncode(byte[] b) {
|
||||
return Base64.getMimeEncoder().encodeToString(b);
|
||||
return Base64.getEncoder().encodeToString(b);
|
||||
}
|
||||
|
||||
public static byte[] samlDecode(String s) {
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ public class OpenSamlMetadataResolverTests {
|
||||
.assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT).build();
|
||||
OpenSamlMetadataResolver openSamlMetadataResolver = new OpenSamlMetadataResolver();
|
||||
String metadata = openSamlMetadataResolver.resolve(relyingPartyRegistration);
|
||||
assertThat(metadata).contains("<EntityDescriptor").contains("entityID=\"rp-entity-id\"")
|
||||
assertThat(metadata).contains("<md:EntityDescriptor").contains("entityID=\"rp-entity-id\"")
|
||||
.contains("WantAssertionsSigned=\"true\"").contains("<md:KeyDescriptor use=\"signing\">")
|
||||
.contains("<md:KeyDescriptor use=\"encryption\">")
|
||||
.contains("<ds:X509Certificate>MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBh")
|
||||
@@ -53,7 +53,7 @@ public class OpenSamlMetadataResolverTests {
|
||||
.build();
|
||||
OpenSamlMetadataResolver openSamlMetadataResolver = new OpenSamlMetadataResolver();
|
||||
String metadata = openSamlMetadataResolver.resolve(relyingPartyRegistration);
|
||||
assertThat(metadata).contains("<EntityDescriptor").contains("entityID=\"rp-entity-id\"")
|
||||
assertThat(metadata).contains("<md:EntityDescriptor").contains("entityID=\"rp-entity-id\"")
|
||||
.contains("WantAssertionsSigned=\"true\"").doesNotContain("<md:KeyDescriptor use=\"signing\">")
|
||||
.doesNotContain("<md:KeyDescriptor use=\"encryption\">")
|
||||
.contains("Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"")
|
||||
|
||||
+2
-2
@@ -64,7 +64,7 @@ public class Saml2AuthenticationTokenConverterTests {
|
||||
.willReturn(this.relyingPartyRegistration);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setParameter(Saml2ParameterNames.SAML_RESPONSE,
|
||||
Saml2Utils.samlEncodeNotRfc2045("response".getBytes(StandardCharsets.UTF_8)));
|
||||
Saml2Utils.samlEncode("response".getBytes(StandardCharsets.UTF_8)));
|
||||
Saml2AuthenticationToken token = converter.convert(request);
|
||||
assertThat(token.getSaml2Response()).isEqualTo("response");
|
||||
assertThat(token.getRelyingPartyRegistration().getRegistrationId())
|
||||
@@ -115,7 +115,7 @@ public class Saml2AuthenticationTokenConverterTests {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setMethod("GET");
|
||||
byte[] deflated = Saml2Utils.samlDeflate("response");
|
||||
String encoded = Saml2Utils.samlEncodeNotRfc2045(deflated);
|
||||
String encoded = Saml2Utils.samlEncode(deflated);
|
||||
request.setParameter(Saml2ParameterNames.SAML_RESPONSE, encoded);
|
||||
Saml2AuthenticationToken token = converter.convert(request);
|
||||
assertThat(token.getSaml2Response()).isEqualTo("response");
|
||||
|
||||
+76
-3
@@ -107,6 +107,15 @@ public class StrictHttpFirewall implements HttpFirewall {
|
||||
|
||||
private static final List<String> FORBIDDEN_NULL = Collections.unmodifiableList(Arrays.asList("\0", "%00"));
|
||||
|
||||
private static final List<String> FORBIDDEN_LF = Collections.unmodifiableList(Arrays.asList("\n", "%0a", "%0A"));
|
||||
|
||||
private static final List<String> FORBIDDEN_CR = Collections.unmodifiableList(Arrays.asList("\r", "%0d", "%0D"));
|
||||
|
||||
private static final List<String> FORBIDDEN_LINE_SEPARATOR = Collections.unmodifiableList(Arrays.asList("\u2028"));
|
||||
|
||||
private static final List<String> FORBIDDEN_PARAGRAPH_SEPARATOR = Collections
|
||||
.unmodifiableList(Arrays.asList("\u2029"));
|
||||
|
||||
private Set<String> encodedUrlBlocklist = new HashSet<>();
|
||||
|
||||
private Set<String> decodedUrlBlocklist = new HashSet<>();
|
||||
@@ -135,10 +144,14 @@ public class StrictHttpFirewall implements HttpFirewall {
|
||||
urlBlocklistsAddAll(FORBIDDEN_DOUBLE_FORWARDSLASH);
|
||||
urlBlocklistsAddAll(FORBIDDEN_BACKSLASH);
|
||||
urlBlocklistsAddAll(FORBIDDEN_NULL);
|
||||
urlBlocklistsAddAll(FORBIDDEN_LF);
|
||||
urlBlocklistsAddAll(FORBIDDEN_CR);
|
||||
|
||||
this.encodedUrlBlocklist.add(ENCODED_PERCENT);
|
||||
this.encodedUrlBlocklist.addAll(FORBIDDEN_ENCODED_PERIOD);
|
||||
this.decodedUrlBlocklist.add(PERCENT);
|
||||
this.decodedUrlBlocklist.addAll(FORBIDDEN_LINE_SEPARATOR);
|
||||
this.decodedUrlBlocklist.addAll(FORBIDDEN_PARAGRAPH_SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -345,6 +358,69 @@ public class StrictHttpFirewall implements HttpFirewall {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a URL encoded Carriage Return is allowed in the path or not. The
|
||||
* default is not to allow this behavior because it is a frequent source of security
|
||||
* exploits.
|
||||
* @param allowUrlEncodedCarriageReturn if URL encoded Carriage Return is allowed in
|
||||
* the URL or not. Default is false.
|
||||
*/
|
||||
public void setAllowUrlEncodedCarriageReturn(boolean allowUrlEncodedCarriageReturn) {
|
||||
if (allowUrlEncodedCarriageReturn) {
|
||||
urlBlocklistsRemoveAll(FORBIDDEN_CR);
|
||||
}
|
||||
else {
|
||||
urlBlocklistsAddAll(FORBIDDEN_CR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a URL encoded Line Feed is allowed in the path or not. The default is
|
||||
* not to allow this behavior because it is a frequent source of security exploits.
|
||||
* @param allowUrlEncodedLineFeed if URL encoded Line Feed is allowed in the URL or
|
||||
* not. Default is false.
|
||||
*/
|
||||
public void setAllowUrlEncodedLineFeed(boolean allowUrlEncodedLineFeed) {
|
||||
if (allowUrlEncodedLineFeed) {
|
||||
urlBlocklistsRemoveAll(FORBIDDEN_LF);
|
||||
}
|
||||
else {
|
||||
urlBlocklistsAddAll(FORBIDDEN_LF);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a URL encoded paragraph separator is allowed in the path or not. The
|
||||
* default is not to allow this behavior because it is a frequent source of security
|
||||
* exploits.
|
||||
* @param allowUrlEncodedParagraphSeparator if URL encoded paragraph separator is
|
||||
* allowed in the URL or not. Default is false.
|
||||
*/
|
||||
public void setAllowUrlEncodedParagraphSeparator(boolean allowUrlEncodedParagraphSeparator) {
|
||||
if (allowUrlEncodedParagraphSeparator) {
|
||||
this.decodedUrlBlocklist.removeAll(FORBIDDEN_PARAGRAPH_SEPARATOR);
|
||||
}
|
||||
else {
|
||||
this.decodedUrlBlocklist.addAll(FORBIDDEN_PARAGRAPH_SEPARATOR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a URL encoded line separator is allowed in the path or not. The
|
||||
* default is not to allow this behavior because it is a frequent source of security
|
||||
* exploits.
|
||||
* @param allowUrlEncodedLineSeparator if URL encoded line separator is allowed in the
|
||||
* URL or not. Default is false.
|
||||
*/
|
||||
public void setAllowUrlEncodedLineSeparator(boolean allowUrlEncodedLineSeparator) {
|
||||
if (allowUrlEncodedLineSeparator) {
|
||||
this.decodedUrlBlocklist.removeAll(FORBIDDEN_LINE_SEPARATOR);
|
||||
}
|
||||
else {
|
||||
this.decodedUrlBlocklist.addAll(FORBIDDEN_LINE_SEPARATOR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Determines which header names should be allowed. The default is to reject header
|
||||
@@ -432,9 +508,6 @@ public class StrictHttpFirewall implements HttpFirewall {
|
||||
throw new RequestRejectedException("The request was rejected because the URL was not normalized.");
|
||||
}
|
||||
rejectNonPrintableAsciiCharactersInFieldName(request.getRequestURI(), "requestURI");
|
||||
rejectNonPrintableAsciiCharactersInFieldName(request.getServletPath(), "servletPath");
|
||||
rejectNonPrintableAsciiCharactersInFieldName(request.getPathInfo(), "pathInfo");
|
||||
rejectNonPrintableAsciiCharactersInFieldName(request.getContextPath(), "contextPath");
|
||||
return new StrictFirewalledRequest(request);
|
||||
}
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ public class CsrfWebFilter implements WebFilter {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
MediaType contentType = headers.getContentType();
|
||||
if (!contentType.includes(MediaType.MULTIPART_FORM_DATA)) {
|
||||
if (!MediaType.MULTIPART_FORM_DATA.isCompatibleWith(contentType)) {
|
||||
return Mono.empty();
|
||||
}
|
||||
return exchange.getMultipartData().map((d) -> d.getFirst(expected.getParameterName())).cast(FormFieldPart.class)
|
||||
|
||||
+124
@@ -343,6 +343,12 @@ public class StrictHttpFirewallTests {
|
||||
this.firewall.getFirewalledRequest(this.request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenJapaneseCharacterThenNoException() {
|
||||
this.request.setServletPath("/\u3042");
|
||||
this.firewall.getFirewalledRequest(this.request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenExceedsUpperboundAsciiThenException() {
|
||||
this.request.setRequestURI("/\u007f");
|
||||
@@ -364,6 +370,20 @@ public class StrictHttpFirewallTests {
|
||||
.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenContainsLowercaseEncodedLineFeedThenException() {
|
||||
this.request.setRequestURI("/something%0a/");
|
||||
assertThatExceptionOfType(RequestRejectedException.class)
|
||||
.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenContainsUppercaseEncodedLineFeedThenException() {
|
||||
this.request.setRequestURI("/something%0A/");
|
||||
assertThatExceptionOfType(RequestRejectedException.class)
|
||||
.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenContainsLineFeedThenException() {
|
||||
this.request.setRequestURI("/something\n/");
|
||||
@@ -378,6 +398,20 @@ public class StrictHttpFirewallTests {
|
||||
.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenContainsLowercaseEncodedCarriageReturnThenException() {
|
||||
this.request.setRequestURI("/something%0d/");
|
||||
assertThatExceptionOfType(RequestRejectedException.class)
|
||||
.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenContainsUppercaseEncodedCarriageReturnThenException() {
|
||||
this.request.setRequestURI("/something%0D/");
|
||||
assertThatExceptionOfType(RequestRejectedException.class)
|
||||
.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenContainsCarriageReturnThenException() {
|
||||
this.request.setRequestURI("/something\r/");
|
||||
@@ -392,6 +426,96 @@ public class StrictHttpFirewallTests {
|
||||
.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenServletPathContainsLineSeparatorThenException() {
|
||||
this.request.setServletPath("/something\u2028/");
|
||||
assertThatExceptionOfType(RequestRejectedException.class)
|
||||
.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenServletPathContainsParagraphSeparatorThenException() {
|
||||
this.request.setServletPath("/something\u2029/");
|
||||
assertThatExceptionOfType(RequestRejectedException.class)
|
||||
.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenContainsLowercaseEncodedLineFeedAndAllowedThenNoException() {
|
||||
this.firewall.setAllowUrlEncodedLineFeed(true);
|
||||
this.request.setRequestURI("/something%0a/");
|
||||
this.firewall.getFirewalledRequest(this.request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenContainsUppercaseEncodedLineFeedAndAllowedThenNoException() {
|
||||
this.firewall.setAllowUrlEncodedLineFeed(true);
|
||||
this.request.setRequestURI("/something%0A/");
|
||||
this.firewall.getFirewalledRequest(this.request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenContainsLineFeedAndAllowedThenException() {
|
||||
this.firewall.setAllowUrlEncodedLineFeed(true);
|
||||
this.request.setRequestURI("/something\n/");
|
||||
// Expected an error because the line feed is decoded in an encoded part of the
|
||||
// URL
|
||||
assertThatExceptionOfType(RequestRejectedException.class)
|
||||
.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenServletPathContainsLineFeedAndAllowedThenNoException() {
|
||||
this.firewall.setAllowUrlEncodedLineFeed(true);
|
||||
this.request.setServletPath("/something\n/");
|
||||
this.firewall.getFirewalledRequest(this.request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenContainsLowercaseEncodedCarriageReturnAndAllowedThenNoException() {
|
||||
this.firewall.setAllowUrlEncodedCarriageReturn(true);
|
||||
this.request.setRequestURI("/something%0d/");
|
||||
this.firewall.getFirewalledRequest(this.request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenContainsUppercaseEncodedCarriageReturnAndAllowedThenNoException() {
|
||||
this.firewall.setAllowUrlEncodedCarriageReturn(true);
|
||||
this.request.setRequestURI("/something%0D/");
|
||||
this.firewall.getFirewalledRequest(this.request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenContainsCarriageReturnAndAllowedThenNoException() {
|
||||
this.firewall.setAllowUrlEncodedCarriageReturn(true);
|
||||
this.request.setRequestURI("/something\r/");
|
||||
// Expected an error because the carriage return is decoded in an encoded part of
|
||||
// the URL
|
||||
assertThatExceptionOfType(RequestRejectedException.class)
|
||||
.isThrownBy(() -> this.firewall.getFirewalledRequest(this.request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenServletPathContainsCarriageReturnAndAllowedThenNoException() {
|
||||
this.firewall.setAllowUrlEncodedCarriageReturn(true);
|
||||
this.request.setServletPath("/something\r/");
|
||||
this.firewall.getFirewalledRequest(this.request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenServletPathContainsLineSeparatorAndAllowedThenNoException() {
|
||||
this.firewall.setAllowUrlEncodedLineSeparator(true);
|
||||
this.request.setServletPath("/something\u2028/");
|
||||
this.firewall.getFirewalledRequest(this.request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFirewalledRequestWhenServletPathContainsParagraphSeparatorAndAllowedThenNoException() {
|
||||
this.firewall.setAllowUrlEncodedParagraphSeparator(true);
|
||||
this.request.setServletPath("/something\u2029/");
|
||||
this.firewall.getFirewalledRequest(this.request);
|
||||
}
|
||||
|
||||
/**
|
||||
* On WebSphere 8.5 a URL like /context-root/a/b;%2f1/c can bypass a rule on /a/b/c
|
||||
* because the pathInfo is /a/b;/1/c which ends up being /a/b/1/c while Spring MVC
|
||||
|
||||
+11
@@ -189,6 +189,17 @@ public class CsrfWebFilterTests {
|
||||
.expectStatus().is2xxSuccessful();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterWhenPostAndMultipartFormDataEnabledAndNoBodyProvided() {
|
||||
this.csrfFilter.setCsrfTokenRepository(this.repository);
|
||||
this.csrfFilter.setTokenFromMultipartDataEnabled(true);
|
||||
given(this.repository.loadToken(any())).willReturn(Mono.just(this.token));
|
||||
given(this.repository.generateToken(any())).willReturn(Mono.just(this.token));
|
||||
WebTestClient client = WebTestClient.bindToController(new OkController()).webFilter(this.csrfFilter).build();
|
||||
client.post().uri("/").header(this.token.getHeaderName(), this.token.getToken()).exchange().expectStatus()
|
||||
.is2xxSuccessful();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterWhenFormDataAndEnabledThenGranted() {
|
||||
this.csrfFilter.setCsrfTokenRepository(this.repository);
|
||||
|
||||
Reference in New Issue
Block a user