Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c40f65f5a2 | |||
| bca43af9bb | |||
| d9b8882fa8 | |||
| 7358c65a8c | |||
| e02d5f2dd7 | |||
| 91a965c6db | |||
| 0e88064942 | |||
| 641b9ef83b | |||
| 6f43d234dc | |||
| d7819ea4da | |||
| 37ee70ae86 | |||
| 8ea37360ac | |||
| 29db051f7a | |||
| f035c30edb | |||
| d22277ce36 | |||
| bd60a0f8c9 | |||
| d882bfcf2b | |||
| 6c3f53ac0a | |||
| b274431c07 | |||
| 3d5e5ff556 | |||
| a3e996a66b | |||
| 0c31cb21dc | |||
| c39d39b35f | |||
| 292585080a | |||
| 8690accd57 | |||
| e3c15260e7 | |||
| 9625382b22 | |||
| 48ef3f4719 | |||
| d0da160007 | |||
| 4caf53e96d |
@@ -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"
|
||||
@@ -74,8 +74,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
|
||||
*/
|
||||
|
||||
+16
-1
@@ -2,7 +2,7 @@ buildscript {
|
||||
dependencies {
|
||||
classpath "io.spring.javaformat:spring-javaformat-gradle-plugin:$springJavaformatVersion"
|
||||
classpath 'io.spring.nohttp:nohttp-gradle:0.0.10'
|
||||
classpath "io.freefair.gradle:aspectj-plugin:6.4.3"
|
||||
classpath "io.freefair.gradle:aspectj-plugin:6.4.3.1"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
classpath "com.netflix.nebula:nebula-project-plugin:8.2.0"
|
||||
}
|
||||
@@ -70,6 +70,9 @@ updateDependenciesSettings {
|
||||
})
|
||||
dependencyExcludes {
|
||||
majorVersionBump()
|
||||
minorVersionBump()
|
||||
releaseCandidatesVersions()
|
||||
milestoneVersions()
|
||||
alphaBetaVersions()
|
||||
snapshotVersions()
|
||||
addRule { components ->
|
||||
@@ -99,6 +102,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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -307,7 +307,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();
|
||||
}
|
||||
|
||||
+6
-6
@@ -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.2.0")
|
||||
api platform("org.springframework.data:spring-data-bom:2021.2.1")
|
||||
api platform("org.jetbrains.kotlin:kotlin-bom:$kotlinVersion")
|
||||
api platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.1")
|
||||
api platform("com.fasterxml.jackson:jackson-bom:2.13.2.20220328")
|
||||
api platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.3")
|
||||
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.4.0"
|
||||
api "org.springframework.ldap:spring-ldap-core:2.4.1"
|
||||
api "org.synchronoss.cloud:nio-multipart-parser:1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
name: ROOT
|
||||
version: '5.7.1'
|
||||
version: '5.7.2'
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
**[[**servlet-authentication-digest]]
|
||||
[[servlet-authentication-digest]]
|
||||
= Digest Authentication
|
||||
|
||||
This section provides details on how Spring Security provides support for https://tools.ietf.org/html/rfc2617[Digest Authentication] which is provided `DigestAuthenticationFilter`.
|
||||
|
||||
@@ -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.7.1
|
||||
version=5.7.2
|
||||
kotlinVersion=1.6.21
|
||||
samplesBranch=5.7.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
@@ -75,7 +75,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);
|
||||
|
||||
+20
@@ -42,6 +42,9 @@ import org.opensaml.xmlsec.SignatureSigningParametersResolver;
|
||||
import org.opensaml.xmlsec.criterion.SignatureSigningConfigurationCriterion;
|
||||
import org.opensaml.xmlsec.crypto.XMLSigningUtil;
|
||||
import org.opensaml.xmlsec.impl.BasicSignatureSigningConfiguration;
|
||||
import org.opensaml.xmlsec.keyinfo.KeyInfoGeneratorManager;
|
||||
import org.opensaml.xmlsec.keyinfo.NamedKeyInfoGeneratorManager;
|
||||
import org.opensaml.xmlsec.keyinfo.impl.X509KeyInfoGeneratorFactory;
|
||||
import org.opensaml.xmlsec.signature.SignableXMLObject;
|
||||
import org.opensaml.xmlsec.signature.support.SignatureConstants;
|
||||
import org.opensaml.xmlsec.signature.support.SignatureSupport;
|
||||
@@ -102,6 +105,7 @@ final class OpenSamlSigningUtils {
|
||||
signingConfiguration.setSignatureAlgorithms(algorithms);
|
||||
signingConfiguration.setSignatureReferenceDigestMethods(digests);
|
||||
signingConfiguration.setSignatureCanonicalizationAlgorithm(canonicalization);
|
||||
signingConfiguration.setKeyInfoGeneratorManager(buildSignatureKeyInfoGeneratorManager());
|
||||
criteria.add(new SignatureSigningConfigurationCriterion(signingConfiguration));
|
||||
try {
|
||||
SignatureSigningParameters parameters = resolver.resolveSingle(criteria);
|
||||
@@ -113,6 +117,22 @@ final class OpenSamlSigningUtils {
|
||||
}
|
||||
}
|
||||
|
||||
private static NamedKeyInfoGeneratorManager buildSignatureKeyInfoGeneratorManager() {
|
||||
final NamedKeyInfoGeneratorManager namedManager = new NamedKeyInfoGeneratorManager();
|
||||
|
||||
namedManager.setUseDefaultManager(true);
|
||||
final KeyInfoGeneratorManager defaultManager = namedManager.getDefaultManager();
|
||||
|
||||
// Generator for X509Credentials
|
||||
final X509KeyInfoGeneratorFactory x509Factory = new X509KeyInfoGeneratorFactory();
|
||||
x509Factory.setEmitEntityCertificate(true);
|
||||
x509Factory.setEmitEntityCertificateChain(true);
|
||||
|
||||
defaultManager.registerFactory(x509Factory);
|
||||
|
||||
return namedManager;
|
||||
}
|
||||
|
||||
private static List<Credential> resolveSigningCredentials(RelyingPartyRegistration relyingPartyRegistration) {
|
||||
List<Credential> credentials = new ArrayList<>();
|
||||
for (Saml2X509Credential x509Credential : relyingPartyRegistration.getSigningX509Credentials()) {
|
||||
|
||||
+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
@@ -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) {
|
||||
|
||||
+3
-3
@@ -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("<md:KeyDescriptor use=\"signing\">").contains("<md:KeyDescriptor use=\"encryption\">")
|
||||
.contains("<ds:X509Certificate>MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBh")
|
||||
.contains("Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"")
|
||||
@@ -52,7 +52,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\"")
|
||||
.doesNotContain("<md:KeyDescriptor use=\"signing\">")
|
||||
.doesNotContain("<md:KeyDescriptor use=\"encryption\">")
|
||||
.contains("Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"")
|
||||
@@ -86,7 +86,7 @@ public class OpenSamlMetadataResolverTests {
|
||||
openSamlMetadataResolver.setEntityDescriptorCustomizer(
|
||||
(parameters) -> parameters.getEntityDescriptor().setEntityID("overriddenEntityId"));
|
||||
String metadata = openSamlMetadataResolver.resolve(relyingPartyRegistration);
|
||||
assertThat(metadata).contains("<EntityDescriptor").contains("entityID=\"overriddenEntityId\"");
|
||||
assertThat(metadata).contains("<md:EntityDescriptor").contains("entityID=\"overriddenEntityId\"");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+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");
|
||||
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.saml2.provider.service.web.authentication;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.opensaml.core.xml.XMLObject;
|
||||
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
|
||||
import org.opensaml.saml.common.SAMLVersion;
|
||||
import org.opensaml.saml.saml2.core.Issuer;
|
||||
import org.opensaml.saml.saml2.core.Response;
|
||||
import org.opensaml.xmlsec.signature.Signature;
|
||||
|
||||
import org.springframework.security.saml2.core.OpenSamlInitializationService;
|
||||
import org.springframework.security.saml2.core.TestSaml2X509Credentials;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Test open SAML signatures
|
||||
*/
|
||||
public class OpenSamlSigningUtilsTests {
|
||||
|
||||
static {
|
||||
OpenSamlInitializationService.initialize();
|
||||
}
|
||||
|
||||
private RelyingPartyRegistration registration;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.registration = RelyingPartyRegistration.withRegistrationId("saml-idp")
|
||||
.entityId("https://some.idp.example.com/entity-id").signingX509Credentials((c) -> {
|
||||
c.add(TestSaml2X509Credentials.relyingPartySigningCredential());
|
||||
c.add(TestSaml2X509Credentials.assertingPartySigningCredential());
|
||||
}).assertingPartyDetails((c) -> c.entityId("https://some.idp.example.com/entity-id")
|
||||
.singleSignOnServiceLocation("https://some.idp.example.com/service-location"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSigningAnObjectThenKeyInfoIsPartOfTheSignature() {
|
||||
Response response = response("destination", "issuer");
|
||||
OpenSamlSigningUtils.sign(response, this.registration);
|
||||
Signature signature = response.getSignature();
|
||||
assertThat(signature).isNotNull();
|
||||
assertThat(signature.getKeyInfo()).isNotNull();
|
||||
}
|
||||
|
||||
Response response(String destination, String issuerEntityId) {
|
||||
Response response = build(Response.DEFAULT_ELEMENT_NAME);
|
||||
response.setID("R" + UUID.randomUUID());
|
||||
response.setVersion(SAMLVersion.VERSION_20);
|
||||
response.setID("_" + UUID.randomUUID());
|
||||
response.setDestination(destination);
|
||||
response.setIssuer(issuer(issuerEntityId));
|
||||
return response;
|
||||
}
|
||||
|
||||
Issuer issuer(String entityId) {
|
||||
Issuer issuer = build(Issuer.DEFAULT_ELEMENT_NAME);
|
||||
issuer.setValue(entityId);
|
||||
return issuer;
|
||||
}
|
||||
|
||||
<T extends XMLObject> T build(QName qName) {
|
||||
return (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName);
|
||||
}
|
||||
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.saml2.provider.service.web.authentication.logout;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.opensaml.core.xml.XMLObject;
|
||||
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
|
||||
import org.opensaml.saml.common.SAMLVersion;
|
||||
import org.opensaml.saml.saml2.core.Issuer;
|
||||
import org.opensaml.saml.saml2.core.Response;
|
||||
import org.opensaml.xmlsec.signature.Signature;
|
||||
|
||||
import org.springframework.security.saml2.core.OpenSamlInitializationService;
|
||||
import org.springframework.security.saml2.core.TestSaml2X509Credentials;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Test open SAML signatures
|
||||
*/
|
||||
public class OpenSamlSigningUtilsTests {
|
||||
|
||||
static {
|
||||
OpenSamlInitializationService.initialize();
|
||||
}
|
||||
|
||||
private RelyingPartyRegistration registration;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.registration = RelyingPartyRegistration.withRegistrationId("saml-idp")
|
||||
.entityId("https://some.idp.example.com/entity-id").signingX509Credentials((c) -> {
|
||||
c.add(TestSaml2X509Credentials.relyingPartySigningCredential());
|
||||
c.add(TestSaml2X509Credentials.assertingPartySigningCredential());
|
||||
}).assertingPartyDetails((c) -> c.entityId("https://some.idp.example.com/entity-id")
|
||||
.singleSignOnServiceLocation("https://some.idp.example.com/service-location"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void whenSigningAnObjectThenKeyInfoIsPartOfTheSignature() {
|
||||
Response response = response("destination", "issuer");
|
||||
OpenSamlSigningUtils.sign(response, this.registration);
|
||||
Signature signature = response.getSignature();
|
||||
assertThat(signature).isNotNull();
|
||||
assertThat(signature.getKeyInfo()).isNotNull();
|
||||
}
|
||||
|
||||
Response response(String destination, String issuerEntityId) {
|
||||
Response response = build(Response.DEFAULT_ELEMENT_NAME);
|
||||
response.setID("R" + UUID.randomUUID());
|
||||
response.setVersion(SAMLVersion.VERSION_20);
|
||||
response.setID("_" + UUID.randomUUID());
|
||||
response.setDestination(destination);
|
||||
response.setIssuer(issuer(issuerEntityId));
|
||||
return response;
|
||||
}
|
||||
|
||||
Issuer issuer(String entityId) {
|
||||
Issuer issuer = build(Issuer.DEFAULT_ELEMENT_NAME);
|
||||
issuer.setValue(entityId);
|
||||
return issuer;
|
||||
}
|
||||
|
||||
<T extends XMLObject> T build(QName qName) {
|
||||
return (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -22,6 +22,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
|
||||
/**
|
||||
* Strategy used for persisting a {@link SecurityContext} between requests.
|
||||
@@ -76,7 +77,7 @@ public interface SecurityContextRepository {
|
||||
* @since 5.7
|
||||
*/
|
||||
default Supplier<SecurityContext> loadContext(HttpServletRequest request) {
|
||||
return () -> loadContext(new HttpRequestResponseHolder(request, null));
|
||||
return SingletonSupplier.of(() -> loadContext(new HttpRequestResponseHolder(request, null)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.context;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class SecurityContextRepositoryTests {
|
||||
|
||||
SecurityContextRepository repository = spy(SecurityContextRepository.class);
|
||||
|
||||
@Test
|
||||
void loadContextHttpRequestResponseHolderWhenInvokeSupplierTwiceThenOnlyInvokesLoadContextOnce() {
|
||||
given(this.repository.loadContext(any(HttpRequestResponseHolder.class))).willReturn(new SecurityContextImpl());
|
||||
Supplier<SecurityContext> deferredContext = this.repository.loadContext(mock(HttpServletRequest.class));
|
||||
verify(this.repository).loadContext(any(HttpServletRequest.class));
|
||||
deferredContext.get();
|
||||
verify(this.repository).loadContext(any(HttpRequestResponseHolder.class));
|
||||
deferredContext.get();
|
||||
verifyNoMoreInteractions(this.repository);
|
||||
}
|
||||
|
||||
}
|
||||
+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