1
0
mirror of synced 2026-07-10 05:10:03 +00:00

Compare commits

...

27 Commits

Author SHA1 Message Date
Steve Riesenberg 13e7c9f58d Release 5.5.3 2021-10-18 14:48:01 -05:00
Steve Riesenberg 14e6d8ad49 Revert "Update io.spring.javaformat to 0.0.29"
This reverts commit fafde0910f.
2021-10-18 14:23:51 -05:00
Steve Riesenberg d1a27676df Update org.springframework.data to 2021.0.6
Closes gh-10417
2021-10-18 14:20:16 -05:00
Steve Riesenberg 844879b84d Update org.springframework to 5.3.11
Closes gh-10416
2021-10-18 14:20:14 -05:00
Steve Riesenberg d5efbd34b5 Update org.jetbrains.kotlinx to 1.5.2
Closes gh-10415
2021-10-18 14:20:11 -05:00
Steve Riesenberg b9ef11d228 Update org.jetbrains.kotlin to 1.5.31
Closes gh-10414
2021-10-18 14:20:09 -05:00
Steve Riesenberg d9b1df8b65 Update org.eclipse.jetty to 9.4.44.v20210927
Closes gh-10413
2021-10-18 14:20:07 -05:00
Steve Riesenberg 020dd90566 Update io.spring.nohttp to 0.0.10
Closes gh-10412
2021-10-18 14:20:05 -05:00
Steve Riesenberg fafde0910f Update io.spring.javaformat to 0.0.29
Closes gh-10411
2021-10-18 14:20:02 -05:00
Steve Riesenberg b2db2bdb2a Update r2dbc-spi-test to 0.8.6.RELEASE
Closes gh-10410
2021-10-18 14:20:00 -05:00
Steve Riesenberg cce1c16555 Update io.projectreactor to 2020.0.12
Closes gh-10408
2021-10-18 14:19:56 -05:00
Steve Riesenberg e969c363c0 Update jackson-bom to 2.12.5
Closes gh-10405
2021-10-18 14:19:49 -05:00
Steve Riesenberg 764e696026 Update logback-classic to 1.2.6
Closes gh-10404
2021-10-18 14:19:48 -05:00
Josh Cummings 6e86fab19d Restructure SwitchUserFilter Logs
Issue gh-6311
2021-10-18 13:02:42 -05:00
Emil Sierżęga 1a3e80506c Fixed link in .editorconfig 2021-10-13 15:37:32 -06:00
Dávid Kováč eb0597154d Update JavaDoc according to implementation
Update ClaimAccessor#getClaimAsMap and ClaimAccessor#getClaimAsStringList
JavaDoc according to the current implementation

Closes gh-10117
2021-10-13 13:13:44 -06:00
Josh Cummings 97dfabe92e Polish SecurityNamespaceHandler Tests
Issue gh-8974
2021-10-13 11:37:06 -06:00
Emil Sierżęga 944463e19a SecurityNamespaceHandler: update schema version to 5.5
Closes gh-8974
2021-10-13 11:35:25 -06:00
Marcus Da Coregio 816e847af2 Allow SAML 2.0 loginProcessingURL without registrationId
Closes gh-10176
2021-10-05 12:54:39 -03:00
Josh Cummings 1f919bc791 Fix OAuth2 Error Code
Closes gh-10319
2021-09-28 14:55:37 -06:00
Rob Winch cae8990046 GitHub Actions uses spring-builds+github 2021-09-28 15:08:55 -05:00
heowc c9917b3cd0 Fix typo
Closes gh-10276
2021-09-22 16:35:32 -06:00
Anthony Lofton 1653b7848d Updated test.adoc SecurityMockServerConfigurers method references
Updated all references to SecurityMockServerConfigurers to refer to
correct methods.
Added documentation for mockJwt to include the
SecurityMockServerConfigurers class.

Issue gh-10254
2021-09-14 15:22:32 -03:00
Derek Van Blerkom c55f1f8bea Fix return type to allow further security config
Issue gh-10245
2021-09-13 15:41:04 -03:00
Marcus Da Coregio 670cf99258 Update docs to point to ACL samples
Closes gh-10110
2021-09-06 11:30:38 -03:00
Josh Cummings 41feec4766 Remove unused Sonar from Build
Closes gh-10205
2021-08-20 16:47:55 -06:00
Josh Cummings 34e04f642c Next Development Version 2021-08-16 12:48:56 -06:00
27 changed files with 272 additions and 119 deletions
+1 -1
View File
@@ -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
View File
@@ -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"
+2 -2
View File
@@ -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.11'
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
@@ -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,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,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());
}
}
@@ -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
@@ -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;
@@ -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() {
}
}
@@ -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) {
@@ -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,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\"");
+7 -7
View File
@@ -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.12")
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.6")
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.5")
constraints {
api "ch.qos.logback:logback-classic:1.2.5"
api "ch.qos.logback:logback-classic:1.2.6"
api "com.google.inject:guice:3.0"
api "com.nimbusds:nimbus-jose-jwt:9.10.1"
api "com.nimbusds:oauth2-oidc-sdk:9.9.1"
@@ -49,8 +49,8 @@ dependencies {
api "org.assertj:assertj-core:3.19.0"
api "org.bouncycastle:bcpkix-jdk15on:1.68"
api "org.bouncycastle:bcprov-jdk15on:1.68"
api "org.eclipse.jetty:jetty-server:9.4.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"
@@ -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
+3 -3
View File
@@ -1,10 +1,10 @@
aspectjVersion=1.9.7
springJavaformatVersion=0.0.28
springBootVersion=2.4.2
springFrameworkVersion=5.3.9
springFrameworkVersion=5.3.11
openSamlVersion=3.4.6
version=5.5.2
kotlinVersion=1.5.21
version=5.5.3
kotlinVersion=1.5.31
samplesBranch=5.5.x
org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError
org.gradle.parallel=true
@@ -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,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.
@@ -133,11 +133,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 +158,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) {
@@ -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");
}
@@ -28,6 +28,7 @@ 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;
@@ -109,6 +110,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);
}
@@ -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());
@@ -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));
@@ -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;
@@ -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();
}