Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5bc0537f1b | |||
| 355bf8ddee | |||
| 05585a5f13 | |||
| 00d350608a | |||
| 906c520fa0 | |||
| ca19601f8b | |||
| bdd4d05ce2 | |||
| 0eec932917 | |||
| af5c1761e8 | |||
| 491989c692 | |||
| 731069a69a | |||
| 47b8860681 | |||
| 828cac8889 | |||
| aefd2d497c | |||
| 775bf91c77 | |||
| 2bf7a5ae80 | |||
| c495ff8759 | |||
| b7a12e1b80 | |||
| 670d10dadd | |||
| 72db6a20c9 | |||
| 00f4033b9b | |||
| ddb59d439f | |||
| e4a76b0ec9 | |||
| 78e8507af7 | |||
| 13e7c9f58d | |||
| 14e6d8ad49 | |||
| d1a27676df | |||
| 844879b84d | |||
| d5efbd34b5 | |||
| b9ef11d228 | |||
| d9b1df8b65 | |||
| 020dd90566 | |||
| fafde0910f | |||
| b2db2bdb2a | |||
| cce1c16555 | |||
| e969c363c0 | |||
| 764e696026 | |||
| 6e86fab19d | |||
| 1a3e80506c | |||
| eb0597154d | |||
| 97dfabe92e | |||
| 944463e19a | |||
| 816e847af2 | |||
| 1f919bc791 | |||
| cae8990046 | |||
| c9917b3cd0 | |||
| 1653b7848d | |||
| c55f1f8bea | |||
| 670cf99258 | |||
| 41feec4766 | |||
| 34e04f642c |
+1
-1
@@ -1,5 +1,5 @@
|
||||
# EditorConfig for Spring Security
|
||||
# see https://github.com/spring-projects/spring-security/blob/master/CONTRIBUTING.md#mind-the-whitespace
|
||||
# see https://github.com/spring-projects/spring-security/blob/master/CONTRIBUTING.adoc#mind-the-whitespace
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Cache Gradle packages
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
@@ -71,49 +71,16 @@ jobs:
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Snapshot Tests
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
./gradlew test --refresh-dependencies -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" -PforceMavenRepositories=snapshot -PspringVersion='5.+' -PreactorVersion='20+' -PspringDataVersion='Neumann-BUILD-SNAPSHOT' -PrsocketVersion=1.1.0-SNAPSHOT -PspringBootVersion=2.4.0-SNAPSHOT -PlocksDisabled --stacktrace
|
||||
sonar_analysis:
|
||||
name: Static Code Analysis
|
||||
needs: [prerequisites]
|
||||
runs-on: ubuntu-latest
|
||||
if: needs.prerequisites.outputs.runjobs
|
||||
env:
|
||||
SONAR_URL: ${{ secrets.SONAR_URL }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
|
||||
- name: Run Sonar on given (non-main) branch
|
||||
if: ${{ github.ref != 'refs/heads/main' }}
|
||||
run: |
|
||||
export BRANCH=${GITHUB_REF#refs/heads/}
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
./gradlew sonarqube -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" -PexcludeProjects='**/samples/**' -Dsonar.projectKey="spring-security-${GITHUB_REF#refs/heads/}" -Dsonar.projectName="spring-security-${GITHUB_REF#refs/heads/}" -Dsonar.host.url="$SONAR_URL" -Dsonar.login="$SONAR_TOKEN" --stacktrace
|
||||
- name: Run Sonar on main
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
./gradlew sonarqube -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" -PexcludeProjects='**/samples/**' -Dsonar.host.url="$SONAR_URL" -Dsonar.login="$SONAR_TOKEN" --stacktrace
|
||||
deploy_artifacts:
|
||||
name: Deploy Artifacts
|
||||
needs: [build_jdk_11, snapshot_tests, sonar_analysis]
|
||||
needs: [build_jdk_11, snapshot_tests]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -124,7 +91,7 @@ jobs:
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Deploy artifacts
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -140,7 +107,7 @@ jobs:
|
||||
ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
deploy_docs:
|
||||
name: Deploy Docs
|
||||
needs: [build_jdk_11, snapshot_tests, sonar_analysis]
|
||||
needs: [build_jdk_11, snapshot_tests]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -151,7 +118,7 @@ jobs:
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Deploy Docs
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -164,7 +131,7 @@ jobs:
|
||||
DOCS_HOST: ${{ secrets.DOCS_HOST }}
|
||||
deploy_schema:
|
||||
name: Deploy Schema
|
||||
needs: [build_jdk_11, snapshot_tests, sonar_analysis]
|
||||
needs: [build_jdk_11, snapshot_tests]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -175,7 +142,7 @@ jobs:
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Deploy Schema
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -188,7 +155,7 @@ jobs:
|
||||
DOCS_HOST: ${{ secrets.DOCS_HOST }}
|
||||
notify_result:
|
||||
name: Check for failures
|
||||
needs: [build_jdk_11, snapshot_tests, sonar_analysis, deploy_artifacts, deploy_docs, deploy_schema]
|
||||
needs: [build_jdk_11, snapshot_tests, deploy_artifacts, deploy_docs, deploy_schema]
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath "io.spring.javaformat:spring-javaformat-gradle-plugin:$springJavaformatVersion"
|
||||
classpath 'io.spring.nohttp:nohttp-gradle:0.0.8'
|
||||
classpath 'io.spring.nohttp:nohttp-gradle:0.0.10'
|
||||
classpath "io.freefair.gradle:aspectj-plugin:5.3.3.3"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
classpath "com.netflix.nebula:nebula-project-plugin:8.0.0"
|
||||
|
||||
@@ -72,14 +72,14 @@ dependencies {
|
||||
implementation localGroovy()
|
||||
|
||||
implementation 'io.github.gradle-nexus:publish-plugin:1.1.0'
|
||||
implementation 'io.projectreactor:reactor-core:3.4.9'
|
||||
implementation 'io.projectreactor:reactor-core:3.4.13'
|
||||
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'
|
||||
implementation 'io.spring.gradle:docbook-reference-plugin:0.3.1'
|
||||
implementation 'io.spring.gradle:propdeps-plugin:0.0.10.RELEASE'
|
||||
implementation 'io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.15'
|
||||
implementation 'io.spring.nohttp:nohttp-gradle:0.0.8'
|
||||
implementation 'io.spring.nohttp:nohttp-gradle:0.0.10'
|
||||
implementation 'org.aim42:htmlSanityCheck:1.1.6'
|
||||
implementation 'org.asciidoctor:asciidoctor-gradle-jvm:3.1.0'
|
||||
implementation 'org.asciidoctor:asciidoctor-gradle-jvm-pdf:3.1.0'
|
||||
|
||||
@@ -36,7 +36,7 @@ class CheckstylePlugin implements Plugin<Project> {
|
||||
if (checkstyleDir.exists() && checkstyleDir.directory) {
|
||||
project.getPluginManager().apply('checkstyle')
|
||||
project.dependencies.add('checkstyle', 'io.spring.javaformat:spring-javaformat-checkstyle:0.0.15')
|
||||
project.dependencies.add('checkstyle', 'io.spring.nohttp:nohttp-checkstyle:0.0.8')
|
||||
project.dependencies.add('checkstyle', 'io.spring.nohttp:nohttp-checkstyle:0.0.10')
|
||||
|
||||
project.checkstyle {
|
||||
configDirectory = checkstyleDir
|
||||
|
||||
@@ -40,9 +40,9 @@ import org.jasig.cas.client.authentication.AttributePrincipal;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see CasJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
|
||||
+1
-1
@@ -37,9 +37,9 @@ import org.jasig.cas.client.proxy.ProxyRetriever;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see CasJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
|
||||
+1
-1
@@ -47,9 +47,9 @@ import org.springframework.security.core.userdetails.UserDetails;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see CasJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
|
||||
@@ -19,8 +19,8 @@ package org.springframework.security.config;
|
||||
/**
|
||||
* Callback interface that accepts a single input argument and returns no result.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @param <T> the type of the input to the operation
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.2
|
||||
*/
|
||||
@FunctionalInterface
|
||||
|
||||
+2
-2
@@ -93,7 +93,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
if (!namespaceMatchesVersion(element)) {
|
||||
pc.getReaderContext().fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or "
|
||||
+ "spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema "
|
||||
+ "with Spring Security 5.4. Please update your schema declarations to the 5.4 schema.", element);
|
||||
+ "with Spring Security 5.5. Please update your schema declarations to the 5.5 schema.", element);
|
||||
}
|
||||
String name = pc.getDelegate().getLocalName(element);
|
||||
BeanDefinitionParser parser = this.parsers.get(name);
|
||||
@@ -213,7 +213,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
|
||||
private boolean matchesVersionInternal(Element element) {
|
||||
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
|
||||
return schemaLocation.matches("(?m).*spring-security-5\\.4.*.xsd.*")
|
||||
return schemaLocation.matches("(?m).*spring-security-5\\.5.*.xsd.*")
|
||||
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|
||||
|| !schemaLocation.matches("(?m).*spring-security.*");
|
||||
}
|
||||
|
||||
+1
-1
@@ -27,8 +27,8 @@ import org.springframework.security.config.annotation.authentication.builders.Au
|
||||
* {@link AuthenticationConfiguration} to configure the global
|
||||
* {@link AuthenticationManagerBuilder}.
|
||||
*
|
||||
* @since 5.0
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
@Order(100)
|
||||
public abstract class GlobalAuthenticationConfigurerAdapter
|
||||
|
||||
+2
-2
@@ -30,10 +30,10 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||
* class is not intended to be imported manually rather it is imported automatically when
|
||||
* using {@link EnableWebSecurity} or {@link EnableGlobalMethodSecurity}.
|
||||
*
|
||||
* @see EnableWebSecurity
|
||||
* @see EnableGlobalMethodSecurity
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @see EnableWebSecurity
|
||||
* @see EnableGlobalMethodSecurity
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
|
||||
+2
-2
@@ -60,11 +60,11 @@ import org.springframework.util.Assert;
|
||||
* {@link WebSecurityConfigurer} and exposing it as a {@link Configuration}. This
|
||||
* configuration is imported when using {@link EnableWebSecurity}.
|
||||
*
|
||||
* @see EnableWebSecurity
|
||||
* @see WebSecurity
|
||||
* @author Rob Winch
|
||||
* @author Keesun Baik
|
||||
* @since 3.2
|
||||
* @see EnableWebSecurity
|
||||
* @see WebSecurity
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
|
||||
|
||||
+1
-1
@@ -87,8 +87,8 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||
* org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyClassThatExtendsAbstractHttpConfigurer, sample.OtherThatExtendsAbstractHttpConfigurer
|
||||
* </pre>
|
||||
*
|
||||
* @see EnableWebSecurity
|
||||
* @author Rob Winch
|
||||
* @see EnableWebSecurity
|
||||
*/
|
||||
@Order(100)
|
||||
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {
|
||||
|
||||
+1
-1
@@ -490,8 +490,8 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* @return the {@link FeaturePolicyConfig} for additional configuration
|
||||
* @throws IllegalArgumentException if policyDirectives is {@code null} or empty
|
||||
* @since 5.1
|
||||
* @see FeaturePolicyHeaderWriter
|
||||
* @deprecated Use {@link #permissionsPolicy(Customizer)} instead.
|
||||
* @seeObjectPostProcessorConfiguration FeaturePolicyHeaderWriter
|
||||
*/
|
||||
@Deprecated
|
||||
public FeaturePolicyConfig featurePolicy(String policyDirectives) {
|
||||
|
||||
+4
-4
@@ -49,15 +49,15 @@ import org.springframework.util.Assert;
|
||||
* <li>{@link ClientRegistrationRepository}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see OAuth2AuthorizationRequestRedirectFilter
|
||||
* @see ClientRegistrationRepository
|
||||
* @deprecated It is not recommended to use the implicit flow due to the inherent risks of
|
||||
* returning access tokens in an HTTP redirect without any confirmation that it has been
|
||||
* received by the client. See reference
|
||||
* <a target="_blank" href="https://oauth.net/2/grant-types/implicit/">OAuth 2.0 Implicit
|
||||
* Grant</a>.
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see OAuth2AuthorizationRequestRedirectFilter
|
||||
* @see ClientRegistrationRepository
|
||||
*/
|
||||
@Deprecated
|
||||
public final class ImplicitGrantConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
|
||||
+1
-1
@@ -683,10 +683,10 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
/**
|
||||
* Sets a custom {@link OAuth2User} type and associates it to the provided client
|
||||
* {@link ClientRegistration#getRegistrationId() registration identifier}.
|
||||
* @deprecated See {@link CustomUserTypesOAuth2UserService} for alternative usage.
|
||||
* @param customUserType a custom {@link OAuth2User} type
|
||||
* @param clientRegistrationId the client registration identifier
|
||||
* @return the {@link UserInfoEndpointConfig} for further configuration
|
||||
* @deprecated See {@link CustomUserTypesOAuth2UserService} for alternative usage.
|
||||
*/
|
||||
@Deprecated
|
||||
public UserInfoEndpointConfig customUserType(Class<? extends OAuth2User> customUserType,
|
||||
|
||||
+14
-3
@@ -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.
|
||||
@@ -160,7 +160,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
* @param repo the repository of relying parties
|
||||
* @return the {@link Saml2LoginConfigurer} for further configuration
|
||||
*/
|
||||
public Saml2LoginConfigurer relyingPartyRegistrationRepository(RelyingPartyRegistrationRepository repo) {
|
||||
public Saml2LoginConfigurer<B> relyingPartyRegistrationRepository(RelyingPartyRegistrationRepository repo) {
|
||||
this.relyingPartyRegistrationRepository = repo;
|
||||
return this;
|
||||
}
|
||||
@@ -172,10 +172,19 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the URL to validate the credentials. If specified a custom URL, consider
|
||||
* specifying a custom {@link AuthenticationConverter} via
|
||||
* {@link #authenticationConverter(AuthenticationConverter)}, since the default
|
||||
* {@link AuthenticationConverter} implementation relies on the
|
||||
* <code>{registrationId}</code> path variable to be present in the URL
|
||||
* @param loginProcessingUrl the URL to validate the credentials
|
||||
* @return the {@link Saml2LoginConfigurer} for additional customization
|
||||
* @see Saml2WebSsoAuthenticationFilter#DEFAULT_FILTER_PROCESSES_URI
|
||||
*/
|
||||
@Override
|
||||
public Saml2LoginConfigurer<B> loginProcessingUrl(String loginProcessingUrl) {
|
||||
Assert.hasText(loginProcessingUrl, "loginProcessingUrl cannot be empty");
|
||||
Assert.state(loginProcessingUrl.contains("{registrationId}"), "{registrationId} path variable is required");
|
||||
this.loginProcessingUrl = loginProcessingUrl;
|
||||
return this;
|
||||
}
|
||||
@@ -254,6 +263,8 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
|
||||
private AuthenticationConverter getAuthenticationConverter(B http) {
|
||||
if (this.authenticationConverter == null) {
|
||||
Assert.state(this.loginProcessingUrl.contains("{registrationId}"),
|
||||
"loginProcessingUrl must contain {registrationId} path variable");
|
||||
return new Saml2AuthenticationTokenConverter(
|
||||
new DefaultRelyingPartyRegistrationResolver(this.relyingPartyRegistrationRepository));
|
||||
}
|
||||
|
||||
+1
-1
@@ -37,9 +37,9 @@ import org.springframework.web.servlet.support.RequestDataValueProcessor;
|
||||
* {@link AuthenticationPrincipalArgumentResolver} as a
|
||||
* {@link HandlerMethodArgumentResolver}.
|
||||
*
|
||||
* @deprecated This is applied internally using SpringWebMvcImportSelector
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @deprecated This is applied internally using SpringWebMvcImportSelector
|
||||
*/
|
||||
@Deprecated
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
||||
+1
-1
@@ -79,8 +79,8 @@ import org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsSe
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @since 4.0
|
||||
* @author Rob Winch
|
||||
* @since 4.0
|
||||
*/
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 100)
|
||||
@Import(ObjectPostProcessorConfiguration.class)
|
||||
|
||||
+9
-1
@@ -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.
|
||||
@@ -27,6 +27,7 @@ import org.powermock.modules.junit4.PowerMockRunner;
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
|
||||
import org.springframework.security.config.util.SpringSecurityVersions;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -147,4 +148,11 @@ public class SecurityNamespaceHandlerTests {
|
||||
// should load just fine since no websocket block
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenOldVersionThenErrorMessageContainsCorrectVersion() {
|
||||
assertThatExceptionOfType(BeanDefinitionParsingException.class)
|
||||
.isThrownBy(() -> new InMemoryXmlApplicationContext(XML_AUTHENTICATION_MANAGER, "3.0", null))
|
||||
.withMessageContaining(SpringSecurityVersions.getCurrentXsdVersionFromSpringSchemas());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+99
-9
@@ -37,6 +37,7 @@ import org.mockito.ArgumentCaptor;
|
||||
import org.opensaml.saml.saml2.core.Assertion;
|
||||
import org.opensaml.saml.saml2.core.AuthnRequest;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -49,6 +50,7 @@ import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
@@ -77,7 +79,9 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
|
||||
import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;
|
||||
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
|
||||
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestContextResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder;
|
||||
@@ -91,6 +95,7 @@ import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
@@ -117,6 +122,8 @@ public class Saml2LoginConfigurerTests {
|
||||
|
||||
private static final String SIGNED_RESPONSE = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9ycC5leGFtcGxlLm9yZy9hY3MiIElEPSJfYzE3MzM2YTAtNTM1My00MTQ5LWI3MmMtMDNkOWY5YWYzMDdlIiBJc3N1ZUluc3RhbnQ9IjIwMjAtMDgtMDRUMjI6MDQ6NDUuMDE2WiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5hcC1lbnRpdHktaWQ8L3NhbWwyOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KPGRzOlNpZ25lZEluZm8+CjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CjxkczpSZWZlcmVuY2UgVVJJPSIjX2MxNzMzNmEwLTUzNTMtNDE0OS1iNzJjLTAzZDlmOWFmMzA3ZSI+CjxkczpUcmFuc2Zvcm1zPgo8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz4KPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgo8L2RzOlRyYW5zZm9ybXM+CjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz4KPGRzOkRpZ2VzdFZhbHVlPjYzTmlyenFzaDVVa0h1a3NuRWUrM0hWWU5aYWFsQW1OQXFMc1lGMlRuRDA9PC9kczpEaWdlc3RWYWx1ZT4KPC9kczpSZWZlcmVuY2U+CjwvZHM6U2lnbmVkSW5mbz4KPGRzOlNpZ25hdHVyZVZhbHVlPgpLMVlvWWJVUjBTclY4RTdVMkhxTTIvZUNTOTNoV25mOExnNnozeGZWMUlyalgzSXhWYkNvMVlYcnRBSGRwRVdvYTJKKzVOMmFNbFBHJiMxMzsKN2VpbDBZRC9xdUVRamRYbTNwQTBjZmEvY25pa2RuKzVhbnM0ZWQwanU1amo2dkpvZ2w2Smt4Q25LWUpwTU9HNzhtampmb0phengrWCYjMTM7CkM2NktQVStBYUdxeGVwUEQ1ZlhRdTFKSy9Jb3lBaitaa3k4Z2Jwc3VyZHFCSEJLRWxjdnVOWS92UGY0OGtBeFZBKzdtRGhNNUMvL1AmIzEzOwp0L084Y3NZYXB2UjZjdjZrdk45QXZ1N3FRdm9qVk1McHVxZWNJZDJwTUVYb0NSSnE2Nkd4MStNTUVPeHVpMWZZQlRoMEhhYjRmK3JyJiMxMzsKOEY2V1NFRC8xZllVeHliRkJqZ1Q4d2lEWHFBRU8wSVY4ZWRQeEE9PQo8L2RzOlNpZ25hdHVyZVZhbHVlPgo8L2RzOlNpZ25hdHVyZT48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iQWUzZjQ5OGI4LTliMTctNDA3OC05ZDM1LTg2YTA4NDA4NDk5NSIgSXNzdWVJbnN0YW50PSIyMDIwLTA4LTA0VDIyOjA0OjQ1LjA3N1oiIFZlcnNpb249IjIuMCI+PHNhbWwyOklzc3Vlcj5hcC1lbnRpdHktaWQ8L3NhbWwyOklzc3Vlcj48c2FtbDI6U3ViamVjdD48c2FtbDI6TmFtZUlEPnRlc3RAc2FtbC51c2VyPC9zYW1sMjpOYW1lSUQ+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90QmVmb3JlPSIyMDIwLTA4LTA0VDIxOjU5OjQ1LjA5MFoiIE5vdE9uT3JBZnRlcj0iMjA0MC0wNy0zMFQyMjowNTowNi4wODhaIiBSZWNpcGllbnQ9Imh0dHBzOi8vcnAuZXhhbXBsZS5vcmcvYWNzIi8+PC9zYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDI6U3ViamVjdD48c2FtbDI6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMjAtMDgtMDRUMjE6NTk6NDUuMDgwWiIgTm90T25PckFmdGVyPSIyMDQwLTA3LTMwVDIyOjA1OjA2LjA4N1oiLz48L3NhbWwyOkFzc2VydGlvbj48L3NhbWwycDpSZXNwb25zZT4=";
|
||||
|
||||
private static final AuthenticationConverter AUTHENTICATION_CONVERTER = mock(AuthenticationConverter.class);
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@@ -230,6 +237,33 @@ public class Saml2LoginConfigurerTests {
|
||||
assertThat(exception.getCause()).isInstanceOf(IOException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saml2LoginWhenLoginProcessingUrlWithoutRegistrationIdAndDefaultAuthenticationConverterThenValidates() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.spring.register(CustomLoginProcessingUrlDefaultAuthenticationConverter.class)
|
||||
.autowire())
|
||||
.havingRootCause().isInstanceOf(IllegalStateException.class)
|
||||
.withMessage("loginProcessingUrl must contain {registrationId} path variable");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenCustomLoginProcessingUrlAndCustomAuthenticationConverterThenAuthenticate()
|
||||
throws Exception {
|
||||
this.spring.register(CustomLoginProcessingUrlCustomAuthenticationConverter.class).autowire();
|
||||
RelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.noCredentials()
|
||||
.assertingPartyDetails((party) -> party.verificationX509Credentials(
|
||||
(c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())))
|
||||
.build();
|
||||
String response = new String(Saml2Utils.samlDecode(SIGNED_RESPONSE));
|
||||
given(AUTHENTICATION_CONVERTER.convert(any(HttpServletRequest.class)))
|
||||
.willReturn(new Saml2AuthenticationToken(relyingPartyRegistration, response));
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder request = post("/my/custom/url").param("SAMLResponse", SIGNED_RESPONSE);
|
||||
// @formatter:on
|
||||
this.mvc.perform(request).andExpect(redirectedUrl("/"));
|
||||
verify(AUTHENTICATION_CONVERTER).convert(any(HttpServletRequest.class));
|
||||
}
|
||||
|
||||
private void validateSaml2WebSsoAuthenticationFilterConfiguration() {
|
||||
// get the OpenSamlAuthenticationProvider
|
||||
Saml2WebSsoAuthenticationFilter filter = getSaml2SsoFilter(this.springSecurityFilterChain);
|
||||
@@ -337,10 +371,10 @@ public class Saml2LoginConfigurerTests {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeRequests((authz) -> authz
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.saml2Login(withDefaults());
|
||||
.authorizeRequests((authz) -> authz
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.saml2Login(withDefaults());
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@@ -359,11 +393,11 @@ public class Saml2LoginConfigurerTests {
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeRequests((authz) -> authz
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.saml2Login((saml2) -> {
|
||||
});
|
||||
.authorizeRequests((authz) -> authz
|
||||
.anyRequest().authenticated()
|
||||
)
|
||||
.saml2Login((saml2) -> {
|
||||
});
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@@ -395,6 +429,62 @@ public class Saml2LoginConfigurerTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(Saml2LoginConfigBeans.class)
|
||||
static class CustomAuthenticationConverterBean {
|
||||
|
||||
private final Saml2AuthenticationTokenConverter authenticationConverter = mock(
|
||||
Saml2AuthenticationTokenConverter.class);
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain app(HttpSecurity http) throws Exception {
|
||||
http.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated())
|
||||
.saml2Login(Customizer.withDefaults());
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Saml2AuthenticationTokenConverter authenticationConverter() {
|
||||
return this.authenticationConverter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(Saml2LoginConfigBeans.class)
|
||||
static class CustomLoginProcessingUrlDefaultAuthenticationConverter {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeRequests((authz) -> authz.anyRequest().authenticated())
|
||||
.saml2Login((saml2) -> saml2.loginProcessingUrl("/my/custom/url"));
|
||||
// @formatter:on
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(Saml2LoginConfigBeans.class)
|
||||
static class CustomLoginProcessingUrlCustomAuthenticationConverter {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeRequests((authz) -> authz.anyRequest().authenticated())
|
||||
.saml2Login((saml2) -> saml2
|
||||
.loginProcessingUrl("/my/custom/url")
|
||||
.authenticationConverter(AUTHENTICATION_CONVERTER)
|
||||
);
|
||||
// @formatter:on
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class Saml2LoginConfigBeans {
|
||||
|
||||
@Bean
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2009-2020 the original author or authors.
|
||||
* Copyright 2009-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.
|
||||
@@ -25,6 +25,7 @@ import org.springframework.security.util.InMemoryResource;
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @author Eddú Meléndez
|
||||
* @author Emil Sierżęga
|
||||
*/
|
||||
public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext {
|
||||
|
||||
@@ -42,7 +43,7 @@ public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext
|
||||
+ "http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-2.5.xsd\n"
|
||||
+ "http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security-";
|
||||
static final String BEANS_CLOSE = "</b:beans>\n";
|
||||
static final String SPRING_SECURITY_VERSION = "5.4";
|
||||
static final String SPRING_SECURITY_VERSION = SpringSecurityVersions.getCurrentXsdVersionFromSpringSchemas();
|
||||
|
||||
Resource inMemoryXml;
|
||||
|
||||
|
||||
+57
@@ -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.config.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
|
||||
/**
|
||||
* For computing different Spring Security versions
|
||||
*/
|
||||
public final class SpringSecurityVersions {
|
||||
|
||||
static final Pattern SCHEMA_VERSION_PATTERN = Pattern.compile("\\d+\\.\\d+(\\.\\d+)?");
|
||||
|
||||
public static String getCurrentXsdVersionFromSpringSchemas() {
|
||||
Properties properties = new Properties();
|
||||
try (InputStream is = SpringSecurityCoreVersion.class.getClassLoader()
|
||||
.getResourceAsStream("META-INF/spring.schemas")) {
|
||||
properties.load(is);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException("Could not read 'META-INF/spring.schemas'", ex);
|
||||
}
|
||||
|
||||
String inPackageLocation = properties
|
||||
.getProperty("https://www.springframework.org/schema/security/spring-security.xsd");
|
||||
Matcher matcher = SCHEMA_VERSION_PATTERN.matcher(inPackageLocation);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(0);
|
||||
}
|
||||
throw new IllegalStateException("Failed to find version");
|
||||
}
|
||||
|
||||
private SpringSecurityVersions() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2010-2016 the original author or authors.
|
||||
* Copyright 2010-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.
|
||||
@@ -266,7 +266,7 @@ public abstract class AbstractJaasAuthenticationProvider implements Authenticati
|
||||
* Publishes the {@link JaasAuthenticationFailedEvent}. Can be overridden by
|
||||
* subclasses for different functionality
|
||||
* @param token The authentication token being processed
|
||||
* @param ase The excetion that caused the authentication failure
|
||||
* @param ase The exception that caused the authentication failure
|
||||
*/
|
||||
protected void publishFailureEvent(UsernamePasswordAuthenticationToken token, AuthenticationException ase) {
|
||||
if (this.applicationEventPublisher != null) {
|
||||
|
||||
+1
-1
@@ -224,7 +224,7 @@ public class JaasAuthenticationProvider extends AbstractJaasAuthenticationProvid
|
||||
* Publishes the {@link JaasAuthenticationFailedEvent}. Can be overridden by
|
||||
* subclasses for different functionality
|
||||
* @param token The authentication token being processed
|
||||
* @param ase The excetion that caused the authentication failure
|
||||
* @param ase The exception that caused the authentication failure
|
||||
*/
|
||||
@Override
|
||||
protected void publishFailureEvent(UsernamePasswordAuthenticationToken token, AuthenticationException ase) {
|
||||
|
||||
+1
-1
@@ -40,9 +40,9 @@ import org.springframework.security.core.GrantedAuthority;
|
||||
* <i>Note: This class will save full class name into a property called @class</i>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see CoreJackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
|
||||
+1
-1
@@ -36,8 +36,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
* called @class</i> <i>The cause and stackTrace are ignored in the serialization.</i>
|
||||
*
|
||||
* @author Yannick Lombardi
|
||||
* @see CoreJackson2Module
|
||||
* @since 5.0
|
||||
* @see CoreJackson2Module
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true, value = { "cause", "stackTrace" })
|
||||
|
||||
@@ -44,8 +44,8 @@ import org.springframework.security.core.userdetails.User;
|
||||
* of all security modules.</b>
|
||||
*
|
||||
* @author Jitendra Singh.
|
||||
* @see SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
* @see SecurityJackson2Modules
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CoreJackson2Module extends SimpleModule {
|
||||
|
||||
+1
-1
@@ -47,9 +47,9 @@ import org.springframework.security.core.GrantedAuthority;
|
||||
* called @class</i>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see CoreJackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
|
||||
+1
-1
@@ -32,9 +32,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see CoreJackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
|
||||
@@ -32,10 +32,10 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
* </pre>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 5.0.2
|
||||
* @see UnmodifiableListDeserializer
|
||||
* @see CoreJackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
* @since 5.0.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonDeserialize(using = UnmodifiableListDeserializer.class)
|
||||
|
||||
@@ -32,10 +32,10 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see UnmodifiableSetDeserializer
|
||||
* @see CoreJackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonDeserialize(using = UnmodifiableSetDeserializer.class)
|
||||
|
||||
@@ -37,10 +37,10 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see UserDeserializer
|
||||
* @see CoreJackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonDeserialize(using = UserDeserializer.class)
|
||||
|
||||
+1
-1
@@ -39,9 +39,9 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see CoreJackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -129,7 +129,7 @@ public class DelegatingPasswordEncoderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenNoClosingPrefixStringThenIllegalArgumentExcetion() {
|
||||
public void matchesWhenNoClosingPrefixStringThenIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "{bcrypt" + this.rawPassword))
|
||||
.withMessage("There is no PasswordEncoder mapped for the id \"null\"");
|
||||
|
||||
+11
-11
@@ -8,14 +8,14 @@ javaPlatform {
|
||||
|
||||
dependencies {
|
||||
api platform("org.springframework:spring-framework-bom:$springFrameworkVersion")
|
||||
api platform("io.projectreactor:reactor-bom:2020.0.10")
|
||||
api platform("io.projectreactor:reactor-bom:2020.0.14")
|
||||
api platform("io.rsocket:rsocket-bom:1.1.1")
|
||||
api platform("org.springframework.data:spring-data-bom:2021.0.4")
|
||||
api platform("org.springframework.data:spring-data-bom:2021.0.7")
|
||||
api platform("org.jetbrains.kotlin:kotlin-bom:$kotlinVersion")
|
||||
api platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.1")
|
||||
api platform("com.fasterxml.jackson:jackson-bom:2.12.4")
|
||||
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.5"
|
||||
api "ch.qos.logback:logback-classic:1.2.9"
|
||||
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,12 +49,12 @@ 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.43.v20210629"
|
||||
api "org.eclipse.jetty:jetty-servlet:9.4.43.v20210629"
|
||||
api "org.eclipse.jetty:jetty-server:9.4.44.v20210927"
|
||||
api "org.eclipse.jetty:jetty-servlet:9.4.44.v20210927"
|
||||
api "org.eclipse.persistence:javax.persistence:2.2.1"
|
||||
api "org.hibernate:hibernate-entitymanager:5.4.32.Final"
|
||||
api "org.hsqldb:hsqldb:2.6.0"
|
||||
api "org.jasig.cas.client:cas-client-core:3.6.2"
|
||||
api "org.hibernate:hibernate-entitymanager:5.4.33"
|
||||
api "org.hsqldb:hsqldb:2.6.1"
|
||||
api "org.jasig.cas.client:cas-client-core:3.6.4"
|
||||
api "org.mockito:mockito-core:3.9.0"
|
||||
api "org.openid4java:openid4java-nodeps:0.9.6"
|
||||
api "org.opensaml:opensaml-core:$openSamlVersion"
|
||||
@@ -74,7 +74,7 @@ dependencies {
|
||||
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.4.RELEASE"
|
||||
api "org.springframework.ldap:spring-ldap-core:2.3.5.RELEASE"
|
||||
api "org.synchronoss.cloud:nio-multipart-parser:1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,7 +463,7 @@ You should instead migrate to using `DelegatingPasswordEncoder` to support secur
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
public static NoOpPasswordEncoder passwordEncoder() {
|
||||
public static PasswordEncoder passwordEncoder() {
|
||||
return NoOpPasswordEncoder.getInstance();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -184,7 +184,7 @@ then Spring Security's test support can come in handy.
|
||||
Testing the method above with `WebTestClient` would require simulating some kind of grant flow with an authorization server.
|
||||
Certainly this would be a daunting task, which is why Spring Security ships with support for removing this boilerplate.
|
||||
|
||||
For example, we can tell Spring Security to include a default `OidcUser` using the `SecurityMockServerConfigurers#oidcLogin` method, like so:
|
||||
For example, we can tell Spring Security to include a default `OidcUser` using the `SecurityMockServerConfigurers#mockOidcLogin` method, like so:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -311,7 +311,7 @@ public Mono<String> foo(@AuthenticationPrincipal OAuth2User oauth2User) {
|
||||
}
|
||||
----
|
||||
|
||||
In that case, we can tell Spring Security to include a default `OAuth2User` using the `SecurityMockServerConfigurers#oauth2User` method, like so:
|
||||
In that case, we can tell Spring Security to include a default `OAuth2User` using the `SecurityMockServerConfigurers#mockOAuth2Login` method, like so:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -431,7 +431,7 @@ public Mono<String> foo(@RegisteredOAuth2AuthorizedClient("my-app") OAuth2Author
|
||||
----
|
||||
|
||||
Simulating this handshake with the authorization server could be cumbersome.
|
||||
Instead, you can use `SecurityMockServerConfigurers#oauth2Client` to add a `OAuth2AuthorizedClient` into a mock `ServerOAuth2AuthorizedClientRepository`:
|
||||
Instead, you can use `SecurityMockServerConfigurers#mockOAuth2Client` to add a `OAuth2AuthorizedClient` into a mock `ServerOAuth2AuthorizedClientRepository`:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -541,7 +541,7 @@ We'll look at two of them now:
|
||||
==== `mockJwt() WebTestClientConfigurer`
|
||||
|
||||
The first way is via a `WebTestClientConfigurer`.
|
||||
The simplest of these would look something like this:
|
||||
The simplest of these would be to use the `SecurityMockServerConfigurers#mockJwt` method like the following:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -664,7 +664,7 @@ public Mono<String> foo(BearerTokenAuthentication authentication) {
|
||||
}
|
||||
----
|
||||
|
||||
In that case, we can tell Spring Security to include a default `BearerTokenAuthentication` using the `SecurityMockServerConfigurers#opaqueToken` method, like so:
|
||||
In that case, we can tell Spring Security to include a default `BearerTokenAuthentication` using the `SecurityMockServerConfigurers#mockOpaqueToken` method, like so:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
|
||||
@@ -125,7 +125,7 @@ This should therefore work with all major databases.
|
||||
At the time of writing, the system had been successfully tested using Hypersonic SQL, PostgreSQL, Microsoft SQL Server and Oracle.
|
||||
|
||||
Two samples ship with Spring Security that demonstrate the ACL module.
|
||||
The first is the Contacts Sample, and the other is the Document Management System (DMS) Sample.
|
||||
The first is the {gh-samples-url}/servlet/xml/java/contacts[Contacts Sample], and the other is the {gh-samples-url}/servlet/xml/java/dms[Document Management System (DMS) Sample].
|
||||
We suggest taking a look over these for examples.
|
||||
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ Their use is enabled through the `global-method-security` namespace element:
|
||||
|
||||
===== Access Control using @PreAuthorize and @PostAuthorize
|
||||
The most obviously useful annotation is `@PreAuthorize` which decides whether a method can actually be invoked or not.
|
||||
For example (from the "Contacts" sample application)
|
||||
For example (from the {gh-samples-url}/servlet/xml/java/contacts[Contacts] sample application)
|
||||
|
||||
====
|
||||
.Java
|
||||
@@ -492,7 +492,7 @@ This would look something like this:
|
||||
|
||||
Where `myPermissionEvaluator` is the bean which implements `PermissionEvaluator`.
|
||||
Usually this will be the implementation from the ACL module which is called `AclPermissionEvaluator`.
|
||||
See the "Contacts" sample application configuration for more details.
|
||||
See the {gh-samples-url}/servlet/xml/java/contacts[Contacts] sample application configuration for more details.
|
||||
|
||||
===== Method Security Meta Annotations
|
||||
|
||||
|
||||
@@ -2641,7 +2641,7 @@ Next, we can construct a `JWTProcessor`:
|
||||
JWTProcessor jwtProcessor(JWTClaimSetJWSKeySelector keySelector) {
|
||||
ConfigurableJWTProcessor<SecurityContext> jwtProcessor =
|
||||
new DefaultJWTProcessor();
|
||||
jwtProcessor.setJWTClaimSetJWSKeySelector(keySelector);
|
||||
jwtProcessor.setJWTClaimsSetAwareJWSKeySelector(keySelector);
|
||||
return jwtProcessor;
|
||||
}
|
||||
----
|
||||
@@ -2734,9 +2734,9 @@ Now that we have a tenant-aware processor and a tenant-aware validator, we can p
|
||||
----
|
||||
@Bean
|
||||
JwtDecoder jwtDecoder(JWTProcessor jwtProcessor, OAuth2TokenValidator<Jwt> jwtValidator) {
|
||||
NimbusJwtDecoder decoder = new NimbusJwtDecoder(processor);
|
||||
NimbusJwtDecoder decoder = new NimbusJwtDecoder(jwtProcessor);
|
||||
OAuth2TokenValidator<Jwt> validator = new DelegatingOAuth2TokenValidator<>
|
||||
(JwtValidators.createDefault(), this.jwtValidator);
|
||||
(JwtValidators.createDefault(), jwtValidator);
|
||||
decoder.setJwtValidator(validator);
|
||||
return decoder;
|
||||
}
|
||||
|
||||
@@ -739,7 +739,7 @@ But, if you do need something from the request, then you can use create a custom
|
||||
----
|
||||
@Component
|
||||
public class AuthnRequestConverter implements
|
||||
Converter<MySaml2AuthenticationRequestContext, AuthnRequest> {
|
||||
Converter<Saml2AuthenticationRequestContext, AuthnRequest> {
|
||||
|
||||
private final AuthnRequestBuilder authnRequestBuilder;
|
||||
private final IssuerBuilder issuerBuilder;
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
aspectjVersion=1.9.7
|
||||
springJavaformatVersion=0.0.28
|
||||
springJavaformatVersion=0.0.29
|
||||
springBootVersion=2.4.2
|
||||
springFrameworkVersion=5.3.9
|
||||
springFrameworkVersion=5.3.14
|
||||
openSamlVersion=3.4.6
|
||||
version=5.5.2
|
||||
kotlinVersion=1.5.21
|
||||
version=5.5.4
|
||||
kotlinVersion=1.5.32
|
||||
samplesBranch=5.5.x
|
||||
org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError
|
||||
org.gradle.parallel=true
|
||||
|
||||
+1
-1
@@ -29,9 +29,9 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* Simple expression configuration attribute for use in {@link Message} authorizations.
|
||||
*
|
||||
* @since 4.0
|
||||
* @author Rob Winch
|
||||
* @author Daniel Bustamante Ospina
|
||||
* @since 4.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
class MessageExpressionConfigAttribute implements ConfigAttribute, EvaluationContextPostProcessor<Message<?>> {
|
||||
|
||||
@@ -25,7 +25,7 @@ dependencies {
|
||||
testImplementation 'io.projectreactor.tools:blockhound'
|
||||
testImplementation 'org.skyscreamer:jsonassert'
|
||||
testImplementation 'io.r2dbc:r2dbc-h2:0.8.4.RELEASE'
|
||||
testImplementation 'io.r2dbc:r2dbc-spi-test:0.8.5.RELEASE'
|
||||
testImplementation 'io.r2dbc:r2dbc-spi-test:0.8.6.RELEASE'
|
||||
|
||||
testRuntimeOnly 'org.hsqldb:hsqldb'
|
||||
|
||||
|
||||
+1
-1
@@ -58,7 +58,6 @@ import org.springframework.util.CollectionUtils;
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @deprecated Use {@link DefaultAuthorizationCodeTokenResponseClient}
|
||||
* @see OAuth2AccessTokenResponseClient
|
||||
* @see OAuth2AuthorizationCodeGrantRequest
|
||||
* @see OAuth2AccessTokenResponse
|
||||
@@ -71,6 +70,7 @@ import org.springframework.util.CollectionUtils;
|
||||
* @see <a target="_blank" href=
|
||||
* "https://tools.ietf.org/html/rfc6749#section-4.1.4">Section 4.1.4 Access Token Response
|
||||
* (Authorization Code Grant)</a>
|
||||
* @deprecated Use {@link DefaultAuthorizationCodeTokenResponseClient}
|
||||
*/
|
||||
@Deprecated
|
||||
public class NimbusAuthorizationCodeTokenResponseClient
|
||||
|
||||
+2
-2
@@ -112,8 +112,8 @@ public final class ClientRegistration implements Serializable {
|
||||
|
||||
/**
|
||||
* Returns the uri (or uri template) for the redirection endpoint.
|
||||
* @deprecated Use {@link #getRedirectUri()} instead
|
||||
* @return the uri (or uri template) for the redirection endpoint
|
||||
* @deprecated Use {@link #getRedirectUri()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public String getRedirectUriTemplate() {
|
||||
@@ -445,10 +445,10 @@ public final class ClientRegistration implements Serializable {
|
||||
|
||||
/**
|
||||
* Sets the uri (or uri template) for the redirection endpoint.
|
||||
* @deprecated Use {@link #redirectUri(String)} instead
|
||||
* @param redirectUriTemplate the uri (or uri template) for the redirection
|
||||
* endpoint
|
||||
* @return the {@link Builder}
|
||||
* @deprecated Use {@link #redirectUri(String)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public Builder redirectUriTemplate(String redirectUriTemplate) {
|
||||
|
||||
+6
-6
@@ -42,18 +42,18 @@ import org.springframework.web.client.RestTemplate;
|
||||
* {@link OAuth2User} type(s) keyed by {@code String}, which represents the
|
||||
* {@link ClientRegistration#getRegistrationId() Registration Id} of the Client.
|
||||
*
|
||||
* @deprecated It is recommended to use a delegation-based strategy of an
|
||||
* {@link OAuth2UserService} to support custom {@link OAuth2User} types, as it provides
|
||||
* much greater flexibility compared to this implementation. See the
|
||||
* <a target="_blank" href=
|
||||
* "https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2login-advanced-map-authorities-oauth2userservice">reference
|
||||
* manual</a> for details on how to implement.
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see OAuth2UserService
|
||||
* @see OAuth2UserRequest
|
||||
* @see OAuth2User
|
||||
* @see ClientRegistration
|
||||
* @deprecated It is recommended to use a delegation-based strategy of an
|
||||
* {@link OAuth2UserService} to support custom {@link OAuth2User} types, as it provides
|
||||
* much greater flexibility compared to this implementation. See the
|
||||
* <a target="_blank" href=
|
||||
* "https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2login-advanced-map-authorities-oauth2userservice">reference
|
||||
* manual</a> for details on how to implement.
|
||||
*/
|
||||
@Deprecated
|
||||
public class CustomUserTypesOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
|
||||
|
||||
+2
-2
@@ -27,13 +27,13 @@ import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
* {@link OAuth2UserRequest#getClientRegistration() Client} and returning an
|
||||
* {@link AuthenticatedPrincipal} in the form of an {@link OAuth2User}.
|
||||
*
|
||||
* @param <R> The type of OAuth 2.0 User Request
|
||||
* @param <U> The type of OAuth 2.0 User
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see OAuth2UserRequest
|
||||
* @see OAuth2User
|
||||
* @see AuthenticatedPrincipal
|
||||
* @param <R> The type of OAuth 2.0 User Request
|
||||
* @param <U> The type of OAuth 2.0 User
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface OAuth2UserService<R extends OAuth2UserRequest, U extends OAuth2User> {
|
||||
|
||||
+2
-2
@@ -29,13 +29,13 @@ import org.springframework.security.oauth2.core.user.OAuth2User;
|
||||
* {@link OAuth2UserRequest#getClientRegistration() Client} and returning an
|
||||
* {@link AuthenticatedPrincipal} in the form of an {@link OAuth2User}.
|
||||
*
|
||||
* @param <R> The type of OAuth 2.0 User Request
|
||||
* @param <U> The type of OAuth 2.0 User
|
||||
* @author Rob Winch
|
||||
* @since 5.1
|
||||
* @see OAuth2UserRequest
|
||||
* @see OAuth2User
|
||||
* @see AuthenticatedPrincipal
|
||||
* @param <R> The type of OAuth 2.0 User Request
|
||||
* @param <U> The type of OAuth 2.0 User
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ReactiveOAuth2UserService<R extends OAuth2UserRequest, U extends OAuth2User> {
|
||||
|
||||
+3
-3
@@ -59,12 +59,12 @@ public interface AuthorizationRequestRepository<T extends OAuth2AuthorizationReq
|
||||
/**
|
||||
* Removes and returns the {@link OAuth2AuthorizationRequest} associated to the
|
||||
* provided {@code HttpServletRequest} or if not available returns {@code null}.
|
||||
* @deprecated Use
|
||||
* {@link #removeAuthorizationRequest(HttpServletRequest, HttpServletResponse)}
|
||||
* instead
|
||||
* @param request the {@code HttpServletRequest}
|
||||
* @return the removed {@link OAuth2AuthorizationRequest} or {@code null} if not
|
||||
* available
|
||||
* @deprecated Use
|
||||
* {@link #removeAuthorizationRequest(HttpServletRequest, HttpServletResponse)}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
T removeAuthorizationRequest(HttpServletRequest request);
|
||||
|
||||
+3
-3
@@ -156,6 +156,9 @@ public final class OAuth2AuthorizedClientArgumentResolver implements HandlerMeth
|
||||
/**
|
||||
* Sets the client used when requesting an access token credential at the Token
|
||||
* Endpoint for the {@code client_credentials} grant.
|
||||
* @param clientCredentialsTokenResponseClient the client used when requesting an
|
||||
* access token credential at the Token Endpoint for the {@code client_credentials}
|
||||
* grant
|
||||
* @deprecated Use
|
||||
* {@link #OAuth2AuthorizedClientArgumentResolver(OAuth2AuthorizedClientManager)}
|
||||
* instead. Create an instance of
|
||||
@@ -165,9 +168,6 @@ public final class OAuth2AuthorizedClientArgumentResolver implements HandlerMeth
|
||||
* to
|
||||
* {@link DefaultOAuth2AuthorizedClientManager#setAuthorizedClientProvider(OAuth2AuthorizedClientProvider)
|
||||
* DefaultOAuth2AuthorizedClientManager}.
|
||||
* @param clientCredentialsTokenResponseClient the client used when requesting an
|
||||
* access token credential at the Token Endpoint for the {@code client_credentials}
|
||||
* grant
|
||||
*/
|
||||
@Deprecated
|
||||
public void setClientCredentialsTokenResponseClient(
|
||||
|
||||
+2
-2
@@ -355,6 +355,7 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
|
||||
/**
|
||||
* Sets the {@link ReactiveOAuth2AccessTokenResponseClient} used for getting an
|
||||
* {@link OAuth2AuthorizedClient} for the client_credentials grant.
|
||||
* @param clientCredentialsTokenResponseClient the client to use
|
||||
* @deprecated Use
|
||||
* {@link #ServerOAuth2AuthorizedClientExchangeFilterFunction(ReactiveOAuth2AuthorizedClientManager)}
|
||||
* instead. Create an instance of
|
||||
@@ -364,7 +365,6 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
|
||||
* supply it to
|
||||
* {@link DefaultReactiveOAuth2AuthorizedClientManager#setAuthorizedClientProvider(ReactiveOAuth2AuthorizedClientProvider)
|
||||
* DefaultReactiveOAuth2AuthorizedClientManager}.
|
||||
* @param clientCredentialsTokenResponseClient the client to use
|
||||
*/
|
||||
@Deprecated
|
||||
public void setClientCredentialsTokenResponseClient(
|
||||
@@ -407,13 +407,13 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
|
||||
/**
|
||||
* An access token will be considered expired by comparing its expiration to now +
|
||||
* this skewed Duration. The default is 1 minute.
|
||||
* @param accessTokenExpiresSkew the Duration to use.
|
||||
* @deprecated The {@code accessTokenExpiresSkew} should be configured with the
|
||||
* specific {@link ReactiveOAuth2AuthorizedClientProvider} implementation, e.g.
|
||||
* {@link ClientCredentialsReactiveOAuth2AuthorizedClientProvider#setClockSkew(Duration)
|
||||
* ClientCredentialsReactiveOAuth2AuthorizedClientProvider} or
|
||||
* {@link RefreshTokenReactiveOAuth2AuthorizedClientProvider#setClockSkew(Duration)
|
||||
* RefreshTokenReactiveOAuth2AuthorizedClientProvider}.
|
||||
* @param accessTokenExpiresSkew the Duration to use.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setAccessTokenExpiresSkew(Duration accessTokenExpiresSkew) {
|
||||
|
||||
+2
-2
@@ -239,6 +239,7 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
||||
/**
|
||||
* Sets the {@link OAuth2AccessTokenResponseClient} used for getting an
|
||||
* {@link OAuth2AuthorizedClient} for the client_credentials grant.
|
||||
* @param clientCredentialsTokenResponseClient the client to use
|
||||
* @deprecated Use
|
||||
* {@link #ServletOAuth2AuthorizedClientExchangeFilterFunction(OAuth2AuthorizedClientManager)}
|
||||
* instead. Create an instance of
|
||||
@@ -248,7 +249,6 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
||||
* to
|
||||
* {@link DefaultOAuth2AuthorizedClientManager#setAuthorizedClientProvider(OAuth2AuthorizedClientProvider)
|
||||
* DefaultOAuth2AuthorizedClientManager}.
|
||||
* @param clientCredentialsTokenResponseClient the client to use
|
||||
*/
|
||||
@Deprecated
|
||||
public void setClientCredentialsTokenResponseClient(
|
||||
@@ -397,13 +397,13 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
||||
/**
|
||||
* An access token will be considered expired by comparing its expiration to now +
|
||||
* this skewed Duration. The default is 1 minute.
|
||||
* @param accessTokenExpiresSkew the Duration to use.
|
||||
* @deprecated The {@code accessTokenExpiresSkew} should be configured with the
|
||||
* specific {@link OAuth2AuthorizedClientProvider} implementation, e.g.
|
||||
* {@link ClientCredentialsOAuth2AuthorizedClientProvider#setClockSkew(Duration)
|
||||
* ClientCredentialsOAuth2AuthorizedClientProvider} or
|
||||
* {@link RefreshTokenOAuth2AuthorizedClientProvider#setClockSkew(Duration)
|
||||
* RefreshTokenOAuth2AuthorizedClientProvider}.
|
||||
* @param accessTokenExpiresSkew the Duration to use.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setAccessTokenExpiresSkew(Duration accessTokenExpiresSkew) {
|
||||
|
||||
+2
-2
@@ -35,10 +35,10 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* {@link ServerWebExchange} is null and that the {@link Authentication} is either null or
|
||||
* anonymous to prevent using it incorrectly.
|
||||
*
|
||||
* @deprecated Use {@link AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager}
|
||||
* instead
|
||||
* @author Rob Winch
|
||||
* @since 5.1
|
||||
* @deprecated Use {@link AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class UnAuthenticatedServerOAuth2AuthorizedClientRepository implements ServerOAuth2AuthorizedClientRepository {
|
||||
|
||||
+14
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -42,11 +42,10 @@ public interface ClaimAccessor {
|
||||
/**
|
||||
* Returns the claim value as a {@code T} type. The claim value is expected to be of
|
||||
* type {@code T}.
|
||||
*
|
||||
* @since 5.2
|
||||
* @param claim the name of the claim
|
||||
* @param <T> the type of the claim value
|
||||
* @return the claim value
|
||||
* @since 5.2
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default <T> T getClaim(String claim) {
|
||||
@@ -133,11 +132,13 @@ public interface ClaimAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the claim value as a {@code Map<String, Object>} or {@code null} if it does
|
||||
* not exist or cannot be assigned to a {@code Map}.
|
||||
* Returns the claim value as a {@code Map<String, Object>} or {@code null} if the
|
||||
* claim does not exist.
|
||||
* @param claim the name of the claim
|
||||
* @return the claim value or {@code null} if it does not exist or cannot be assigned
|
||||
* to a {@code Map}
|
||||
* @return the claim value or {@code null} if the claim does not exist
|
||||
* @throws IllegalArgumentException if the claim value cannot be converted to a
|
||||
* {@code List}
|
||||
* @throws NullPointerException if the claim value is {@code null}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default Map<String, Object> getClaimAsMap(String claim) {
|
||||
@@ -156,11 +157,13 @@ public interface ClaimAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the claim value as a {@code List<String>} or {@code null} if it does not
|
||||
* exist or cannot be assigned to a {@code List}.
|
||||
* Returns the claim value as a {@code List<String>} or {@code null} if the claim does
|
||||
* not exist.
|
||||
* @param claim the name of the claim
|
||||
* @return the claim value or {@code null} if it does not exist or cannot be assigned
|
||||
* to a {@code List}
|
||||
* @return the claim value or {@code null} if the claim does not exist
|
||||
* @throws IllegalArgumentException if the claim value cannot be converted to a
|
||||
* {@code List}
|
||||
* @throws NullPointerException if the claim value is {@code null}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
default List<String> getClaimAsStringList(String claim) {
|
||||
|
||||
+1
@@ -34,6 +34,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Joe Grandja
|
||||
* @author Nikita Konev
|
||||
* @since 5.3
|
||||
* @deprecated Use {@link DefaultMapOAuth2AccessTokenResponseConverter} instead
|
||||
*/
|
||||
public final class MapOAuth2AccessTokenResponseConverter
|
||||
implements Converter<Map<String, String>, OAuth2AccessTokenResponse> {
|
||||
|
||||
+1
@@ -32,6 +32,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Joe Grandja
|
||||
* @author Nikita Konev
|
||||
* @since 5.3
|
||||
* @deprecated Use {@link DefaultOAuth2AccessTokenResponseMapConverter} instead
|
||||
*/
|
||||
public final class OAuth2AccessTokenResponseMapConverter
|
||||
implements Converter<OAuth2AccessTokenResponse, Map<String, String>> {
|
||||
|
||||
+3
@@ -110,6 +110,7 @@ public class OAuth2AccessTokenResponseHttpMessageConverter
|
||||
* parameters to an {@link OAuth2AccessTokenResponse}.
|
||||
* @param tokenResponseConverter the {@link Converter} used for converting to an
|
||||
* {@link OAuth2AccessTokenResponse}
|
||||
* @deprecated Use {@link #setAccessTokenResponseConverter(Converter)} instead
|
||||
*/
|
||||
public final void setTokenResponseConverter(
|
||||
Converter<Map<String, String>, OAuth2AccessTokenResponse> tokenResponseConverter) {
|
||||
@@ -123,6 +124,8 @@ public class OAuth2AccessTokenResponseHttpMessageConverter
|
||||
* Access Token Response parameters.
|
||||
* @param tokenResponseParametersConverter the {@link Converter} used for converting
|
||||
* to a {@code Map} representation of the Access Token Response parameters
|
||||
* @deprecated Use {@link #setAccessTokenResponseParametersConverter(Converter)}
|
||||
* instead
|
||||
*/
|
||||
public final void setTokenResponseParametersConverter(
|
||||
Converter<OAuth2AccessTokenResponse, Map<String, String>> tokenResponseParametersConverter) {
|
||||
|
||||
+2
-2
@@ -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.
|
||||
@@ -54,7 +54,7 @@ public final class JwtClaimValidator<T> implements OAuth2TokenValidator<Jwt> {
|
||||
Assert.notNull(test, "test can not be null");
|
||||
this.claim = claim;
|
||||
this.test = test;
|
||||
this.error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, "The " + this.claim + " claim is not valid",
|
||||
this.error = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, "The " + this.claim + " claim is not valid",
|
||||
"https://tools.ietf.org/html/rfc6750#section-3.1");
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -20,11 +20,11 @@ package org.springframework.security.oauth2.jwt;
|
||||
* A factory for {@link JwtDecoder}(s). This factory should be supplied with a type that
|
||||
* provides contextual information used to create a specific {@code JwtDecoder}.
|
||||
*
|
||||
* @param <C> The type that provides contextual information used to create a specific
|
||||
* {@code JwtDecoder}.
|
||||
* @author Joe Grandja
|
||||
* @since 5.2
|
||||
* @see JwtDecoder
|
||||
* @param <C> The type that provides contextual information used to create a specific
|
||||
* {@code JwtDecoder}.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface JwtDecoderFactory<C> {
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ public final class JwtTimestampValidator implements OAuth2TokenValidator<Jwt> {
|
||||
|
||||
private OAuth2Error createOAuth2Error(String reason) {
|
||||
this.logger.debug(reason);
|
||||
return new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, reason,
|
||||
return new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, reason,
|
||||
"https://tools.ietf.org/html/rfc6750#section-3.1");
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -35,7 +35,6 @@ import org.springframework.web.client.RestOperations;
|
||||
* <p>
|
||||
* <b>NOTE:</b> This implementation uses the Nimbus JOSE + JWT SDK internally.
|
||||
*
|
||||
* @deprecated Use {@link NimbusJwtDecoder} or {@link JwtDecoders} instead
|
||||
* @author Joe Grandja
|
||||
* @author Josh Cummings
|
||||
* @since 5.0
|
||||
@@ -49,6 +48,7 @@ import org.springframework.web.client.RestOperations;
|
||||
* (JWK)</a>
|
||||
* @see <a target="_blank" href="https://connect2id.com/products/nimbus-jose-jwt">Nimbus
|
||||
* JOSE + JWT SDK</a>
|
||||
* @deprecated Use {@link NimbusJwtDecoder} or {@link JwtDecoders} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public final class NimbusJwtDecoderJwkSupport implements JwtDecoder {
|
||||
|
||||
+2
-2
@@ -21,11 +21,11 @@ package org.springframework.security.oauth2.jwt;
|
||||
* type that provides contextual information used to create a specific
|
||||
* {@code ReactiveJwtDecoder}.
|
||||
*
|
||||
* @param <C> The type that provides contextual information used to create a specific
|
||||
* {@code ReactiveJwtDecoder}.
|
||||
* @author Joe Grandja
|
||||
* @since 5.2
|
||||
* @see ReactiveJwtDecoder
|
||||
* @param <C> The type that provides contextual information used to create a specific
|
||||
* {@code ReactiveJwtDecoder}.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ReactiveJwtDecoderFactory<C> {
|
||||
|
||||
+7
-1
@@ -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.
|
||||
@@ -16,10 +16,14 @@
|
||||
|
||||
package org.springframework.security.oauth2.jwt;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -45,7 +49,9 @@ public class JwtClaimValidatorTests {
|
||||
@Test
|
||||
public void validateWhenClaimFailsTheTestThenReturnsFailure() {
|
||||
Jwt jwt = TestJwts.jwt().claim(JwtClaimNames.ISS, "http://abc").build();
|
||||
Collection<OAuth2Error> details = this.validator.validate(jwt).getErrors();
|
||||
assertThat(this.validator.validate(jwt).getErrors().isEmpty()).isFalse();
|
||||
assertThat(details).allMatch((error) -> Objects.equals(error.getErrorCode(), OAuth2ErrorCodes.INVALID_TOKEN));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+6
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -23,11 +23,13 @@ import java.time.ZoneId;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
|
||||
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
|
||||
|
||||
@@ -63,6 +65,7 @@ public class JwtTimestampValidatorTests {
|
||||
.collect(Collectors.toList());
|
||||
// @formatter:on
|
||||
assertThat(messages).contains("Jwt expired at " + oneHourAgo);
|
||||
assertThat(details).allMatch((error) -> Objects.equals(error.getErrorCode(), OAuth2ErrorCodes.INVALID_TOKEN));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -77,6 +80,7 @@ public class JwtTimestampValidatorTests {
|
||||
.collect(Collectors.toList());
|
||||
// @formatter:on
|
||||
assertThat(messages).contains("Jwt used before " + oneHourFromNow);
|
||||
assertThat(details).allMatch((error) -> Objects.equals(error.getErrorCode(), OAuth2ErrorCodes.INVALID_TOKEN));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -109,6 +113,7 @@ public class JwtTimestampValidatorTests {
|
||||
.collect(Collectors.toList());
|
||||
// @formatter:on
|
||||
assertThat(result.hasErrors()).isTrue();
|
||||
assertThat(result.getErrors().iterator().next().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);
|
||||
assertThat(messages).contains("Jwt used before " + justOverOneDayFromNow);
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.security.oauth2.server.resource.authentication;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -186,7 +187,7 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
|
||||
return this.authenticationManagers.computeIfAbsent(issuer,
|
||||
(k) -> Mono.<ReactiveAuthenticationManager>fromCallable(() -> new JwtReactiveAuthenticationManager(ReactiveJwtDecoders.fromIssuerLocation(k)))
|
||||
.subscribeOn(Schedulers.boundedElastic())
|
||||
.cache()
|
||||
.cache((manager) -> Duration.ofMillis(Long.MAX_VALUE), (ex) -> Duration.ZERO, () -> Duration.ZERO)
|
||||
);
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
+1
@@ -33,6 +33,7 @@ import org.springframework.security.oauth2.core.ClaimAccessor;
|
||||
* @see OAuth2IntrospectionAuthenticatedPrincipal
|
||||
* @see <a target="_blank" href=
|
||||
* "https://tools.ietf.org/html/rfc7662#section-2.2">Introspection Response</a>
|
||||
* @deprecated Use {@link OAuth2TokenIntrospectionClaimAccessor} instead
|
||||
*/
|
||||
public interface OAuth2IntrospectionClaimAccessor extends ClaimAccessor {
|
||||
|
||||
|
||||
+1
@@ -23,6 +23,7 @@ package org.springframework.security.oauth2.server.resource.introspection;
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 5.2
|
||||
* @deprecated Use {@link OAuth2TokenIntrospectionClaimNames} instead
|
||||
*/
|
||||
public interface OAuth2IntrospectionClaimNames {
|
||||
|
||||
|
||||
+38
@@ -96,6 +96,44 @@ public class JwtIssuerAuthenticationManagerResolverTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveWhednUsingTrustedIssuerThenReturnsAuthenticationManager() throws Exception {
|
||||
try (MockWebServer server = new MockWebServer()) {
|
||||
server.start();
|
||||
String issuer = server.url("").toString();
|
||||
// @formatter:off
|
||||
server.enqueue(new MockResponse().setResponseCode(500)
|
||||
.setHeader("Content-Type", "application/json")
|
||||
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer))
|
||||
);
|
||||
server.enqueue(new MockResponse().setResponseCode(200)
|
||||
.setHeader("Content-Type", "application/json")
|
||||
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer))
|
||||
);
|
||||
server.enqueue(new MockResponse().setResponseCode(200)
|
||||
.setHeader("Content-Type", "application/json")
|
||||
.setBody(JWK_SET)
|
||||
);
|
||||
server.enqueue(new MockResponse().setResponseCode(200)
|
||||
.setHeader("Content-Type", "application/json")
|
||||
.setBody(JWK_SET)
|
||||
);
|
||||
// @formatter:on
|
||||
JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
|
||||
new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
|
||||
jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
|
||||
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
|
||||
issuer);
|
||||
Authentication token = withBearerToken(jws.serialize());
|
||||
AuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null);
|
||||
assertThat(authenticationManager).isNotNull();
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> authenticationManager.authenticate(token));
|
||||
Authentication authentication = authenticationManager.authenticate(token);
|
||||
assertThat(authentication.isAuthenticated()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveWhenUsingSameIssuerThenReturnsSameAuthenticationManager() throws Exception {
|
||||
try (MockWebServer server = new MockWebServer()) {
|
||||
|
||||
+30
@@ -95,6 +95,36 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
|
||||
}
|
||||
}
|
||||
|
||||
// gh-10444
|
||||
@Test
|
||||
public void resolveWhednUsingTrustedIssuerThenReturnsAuthenticationManager() throws Exception {
|
||||
try (MockWebServer server = new MockWebServer()) {
|
||||
String issuer = server.url("").toString();
|
||||
// @formatter:off
|
||||
server.enqueue(new MockResponse().setResponseCode(500).setHeader("Content-Type", "application/json")
|
||||
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)));
|
||||
server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json")
|
||||
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)));
|
||||
server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json")
|
||||
.setBody(JWK_SET));
|
||||
server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json")
|
||||
.setBody(JWK_SET));
|
||||
// @formatter:on
|
||||
JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
|
||||
new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
|
||||
jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
|
||||
JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
|
||||
issuer);
|
||||
ReactiveAuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null).block();
|
||||
assertThat(authenticationManager).isNotNull();
|
||||
Authentication token = withBearerToken(jws.serialize());
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> authenticationManager.authenticate(token).block());
|
||||
Authentication authentication = authenticationManager.authenticate(token).block();
|
||||
assertThat(authentication.isAuthenticated()).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveWhenUsingSameIssuerThenReturnsSameAuthenticationManager() throws Exception {
|
||||
try (MockWebServer server = new MockWebServer()) {
|
||||
|
||||
@@ -43,12 +43,12 @@ import org.openid4java.message.ax.FetchResponse;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Ray Krueger
|
||||
* @author Luke Taylor
|
||||
* @deprecated The OpenID 1.0 and 2.0 protocols have been deprecated and users are
|
||||
* <a href="https://openid.net/specs/openid-connect-migration-1_0.html">encouraged to
|
||||
* migrate</a> to <a href="https://openid.net/connect/">OpenID Connect</a>, which is
|
||||
* supported by <code>spring-security-oauth2</code>.
|
||||
* @author Ray Krueger
|
||||
* @author Luke Taylor
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
+4
-4
@@ -126,6 +126,10 @@ final class OpenSamlSigningUtils {
|
||||
return credentials;
|
||||
}
|
||||
|
||||
private OpenSamlSigningUtils() {
|
||||
|
||||
}
|
||||
|
||||
static class QueryParametersPartial {
|
||||
|
||||
final RelyingPartyRegistration registration;
|
||||
@@ -166,8 +170,4 @@ final class OpenSamlSigningUtils {
|
||||
|
||||
}
|
||||
|
||||
private OpenSamlSigningUtils() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-4
@@ -85,6 +85,10 @@ final class OpenSamlVerificationUtils {
|
||||
DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());
|
||||
}
|
||||
|
||||
private OpenSamlVerificationUtils() {
|
||||
|
||||
}
|
||||
|
||||
static class VerifierPartial {
|
||||
|
||||
private final String id;
|
||||
@@ -210,8 +214,4 @@ final class OpenSamlVerificationUtils {
|
||||
|
||||
}
|
||||
|
||||
private OpenSamlVerificationUtils() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-8
@@ -63,23 +63,21 @@ public class Saml2WebSsoAuthenticationFilter extends AbstractAuthenticationProce
|
||||
String filterProcessesUrl) {
|
||||
this(new Saml2AuthenticationTokenConverter(
|
||||
new DefaultRelyingPartyRegistrationResolver(relyingPartyRegistrationRepository)), filterProcessesUrl);
|
||||
Assert.isTrue(filterProcessesUrl.contains("{registrationId}"),
|
||||
"filterProcessesUrl must contain a {registrationId} match variable");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Saml2WebSsoAuthenticationFilter} given the provided parameters
|
||||
* @param authenticationConverter the strategy for converting an
|
||||
* {@link HttpServletRequest} into an {@link Authentication}
|
||||
* @param filterProcessingUrl the processing URL, must contain a {registrationId}
|
||||
* variable
|
||||
* @param filterProcessesUrl the processing URL
|
||||
* @since 5.4
|
||||
*/
|
||||
public Saml2WebSsoAuthenticationFilter(AuthenticationConverter authenticationConverter,
|
||||
String filterProcessingUrl) {
|
||||
super(filterProcessingUrl);
|
||||
public Saml2WebSsoAuthenticationFilter(AuthenticationConverter authenticationConverter, String filterProcessesUrl) {
|
||||
super(filterProcessesUrl);
|
||||
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
|
||||
Assert.hasText(filterProcessingUrl, "filterProcessesUrl must contain a URL pattern");
|
||||
Assert.isTrue(filterProcessingUrl.contains("{registrationId}"),
|
||||
"filterProcessesUrl must contain a {registrationId} match variable");
|
||||
Assert.hasText(filterProcessesUrl, "filterProcessesUrl must contain a URL pattern");
|
||||
this.authenticationConverter = authenticationConverter;
|
||||
setAllowSessionCreation(true);
|
||||
setSessionAuthenticationStrategy(new ChangeSessionIdAuthenticationStrategy());
|
||||
|
||||
+8
-1
@@ -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.
|
||||
@@ -26,6 +26,7 @@ import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
@@ -60,6 +61,12 @@ public class Saml2WebSsoAuthenticationFilterTests {
|
||||
this.filter = new Saml2WebSsoAuthenticationFilter(this.repository, "/url/variable/is/present/{registrationId}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructingFilterWithMissingRegistrationIdVariableAndCustomAuthenticationConverterThenSucceeds() {
|
||||
AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);
|
||||
this.filter = new Saml2WebSsoAuthenticationFilter(authenticationConverter, "/url/missing/variable");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requiresAuthenticationWhenHappyPathThenReturnsTrue() {
|
||||
Assert.assertTrue(this.filter.requiresAuthentication(this.request, this.response));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
* 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.
|
||||
@@ -29,6 +29,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -78,10 +79,19 @@ public class FilterInvocation {
|
||||
}
|
||||
|
||||
public FilterInvocation(String contextPath, String servletPath, String method) {
|
||||
this(contextPath, servletPath, null, null, method);
|
||||
this(contextPath, servletPath, method, null);
|
||||
}
|
||||
|
||||
public FilterInvocation(String contextPath, String servletPath, String method, ServletContext servletContext) {
|
||||
this(contextPath, servletPath, null, null, method, servletContext);
|
||||
}
|
||||
|
||||
public FilterInvocation(String contextPath, String servletPath, String pathInfo, String query, String method) {
|
||||
this(contextPath, servletPath, pathInfo, query, method, null);
|
||||
}
|
||||
|
||||
public FilterInvocation(String contextPath, String servletPath, String pathInfo, String query, String method,
|
||||
ServletContext servletContext) {
|
||||
DummyRequest request = new DummyRequest();
|
||||
contextPath = (contextPath != null) ? contextPath : "/cp";
|
||||
request.setContextPath(contextPath);
|
||||
@@ -90,6 +100,7 @@ public class FilterInvocation {
|
||||
request.setPathInfo(pathInfo);
|
||||
request.setQueryString(query);
|
||||
request.setMethod(method);
|
||||
request.setServletContext(servletContext);
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@@ -160,6 +171,8 @@ public class FilterInvocation {
|
||||
|
||||
private String method;
|
||||
|
||||
private ServletContext servletContext;
|
||||
|
||||
private final HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
private final Map<String, String[]> parameters = new LinkedHashMap<>();
|
||||
@@ -290,6 +303,15 @@ public class FilterInvocation {
|
||||
this.parameters.put(name, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletContext getServletContext() {
|
||||
return this.servletContext;
|
||||
}
|
||||
|
||||
void setServletContext(ServletContext servletContext) {
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class UnsupportedOperationExceptionInvocationHandler implements InvocationHandler {
|
||||
|
||||
+13
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
* 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.
|
||||
@@ -18,6 +18,8 @@ package org.springframework.security.web.access;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
@@ -28,6 +30,7 @@ import org.springframework.security.access.intercept.AbstractSecurityInterceptor
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.context.ServletContextAware;
|
||||
|
||||
/**
|
||||
* Allows users to determine whether they have privileges for a given web URI.
|
||||
@@ -36,12 +39,14 @@ import org.springframework.util.Assert;
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
*/
|
||||
public class DefaultWebInvocationPrivilegeEvaluator implements WebInvocationPrivilegeEvaluator {
|
||||
public class DefaultWebInvocationPrivilegeEvaluator implements WebInvocationPrivilegeEvaluator, ServletContextAware {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(DefaultWebInvocationPrivilegeEvaluator.class);
|
||||
|
||||
private final AbstractSecurityInterceptor securityInterceptor;
|
||||
|
||||
private ServletContext servletContext;
|
||||
|
||||
public DefaultWebInvocationPrivilegeEvaluator(AbstractSecurityInterceptor securityInterceptor) {
|
||||
Assert.notNull(securityInterceptor, "SecurityInterceptor cannot be null");
|
||||
Assert.isTrue(FilterInvocation.class.equals(securityInterceptor.getSecureObjectClass()),
|
||||
@@ -82,7 +87,7 @@ public class DefaultWebInvocationPrivilegeEvaluator implements WebInvocationPriv
|
||||
@Override
|
||||
public boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {
|
||||
Assert.notNull(uri, "uri parameter is required");
|
||||
FilterInvocation filterInvocation = new FilterInvocation(contextPath, uri, method);
|
||||
FilterInvocation filterInvocation = new FilterInvocation(contextPath, uri, method, this.servletContext);
|
||||
Collection<ConfigAttribute> attributes = this.securityInterceptor.obtainSecurityMetadataSource()
|
||||
.getAttributes(filterInvocation);
|
||||
if (attributes == null) {
|
||||
@@ -101,4 +106,9 @@ public class DefaultWebInvocationPrivilegeEvaluator implements WebInvocationPriv
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServletContext(ServletContext servletContext) {
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
-6
@@ -175,6 +175,7 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
|
||||
Authentication targetUser = attemptSwitchUser(request);
|
||||
// update the current context to the new target user
|
||||
SecurityContextHolder.getContext().setAuthentication(targetUser);
|
||||
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", targetUser));
|
||||
// redirect to target url
|
||||
this.successHandler.onAuthenticationSuccess(request, response, targetUser);
|
||||
}
|
||||
@@ -189,10 +190,13 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
|
||||
Authentication originalUser = attemptExitUser(request);
|
||||
// update the current context back to the original user
|
||||
SecurityContextHolder.getContext().setAuthentication(originalUser);
|
||||
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", originalUser));
|
||||
// redirect to target url
|
||||
this.successHandler.onAuthenticationSuccess(request, response, originalUser);
|
||||
return;
|
||||
}
|
||||
this.logger.trace(LogMessage.format("Did not attempt to switch user since request did not match [%s] or [%s]",
|
||||
this.switchUserMatcher, this.exitUserMatcher));
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@@ -211,12 +215,11 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
|
||||
UsernamePasswordAuthenticationToken targetUserRequest;
|
||||
String username = request.getParameter(this.usernameParameter);
|
||||
username = (username != null) ? username : "";
|
||||
this.logger.debug(LogMessage.format("Attempt to switch to user [%s]", username));
|
||||
this.logger.debug(LogMessage.format("Attempting to switch to user [%s]", username));
|
||||
UserDetails targetUser = this.userDetailsService.loadUserByUsername(username);
|
||||
this.userDetailsChecker.check(targetUser);
|
||||
// OK, create the switch user token
|
||||
targetUserRequest = createSwitchUserToken(request, targetUser);
|
||||
this.logger.debug(LogMessage.format("Switch User Token [%s]", targetUserRequest));
|
||||
// publish event
|
||||
if (this.eventPublisher != null) {
|
||||
this.eventPublisher.publishEvent(new AuthenticationSwitchUserEvent(
|
||||
@@ -245,9 +248,9 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
|
||||
// if so, get the original source user so we can switch back
|
||||
Authentication original = getSourceAuthentication(current);
|
||||
if (original == null) {
|
||||
this.logger.debug("Could not find original user Authentication object!");
|
||||
throw new AuthenticationCredentialsNotFoundException(this.messages.getMessage(
|
||||
"SwitchUserFilter.noOriginalAuthentication", "Could not find original Authentication object"));
|
||||
this.logger.debug("Failed to find original user");
|
||||
throw new AuthenticationCredentialsNotFoundException(this.messages
|
||||
.getMessage("SwitchUserFilter.noOriginalAuthentication", "Failed to find original user"));
|
||||
}
|
||||
// get the source user details
|
||||
UserDetails originalUser = null;
|
||||
@@ -322,7 +325,7 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
|
||||
// check for switch user type of authority
|
||||
if (auth instanceof SwitchUserGrantedAuthority) {
|
||||
original = ((SwitchUserGrantedAuthority) auth).getSource();
|
||||
this.logger.debug("Found original switch user granted authority [" + original + "]");
|
||||
this.logger.debug(LogMessage.format("Found original switch user granted authority [%s]", original));
|
||||
}
|
||||
}
|
||||
return original;
|
||||
|
||||
+2
-2
@@ -78,11 +78,11 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver}
|
||||
* instead.
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
@Deprecated
|
||||
public final class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -26,7 +26,7 @@ package org.springframework.security.web.csrf;
|
||||
public class MissingCsrfTokenException extends CsrfException {
|
||||
|
||||
public MissingCsrfTokenException(String actualToken) {
|
||||
super("Could not verify the provided CSRF token because your session was not found.");
|
||||
super("Could not verify the provided CSRF token because no token was found to compare.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see WebServletJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonDeserialize(using = CookieDeserializer.class)
|
||||
|
||||
+1
-1
@@ -31,9 +31,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see WebJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
|
||||
+1
-1
@@ -34,9 +34,9 @@ import org.springframework.security.web.savedrequest.DefaultSavedRequest;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see WebServletJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonDeserialize(builder = DefaultSavedRequest.Builder.class)
|
||||
|
||||
+1
-1
@@ -42,9 +42,9 @@ import org.springframework.security.jackson2.SimpleGrantedAuthorityMixin;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see Webackson2Module
|
||||
* @see SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
|
||||
@@ -33,9 +33,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh.
|
||||
* @since 4.2
|
||||
* @see WebServletJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
|
||||
+1
-1
@@ -32,9 +32,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see WebServletJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
|
||||
+11
-6
@@ -158,8 +158,12 @@ public class SwitchUserWebFilter implements WebFilter {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
final WebFilterExchange webFilterExchange = new WebFilterExchange(exchange, chain);
|
||||
return switchUser(webFilterExchange).switchIfEmpty(Mono.defer(() -> exitSwitchUser(webFilterExchange)))
|
||||
.switchIfEmpty(Mono.defer(() -> chain.filter(exchange).then(Mono.empty())))
|
||||
.flatMap((authentication) -> onAuthenticationSuccess(authentication, webFilterExchange))
|
||||
.switchIfEmpty(Mono.defer(() -> {
|
||||
this.logger.trace(
|
||||
LogMessage.format("Did not attempt to switch user since request did not match [%s] or [%s]",
|
||||
this.switchUserMatcher, this.exitUserMatcher));
|
||||
return chain.filter(exchange).then(Mono.empty());
|
||||
})).flatMap((authentication) -> onAuthenticationSuccess(authentication, webFilterExchange))
|
||||
.onErrorResume(SwitchUserAuthenticationException.class, (exception) -> Mono.empty());
|
||||
}
|
||||
|
||||
@@ -211,7 +215,7 @@ public class SwitchUserWebFilter implements WebFilter {
|
||||
@NonNull
|
||||
private Mono<Authentication> attemptSwitchUser(Authentication currentAuthentication, String userName) {
|
||||
Assert.notNull(userName, "The userName can not be null.");
|
||||
this.logger.debug(LogMessage.format("Attempt to switch to user [%s]", userName));
|
||||
this.logger.debug(LogMessage.format("Attempting to switch to user [%s]", userName));
|
||||
return this.userDetailsService.findByUsername(userName)
|
||||
.switchIfEmpty(Mono.error(this::noTargetAuthenticationException))
|
||||
.doOnNext(this.userDetailsChecker::check)
|
||||
@@ -222,7 +226,7 @@ public class SwitchUserWebFilter implements WebFilter {
|
||||
private Authentication attemptExitUser(Authentication currentAuthentication) {
|
||||
Optional<Authentication> sourceAuthentication = extractSourceAuthentication(currentAuthentication);
|
||||
if (!sourceAuthentication.isPresent()) {
|
||||
this.logger.debug("Could not find original user Authentication object!");
|
||||
this.logger.debug("Failed to find original user");
|
||||
throw noOriginalAuthenticationException();
|
||||
}
|
||||
return sourceAuthentication.get();
|
||||
@@ -232,13 +236,14 @@ public class SwitchUserWebFilter implements WebFilter {
|
||||
ServerWebExchange exchange = webFilterExchange.getExchange();
|
||||
SecurityContextImpl securityContext = new SecurityContextImpl(authentication);
|
||||
return this.securityContextRepository.save(exchange, securityContext)
|
||||
.doOnSuccess((v) -> this.logger.debug(LogMessage.format("Switched user to %s", authentication)))
|
||||
.then(this.successHandler.onAuthenticationSuccess(webFilterExchange, authentication))
|
||||
.subscriberContext(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)));
|
||||
}
|
||||
|
||||
private Mono<Void> onAuthenticationFailure(AuthenticationException exception, WebFilterExchange webFilterExchange) {
|
||||
return Mono.justOrEmpty(this.failureHandler).switchIfEmpty(Mono.defer(() -> {
|
||||
this.logger.error("Switch User failed", exception);
|
||||
this.logger.debug("Failed to switch user", exception);
|
||||
return Mono.error(exception);
|
||||
})).flatMap((failureHandler) -> failureHandler.onAuthenticationFailure(webFilterExchange, exception));
|
||||
}
|
||||
@@ -247,7 +252,7 @@ public class SwitchUserWebFilter implements WebFilter {
|
||||
Optional<Authentication> sourceAuthentication = extractSourceAuthentication(currentAuthentication);
|
||||
if (sourceAuthentication.isPresent()) {
|
||||
// SEC-1763. Check first if we are already switched.
|
||||
this.logger.info(
|
||||
this.logger.debug(
|
||||
LogMessage.format("Found original switch user granted authority [%s]", sourceAuthentication.get()));
|
||||
currentAuthentication = sourceAuthentication.get();
|
||||
}
|
||||
|
||||
+11
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.security.web.server.header;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@@ -41,8 +40,16 @@ public class StaticServerHttpHeadersWriter implements ServerHttpHeadersWriter {
|
||||
@Override
|
||||
public Mono<Void> writeHttpHeaders(ServerWebExchange exchange) {
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
boolean containsOneHeaderToAdd = Collections.disjoint(headers.keySet(), this.headersToAdd.keySet());
|
||||
if (containsOneHeaderToAdd) {
|
||||
// Note: We need to ensure that the following algorithm compares headers
|
||||
// case insensitively, which should be true of headers.containsKey().
|
||||
boolean containsNoHeadersToAdd = true;
|
||||
for (String headerName : this.headersToAdd.keySet()) {
|
||||
if (headers.containsKey(headerName)) {
|
||||
containsNoHeadersToAdd = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (containsNoHeadersToAdd) {
|
||||
this.headersToAdd.forEach(headers::put);
|
||||
}
|
||||
return Mono.empty();
|
||||
|
||||
+1
-1
@@ -32,8 +32,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
* </pre>
|
||||
*
|
||||
* @author Boris Finkelshteyn
|
||||
* @see WebServerJackson2Module
|
||||
* @since 5.1
|
||||
* @see WebServerJackson2Module
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
* 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.
|
||||
@@ -24,6 +24,7 @@ import org.junit.Test;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.security.web.FilterInvocation.DummyRequest;
|
||||
import org.springframework.security.web.util.UrlUtils;
|
||||
|
||||
@@ -131,4 +132,14 @@ public class FilterInvocationTests {
|
||||
UrlUtils.buildRequestUrl(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorWhenServletContextProvidedThenSetServletContextInRequest() {
|
||||
String contextPath = "";
|
||||
String servletPath = "/path";
|
||||
String method = "";
|
||||
MockServletContext mockServletContext = new MockServletContext();
|
||||
FilterInvocation filterInvocation = new FilterInvocation(contextPath, servletPath, method, mockServletContext);
|
||||
assertThat(filterInvocation.getRequest().getServletContext()).isSameAs(mockServletContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+18
@@ -18,8 +18,10 @@ package org.springframework.security.web.access;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.intercept.RunAsManager;
|
||||
@@ -27,6 +29,7 @@ import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
|
||||
@@ -34,9 +37,11 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.ArgumentMatchers.anyObject;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.BDDMockito.willThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests
|
||||
@@ -106,4 +111,17 @@ public class DefaultWebInvocationPrivilegeEvaluatorTests {
|
||||
assertThat(wipe.isAllowed("/foo/index.jsp", token)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAllowedWhenServletContextIsSetThenPassedFilterInvocationHasServletContext() {
|
||||
Authentication token = new TestingAuthenticationToken("test", "Password", "MOCK_INDEX");
|
||||
MockServletContext servletContext = new MockServletContext();
|
||||
ArgumentCaptor<FilterInvocation> filterInvocationArgumentCaptor = ArgumentCaptor
|
||||
.forClass(FilterInvocation.class);
|
||||
DefaultWebInvocationPrivilegeEvaluator wipe = new DefaultWebInvocationPrivilegeEvaluator(this.interceptor);
|
||||
wipe.setServletContext(servletContext);
|
||||
wipe.isAllowed("/foo/index.jsp", token);
|
||||
verify(this.adm).decide(eq(token), filterInvocationArgumentCaptor.capture(), any());
|
||||
assertThat(filterInvocationArgumentCaptor.getValue().getRequest().getServletContext()).isNotNull();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+22
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -16,11 +16,14 @@
|
||||
|
||||
package org.springframework.security.web.server.header;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -56,6 +59,24 @@ public class StaticServerHttpHeadersWriterTests {
|
||||
.containsOnly(headerValue);
|
||||
}
|
||||
|
||||
// gh-10557
|
||||
@Test
|
||||
public void writeHeadersWhenHeaderWrittenWithDifferentCaseThenDoesNotWriteHeaders() {
|
||||
String headerName = HttpHeaders.CACHE_CONTROL.toLowerCase(Locale.ROOT);
|
||||
String headerValue = "max-age=120";
|
||||
this.headers.set(headerName, headerValue);
|
||||
// Note: This test inverts which collection uses case sensitive headers,
|
||||
// due to the fact that gh-10557 reports NettyHeadersAdapter as the
|
||||
// response headers implementation, which is not accessible here.
|
||||
HttpHeaders caseSensitiveHeaders = new HttpHeaders(new LinkedMultiValueMap<>());
|
||||
caseSensitiveHeaders.set(HttpHeaders.CACHE_CONTROL, CacheControlServerHttpHeadersWriter.CACHE_CONTRTOL_VALUE);
|
||||
caseSensitiveHeaders.set(HttpHeaders.PRAGMA, CacheControlServerHttpHeadersWriter.PRAGMA_VALUE);
|
||||
caseSensitiveHeaders.set(HttpHeaders.EXPIRES, CacheControlServerHttpHeadersWriter.EXPIRES_VALUE);
|
||||
this.writer = new StaticServerHttpHeadersWriter(caseSensitiveHeaders);
|
||||
this.writer.writeHttpHeaders(this.exchange);
|
||||
assertThat(this.headers.get(headerName)).containsOnly(headerValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeHeadersWhenMultiHeaderThenWritesAllHeaders() {
|
||||
this.writer = StaticServerHttpHeadersWriter.builder()
|
||||
|
||||
Reference in New Issue
Block a user