diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 596cf8f9ff..0000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,92 +0,0 @@ -version: 2 - -registries: - spring-milestones: - type: maven-repository - url: https://repo.spring.io/milestone - -updates: - - - package-ecosystem: "gradle" - target-branch: "main" - milestone: 319 # 6.2.x - directory: "/" - schedule: - interval: "daily" - time: "03:00" - timezone: "Etc/UTC" - labels: [ "type: dependency-upgrade" ] - registries: - - "spring-milestones" - ignore: - - dependency-name: "com.nimbusds:nimbus-jose-jwt" # nimbus-jose-jwt gets updated when oauth2-oidc-sdk is updated to ensure consistency - - dependency-name: "org.python:jython" # jython updates break integration tests - - dependency-name: "org.apache.directory.server:*" # ApacheDS version > 1.5.5 contains break changes - - dependency-name: "org.junit:junit-bom" - update-types: [ "version-update:semver-major" ] - - dependency-name: "org.mockito:mockito-bom" - update-types: [ "version-update:semver-major" ] - - dependency-name: "*" - update-types: [ "version-update:semver-major", "version-update:semver-minor" ] - - - package-ecosystem: "gradle" - target-branch: "6.1.x" - milestone: 318 # 6.1.x - directory: "/" - schedule: - interval: "daily" - time: "03:00" - timezone: "Etc/UTC" - labels: [ "type: dependency-upgrade" ] - ignore: - - dependency-name: "com.nimbusds:nimbus-jose-jwt" # nimbus-jose-jwt gets updated when oauth2-oidc-sdk is updated to ensure consistency - - dependency-name: "org.python:jython" # jython updates break integration tests - - dependency-name: "org.apache.directory.server:*" # ApacheDS version > 1.5.5 contains break changes - - dependency-name: "org.junit:junit-bom" - update-types: [ "version-update:semver-major" ] - - dependency-name: "org.mockito:mockito-bom" - update-types: [ "version-update:semver-major" ] - - dependency-name: "*" - update-types: [ "version-update:semver-major", "version-update:semver-minor" ] - - - package-ecosystem: "gradle" - target-branch: "6.0.x" - milestone: 143 # 6.0.x - directory: "/" - schedule: - interval: "daily" - time: "03:00" - timezone: "Etc/UTC" - labels: [ "type: dependency-upgrade" ] - ignore: - - dependency-name: "com.nimbusds:nimbus-jose-jwt" # nimbus-jose-jwt gets updated when oauth2-oidc-sdk is updated to ensure consistency - - dependency-name: "org.python:jython" # jython updates break integration tests - - dependency-name: "org.apache.directory.server:*" # ApacheDS version > 1.5.5 contains break changes - - dependency-name: "org.junit:junit-bom" - update-types: [ "version-update:semver-major" ] - - dependency-name: "org.mockito:mockito-bom" - update-types: [ "version-update:semver-major" ] - - dependency-name: "*" - update-types: [ "version-update:semver-major", "version-update:semver-minor" ] - - - package-ecosystem: "gradle" - target-branch: "5.8.x" - milestone: 246 # 5.8.x - directory: "/" - schedule: - interval: "daily" - time: "03:00" - timezone: "Etc/UTC" - labels: [ "type: dependency-upgrade" ] - ignore: - - dependency-name: "com.nimbusds:nimbus-jose-jwt" # nimbus-jose-jwt gets updated when oauth2-oidc-sdk is updated to ensure consistency - - dependency-name: "org.python:jython" # jython updates break integration tests - - dependency-name: "org.apache.directory.server:*" # ApacheDS version > 1.5.5 contains break changes - - dependency-name: "io.mockk:mockk" # mockk updates break tests - - dependency-name: "org.opensaml:*" # org.opensaml maintains two different versions, so it must be updated manually - - dependency-name: "org.junit:junit-bom" - update-types: [ "version-update:semver-major" ] - - dependency-name: "org.mockito:mockito-bom" - update-types: [ "version-update:semver-major" ] - - dependency-name: "*" - update-types: [ "version-update:semver-major", "version-update:semver-minor" ] diff --git a/.github/workflows/continuous-integration-workflow.yml b/.github/workflows/continuous-integration-workflow.yml index ab8f3dbdd8..1255f25028 100644 --- a/.github/workflows/continuous-integration-workflow.yml +++ b/.github/workflows/continuous-integration-workflow.yml @@ -69,13 +69,6 @@ jobs: snapshot_tests: name: Test against snapshots needs: [prerequisites] - strategy: - matrix: - include: - - java-version: '21-ea' - toolchain: '21' - - java-version: '17' - toolchain: '17' runs-on: ubuntu-latest if: needs.prerequisites.outputs.runjobs steps: @@ -83,14 +76,14 @@ jobs: - name: Set up gradle uses: spring-io/spring-gradle-build-action@v1 with: - java-version: ${{ matrix.java-version }} + java-version: '17' distribution: 'temurin' - 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 -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion='6.1.+' -PreactorVersion='2023.0.+' -PspringDataVersion='2023.1.+' -PlocksDisabled --stacktrace + ./gradlew test --refresh-dependencies -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" -PforceMavenRepositories=snapshot -PisOverrideVersionCatalog -PspringFrameworkVersion='6.0.+' -PreactorVersion='2022.0.+' -PspringDataVersion='2022.0.+' -PlocksDisabled --stacktrace check_samples: name: Check Samples project needs: [prerequisites] diff --git a/.github/workflows/gradle-wrapper-upgrade-execution.yml b/.github/workflows/gradle-wrapper-upgrade-execution.yml deleted file mode 100644 index dcb48ab942..0000000000 --- a/.github/workflows/gradle-wrapper-upgrade-execution.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Execute Gradle Wrapper Upgrade - -on: - schedule: - - cron: '0 2 * * *' # 2am UTC - workflow_dispatch: - -jobs: - upgrade_wrapper: - name: Execution - runs-on: ubuntu-latest - steps: - - name: Set up Git configuration - env: - TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - git config --global url."https://unused-username:${TOKEN}@github.com/".insteadOf "https://github.com/" - git config --global user.name 'github-actions[bot]' - git config --global user.email 'github-actions[bot]@users.noreply.github.com' - - name: Checkout - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' - - name: Set up Gradle - uses: gradle/gradle-build-action@v2 - - name: Upgrade Wrappers - run: ./gradlew clean upgradeGradleWrapperAll --continue -Porg.gradle.java.installations.auto-download=false - env: - WRAPPER_UPGRADE_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-scheduler.yml b/.github/workflows/release-scheduler.yml index c3a37c30ce..ddacbcb439 100644 --- a/.github/workflows/release-scheduler.yml +++ b/.github/workflows/release-scheduler.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: # List of active maintenance branches. - branch: [ main, 6.1.x, 6.0.x, 5.8.x ] + branch: [ main, 6.0.x, 5.8.x, 5.7.x ] runs-on: ubuntu-latest steps: - name: Checkout diff --git a/acl/src/main/java/org/springframework/security/acls/AclPermissionEvaluator.java b/acl/src/main/java/org/springframework/security/acls/AclPermissionEvaluator.java index 53fe8c1efe..15907a22ca 100644 --- a/acl/src/main/java/org/springframework/security/acls/AclPermissionEvaluator.java +++ b/acl/src/main/java/org/springframework/security/acls/AclPermissionEvaluator.java @@ -118,7 +118,8 @@ public class AclPermissionEvaluator implements PermissionEvaluator { if (permission instanceof Permission[]) { return Arrays.asList((Permission[]) permission); } - if (permission instanceof String permString) { + if (permission instanceof String) { + String permString = (String) permission; Permission p = buildPermission(permString); if (p != null) { return Arrays.asList(p); diff --git a/acl/src/main/java/org/springframework/security/acls/domain/AbstractPermission.java b/acl/src/main/java/org/springframework/security/acls/domain/AbstractPermission.java index 8365552a5a..147f643966 100644 --- a/acl/src/main/java/org/springframework/security/acls/domain/AbstractPermission.java +++ b/acl/src/main/java/org/springframework/security/acls/domain/AbstractPermission.java @@ -56,9 +56,10 @@ public abstract class AbstractPermission implements Permission { if (obj == null) { return false; } - if (!(obj instanceof Permission other)) { + if (!(obj instanceof Permission)) { return false; } + Permission other = (Permission) obj; return (this.mask == other.getMask()); } diff --git a/acl/src/test/java/org/springframework/security/acls/jdbc/JdbcAclServiceTests.java b/acl/src/test/java/org/springframework/security/acls/jdbc/JdbcAclServiceTests.java index 6253c29702..b817d33c1e 100644 --- a/acl/src/test/java/org/springframework/security/acls/jdbc/JdbcAclServiceTests.java +++ b/acl/src/test/java/org/springframework/security/acls/jdbc/JdbcAclServiceTests.java @@ -112,7 +112,7 @@ public class JdbcAclServiceTests { given(this.jdbcOperations.query(anyString(), eq(args), any(RowMapper.class))).willReturn(result); ObjectIdentity objectIdentity = new ObjectIdentityImpl(MockLongIdDomainObject.class, 1L); List objectIdentities = this.aclService.findChildren(objectIdentity); - assertThat(objectIdentities).hasSize(1); + assertThat(objectIdentities.size()).isEqualTo(1); assertThat(objectIdentities.get(0).getIdentifier()).isEqualTo("5577"); } @@ -127,7 +127,7 @@ public class JdbcAclServiceTests { public void findChildrenWithoutIdType() { ObjectIdentity objectIdentity = new ObjectIdentityImpl(MockLongIdDomainObject.class, 4711L); List objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity); - assertThat(objectIdentities).hasSize(1); + assertThat(objectIdentities.size()).isEqualTo(1); assertThat(objectIdentities.get(0).getType()).isEqualTo(MockUntypedIdDomainObject.class.getName()); assertThat(objectIdentities.get(0).getIdentifier()).isEqualTo(5000L); } @@ -143,7 +143,7 @@ public class JdbcAclServiceTests { public void findChildrenOfIdTypeLong() { ObjectIdentity objectIdentity = new ObjectIdentityImpl("location", "US-PAL"); List objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity); - assertThat(objectIdentities).hasSize(2); + assertThat(objectIdentities.size()).isEqualTo(2); assertThat(objectIdentities.get(0).getType()).isEqualTo(MockLongIdDomainObject.class.getName()); assertThat(objectIdentities.get(0).getIdentifier()).isEqualTo(4711L); assertThat(objectIdentities.get(1).getType()).isEqualTo(MockLongIdDomainObject.class.getName()); @@ -155,7 +155,7 @@ public class JdbcAclServiceTests { ObjectIdentity objectIdentity = new ObjectIdentityImpl("location", "US"); this.aclServiceIntegration.setAclClassIdSupported(true); List objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity); - assertThat(objectIdentities).hasSize(1); + assertThat(objectIdentities.size()).isEqualTo(1); assertThat(objectIdentities.get(0).getType()).isEqualTo("location"); assertThat(objectIdentities.get(0).getIdentifier()).isEqualTo("US-PAL"); } @@ -165,7 +165,7 @@ public class JdbcAclServiceTests { ObjectIdentity objectIdentity = new ObjectIdentityImpl(MockUntypedIdDomainObject.class, 5000L); this.aclServiceIntegration.setAclClassIdSupported(true); List objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity); - assertThat(objectIdentities).hasSize(1); + assertThat(objectIdentities.size()).isEqualTo(1); assertThat(objectIdentities.get(0).getType()).isEqualTo("costcenter"); assertThat(objectIdentities.get(0).getIdentifier()) .isEqualTo(UUID.fromString("25d93b3f-c3aa-4814-9d5e-c7c96ced7762")); @@ -186,7 +186,7 @@ public class JdbcAclServiceTests { ObjectIdentity objectIdentity = new ObjectIdentityImpl("location", "US"); this.aclServiceIntegration.setAclClassIdSupported(true); List objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity); - assertThat(objectIdentities).hasSize(1); + assertThat(objectIdentities.size()).isEqualTo(1); assertThat(objectIdentities.get(0).getType()).isEqualTo("location"); assertThat(objectIdentities.get(0).getIdentifier()).isEqualTo("prefix:US-PAL"); } diff --git a/acl/src/test/java/org/springframework/security/acls/jdbc/JdbcMutableAclServiceTests.java b/acl/src/test/java/org/springframework/security/acls/jdbc/JdbcMutableAclServiceTests.java index 76e5a10ec9..f7f69d19e4 100644 --- a/acl/src/test/java/org/springframework/security/acls/jdbc/JdbcMutableAclServiceTests.java +++ b/acl/src/test/java/org/springframework/security/acls/jdbc/JdbcMutableAclServiceTests.java @@ -454,7 +454,7 @@ public class JdbcMutableAclServiceTests { CustomSid customSid = new CustomSid("Custom sid"); given(customJdbcMutableAclService.createOrRetrieveSidPrimaryKey("Custom sid", false, false)).willReturn(1L); Long result = customJdbcMutableAclService.createOrRetrieveSidPrimaryKey(customSid, false); - assertThat(Long.valueOf(1L)).isEqualTo(result); + assertThat(new Long(1L)).isEqualTo(result); } protected Authentication getAuth() { diff --git a/acl/src/test/java/org/springframework/security/acls/sid/SidTests.java b/acl/src/test/java/org/springframework/security/acls/sid/SidTests.java index 3b002190bc..d52d6980fd 100644 --- a/acl/src/test/java/org/springframework/security/acls/sid/SidTests.java +++ b/acl/src/test/java/org/springframework/security/acls/sid/SidTests.java @@ -120,9 +120,9 @@ public class SidTests { PrincipalSid principalSid = new PrincipalSid(authentication); GrantedAuthority ga = new SimpleGrantedAuthority("ROLE_TEST"); GrantedAuthoritySid gaSid = new GrantedAuthoritySid(ga); - assertThat("johndoe").isEqualTo(principalSid.getPrincipal()); + assertThat("johndoe".equals(principalSid.getPrincipal())).isTrue(); assertThat("scott".equals(principalSid.getPrincipal())).isFalse(); - assertThat("ROLE_TEST").isEqualTo(gaSid.getGrantedAuthority()); + assertThat("ROLE_TEST".equals(gaSid.getGrantedAuthority())).isTrue(); assertThat("ROLE_TEST2".equals(gaSid.getGrantedAuthority())).isFalse(); } diff --git a/aspects/spring-security-aspects.gradle b/aspects/spring-security-aspects.gradle index 4323430c3d..eb43bb8757 100644 --- a/aspects/spring-security-aspects.gradle +++ b/aspects/spring-security-aspects.gradle @@ -29,4 +29,10 @@ dependencies { testAspect sourceSets.main.output } +sourceSets.main.aspectj.srcDir "src/main/java" +sourceSets.main.java.srcDirs = files() + +sourceSets.test.aspectj.srcDir "src/test/java" +sourceSets.test.java.srcDirs = files() + compileAspectj.ajcOptions.outxmlfile = "META-INF/aop.xml" diff --git a/aspects/src/test/java/org/springframework/security/access/intercept/aspectj/aspect/AnnotationSecurityAspectTests.java b/aspects/src/test/java/org/springframework/security/access/intercept/aspectj/aspect/AnnotationSecurityAspectTests.java index 2e0f4ceea1..778268a5a3 100644 --- a/aspects/src/test/java/org/springframework/security/access/intercept/aspectj/aspect/AnnotationSecurityAspectTests.java +++ b/aspects/src/test/java/org/springframework/security/access/intercept/aspectj/aspect/AnnotationSecurityAspectTests.java @@ -143,8 +143,8 @@ public class AnnotationSecurityAspectTests { SecurityContextHolder.getContext().setAuthentication(this.anne); List objects = this.prePostSecured.postFilterMethod(); assertThat(objects).hasSize(2); - assertThat(objects).contains("apple"); - assertThat(objects).contains("aubergine"); + assertThat(objects.contains("apple")).isTrue(); + assertThat(objects.contains("aubergine")).isTrue(); } private void configureForElAnnotations() { diff --git a/build.gradle b/build.gradle index d2af627741..a169da4db5 100644 --- a/build.gradle +++ b/build.gradle @@ -14,10 +14,6 @@ buildscript { } } -plugins { - alias(libs.plugins.org.gradle.wrapper.upgrade) -} - apply plugin: 'io.spring.nohttp' apply plugin: 'locks' apply plugin: 's101' @@ -39,7 +35,6 @@ ext.milestoneBuild = !(snapshotBuild || releaseBuild) repositories { mavenCentral() - maven { url "https://repo.spring.io/milestone" } } tasks.named("saganCreateRelease") { @@ -91,31 +86,17 @@ tasks.named("dispatchGitHubWorkflow") { } } -def toolchainVersion() { - if (project.hasProperty('testToolchain')) { - return project.property('testToolchain').toString().toInteger() - } - return 17 -} - subprojects { - java { - toolchain { - languageVersion = JavaLanguageVersion.of(toolchainVersion()) - } + plugins.withType(JavaPlugin) { + project.sourceCompatibility=JavaVersion.VERSION_17 } - kotlin { - jvmToolchain { - languageVersion = JavaLanguageVersion.of(17) - } - } - tasks.withType(JavaCompile).configureEach { + tasks.withType(JavaCompile) { options.encoding = "UTF-8" options.compilerArgs.add("-parameters") - options.release.set(17) } } + allprojects { if (!['spring-security-bom', 'spring-security-docs'].contains(project.name)) { apply plugin: 'io.spring.javaformat' @@ -140,6 +121,12 @@ allprojects { } } } + + tasks.withType(JavaCompile).configureEach { + javaCompiler = javaToolchains.compilerFor { + languageVersion = JavaLanguageVersion.of(17) + } + } } if (hasProperty('buildScan')) { @@ -163,12 +150,3 @@ tasks.register('cloneSamples', IncludeRepoTask) { s101 { configurationDirectory = project.file("etc/s101") } - -wrapperUpgrade { - gradle { - 'spring-security' { - repo = 'spring-projects/spring-security' - baseBranch = '6.0.x' // runs only on 6.0.x and the update is merged forward to main - } - } -} diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 0f3cea12e1..0b6e4db82d 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -9,7 +9,6 @@ sourceCompatibility = JavaVersion.VERSION_17 repositories { gradlePluginPortal() mavenCentral() - maven { url 'https://repo.spring.io/milestone' } } sourceSets { diff --git a/buildSrc/src/main/groovy/io/spring/gradle/convention/CheckstylePlugin.groovy b/buildSrc/src/main/groovy/io/spring/gradle/convention/CheckstylePlugin.groovy index 6e33f5d7e3..15ba096adf 100644 --- a/buildSrc/src/main/groovy/io/spring/gradle/convention/CheckstylePlugin.groovy +++ b/buildSrc/src/main/groovy/io/spring/gradle/convention/CheckstylePlugin.groovy @@ -1,5 +1,5 @@ /* - * Copyright 2016-2019 the original author or authors. + * Copyright 2016-2023 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 diff --git a/buildSrc/src/main/java/org/springframework/gradle/antora/AntoraVersionPlugin.java b/buildSrc/src/main/java/org/springframework/gradle/antora/AntoraVersionPlugin.java index 48e8e805ac..9a1f95d5b4 100644 --- a/buildSrc/src/main/java/org/springframework/gradle/antora/AntoraVersionPlugin.java +++ b/buildSrc/src/main/java/org/springframework/gradle/antora/AntoraVersionPlugin.java @@ -4,6 +4,7 @@ import org.gradle.api.Action; import org.gradle.api.GradleException; import org.gradle.api.Plugin; import org.gradle.api.Project; +import org.gradle.api.Task; import org.gradle.api.tasks.TaskProvider; import org.gradle.language.base.plugins.LifecycleBasePlugin; @@ -26,8 +27,12 @@ public class AntoraVersionPlugin implements Plugin { project.getPlugins().withType(LifecycleBasePlugin.class, new Action() { @Override public void execute(LifecycleBasePlugin lifecycleBasePlugin) { - project.getTasks().named(LifecycleBasePlugin.CHECK_TASK_NAME) - .configure(check -> check.dependsOn(antoraCheckVersion)); + project.getTasks().named(LifecycleBasePlugin.CHECK_TASK_NAME).configure(new Action() { + @Override + public void execute(Task check) { + check.dependsOn(antoraCheckVersion); + } + }); } }); project.getTasks().register("antoraUpdateVersion", UpdateAntoraVersionTask.class, new Action() { diff --git a/buildSrc/src/main/java/org/springframework/gradle/classpath/CheckClasspathForProhibitedDependenciesPlugin.java b/buildSrc/src/main/java/org/springframework/gradle/classpath/CheckClasspathForProhibitedDependenciesPlugin.java index 41caa9b8d5..0791a193e8 100644 --- a/buildSrc/src/main/java/org/springframework/gradle/classpath/CheckClasspathForProhibitedDependenciesPlugin.java +++ b/buildSrc/src/main/java/org/springframework/gradle/classpath/CheckClasspathForProhibitedDependenciesPlugin.java @@ -35,7 +35,9 @@ public class CheckClasspathForProhibitedDependenciesPlugin implements Plugin configureProhibitedDependencyChecks(project)); + project.getPlugins().withType(JavaBasePlugin.class, javaBasePlugin -> { + configureProhibitedDependencyChecks(project); + }); } private void configureProhibitedDependencyChecks(Project project) { diff --git a/buildSrc/src/main/java/org/springframework/gradle/classpath/CheckProhibitedDependenciesLifecyclePlugin.java b/buildSrc/src/main/java/org/springframework/gradle/classpath/CheckProhibitedDependenciesLifecyclePlugin.java index ebdde90c50..77fd369528 100644 --- a/buildSrc/src/main/java/org/springframework/gradle/classpath/CheckProhibitedDependenciesLifecyclePlugin.java +++ b/buildSrc/src/main/java/org/springframework/gradle/classpath/CheckProhibitedDependenciesLifecyclePlugin.java @@ -34,6 +34,8 @@ public class CheckProhibitedDependenciesLifecyclePlugin implements Plugin checkTask.dependsOn(checkProhibitedDependencies)); + project.getTasks().named(JavaBasePlugin.CHECK_TASK_NAME, checkTask -> { + checkTask.dependsOn(checkProhibitedDependencies); + }); } } diff --git a/buildSrc/src/main/java/org/springframework/gradle/github/changelog/GitHubChangelogPlugin.java b/buildSrc/src/main/java/org/springframework/gradle/github/changelog/GitHubChangelogPlugin.java index 907a69ef96..0eab3d8006 100644 --- a/buildSrc/src/main/java/org/springframework/gradle/github/changelog/GitHubChangelogPlugin.java +++ b/buildSrc/src/main/java/org/springframework/gradle/github/changelog/GitHubChangelogPlugin.java @@ -26,6 +26,7 @@ import org.gradle.api.Task; import org.gradle.api.artifacts.Configuration; import org.gradle.api.artifacts.DependencySet; import org.gradle.api.artifacts.repositories.ExclusiveContentRepository; +import org.gradle.api.artifacts.repositories.InclusiveRepositoryContentDescriptor; import org.gradle.api.artifacts.repositories.IvyArtifactRepository; import org.gradle.api.artifacts.repositories.IvyPatternRepositoryLayout; import org.gradle.api.tasks.JavaExec; @@ -90,7 +91,12 @@ public class GitHubChangelogPlugin implements Plugin { @Override public void execute(ExclusiveContentRepository exclusiveContentRepository) { exclusiveContentRepository.forRepositories(repository); - exclusiveContentRepository.filter(descriptor -> descriptor.includeGroup("spring-io")); + exclusiveContentRepository.filter(new Action() { + @Override + public void execute(InclusiveRepositoryContentDescriptor descriptor) { + descriptor.includeGroup("spring-io"); + } + }); } }); } diff --git a/buildSrc/src/main/java/org/springframework/gradle/github/milestones/SpringReleaseTrainSpec.java b/buildSrc/src/main/java/org/springframework/gradle/github/milestones/SpringReleaseTrainSpec.java index 8b2f3bcd0d..792e390c00 100644 --- a/buildSrc/src/main/java/org/springframework/gradle/github/milestones/SpringReleaseTrainSpec.java +++ b/buildSrc/src/main/java/org/springframework/gradle/github/milestones/SpringReleaseTrainSpec.java @@ -114,11 +114,11 @@ public final class SpringReleaseTrainSpec { } public Builder train(int train) { - this.train = switch (train) { - case 1 -> Train.ONE; - case 2 -> Train.TWO; - default -> throw new IllegalArgumentException("Invalid train: " + train); - }; + switch (train) { + case 1: this.train = Train.ONE; break; + case 2: this.train = Train.TWO; break; + default: throw new IllegalArgumentException("Invalid train: " + train); + } return this; } @@ -156,13 +156,13 @@ public final class SpringReleaseTrainSpec { } public Builder weekOfMonth(int weekOfMonth) { - this.weekOfMonth = switch (weekOfMonth) { - case 1 -> WeekOfMonth.FIRST; - case 2 -> WeekOfMonth.SECOND; - case 3 -> WeekOfMonth.THIRD; - case 4 -> WeekOfMonth.FOURTH; - default -> throw new IllegalArgumentException("Invalid weekOfMonth: " + weekOfMonth); - }; + switch (weekOfMonth) { + case 1: this.weekOfMonth = WeekOfMonth.FIRST; break; + case 2: this.weekOfMonth = WeekOfMonth.SECOND; break; + case 3: this.weekOfMonth = WeekOfMonth.THIRD; break; + case 4: this.weekOfMonth = WeekOfMonth.FOURTH; break; + default: throw new IllegalArgumentException("Invalid weekOfMonth: " + weekOfMonth); + } return this; } @@ -172,14 +172,14 @@ public final class SpringReleaseTrainSpec { } public Builder dayOfWeek(int dayOfWeek) { - this.dayOfWeek = switch (dayOfWeek) { - case 1 -> DayOfWeek.MONDAY; - case 2 -> DayOfWeek.TUESDAY; - case 3 -> DayOfWeek.WEDNESDAY; - case 4 -> DayOfWeek.THURSDAY; - case 5 -> DayOfWeek.FRIDAY; - default -> throw new IllegalArgumentException("Invalid dayOfWeek: " + dayOfWeek); - }; + switch (dayOfWeek) { + case 1: this.dayOfWeek = DayOfWeek.MONDAY; break; + case 2: this.dayOfWeek = DayOfWeek.TUESDAY; break; + case 3: this.dayOfWeek = DayOfWeek.WEDNESDAY; break; + case 4: this.dayOfWeek = DayOfWeek.THURSDAY; break; + case 5: this.dayOfWeek = DayOfWeek.FRIDAY; break; + default: throw new IllegalArgumentException("Invalid dayOfWeek: " + dayOfWeek); + } return this; } diff --git a/buildSrc/src/main/java/org/springframework/gradle/maven/SpringSigningPlugin.java b/buildSrc/src/main/java/org/springframework/gradle/maven/SpringSigningPlugin.java index 076872770a..112651ba2e 100644 --- a/buildSrc/src/main/java/org/springframework/gradle/maven/SpringSigningPlugin.java +++ b/buildSrc/src/main/java/org/springframework/gradle/maven/SpringSigningPlugin.java @@ -21,6 +21,7 @@ import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.publish.Publication; import org.gradle.api.publish.PublishingExtension; +import org.gradle.api.publish.plugins.PublishingPlugin; import org.gradle.plugins.signing.SigningExtension; import org.gradle.plugins.signing.SigningPlugin; @@ -43,7 +44,12 @@ public class SpringSigningPlugin implements Plugin { private void sign(Project project) { SigningExtension signing = project.getExtensions().findByType(SigningExtension.class); - signing.setRequired((Callable) () -> project.getGradle().getTaskGraph().hasTask("publishArtifacts")); + signing.setRequired(new Callable() { + @Override + public Boolean call() throws Exception { + return project.getGradle().getTaskGraph().hasTask("publishArtifacts"); + } + }); String signingKeyId = (String) project.findProperty("signingKeyId"); String signingKey = (String) project.findProperty("signingKey"); String signingPassword = (String) project.findProperty("signingPassword"); diff --git a/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganCreateReleaseTask.java b/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganCreateReleaseTask.java index 2e6d33f405..7c37a02143 100644 --- a/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganCreateReleaseTask.java +++ b/buildSrc/src/main/java/org/springframework/gradle/sagan/SaganCreateReleaseTask.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2020 the original author or authors. + * Copyright 2019-2023 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. diff --git a/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java b/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java index 94c783e3a2..854332f7f3 100644 --- a/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java +++ b/cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java @@ -38,7 +38,6 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; @@ -196,9 +195,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil private SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository(); - private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder - .getContextHolderStrategy(); - public CasAuthenticationFilter() { super("/login/cas"); setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler()); @@ -215,10 +211,9 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil } this.logger.debug( LogMessage.format("Authentication success. Updating SecurityContextHolder to contain: %s", authResult)); - - SecurityContext context = this.securityContextHolderStrategy.createEmptyContext(); + SecurityContext context = SecurityContextHolder.createEmptyContext(); context.setAuthentication(authResult); - this.securityContextHolderStrategy.setContext(context); + SecurityContextHolder.setContext(context); this.securityContextRepository.saveContext(context, request, response); if (this.eventPublisher != null) { this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass())); diff --git a/config/spring-security-config.gradle b/config/spring-security-config.gradle index 05dbc07057..cd0a11b1a5 100644 --- a/config/spring-security-config.gradle +++ b/config/spring-security-config.gradle @@ -80,6 +80,7 @@ dependencies { testImplementation "org.hibernate.orm:hibernate-core" testImplementation 'org.hsqldb:hsqldb' testImplementation 'org.mockito:mockito-core' + testImplementation "org.mockito:mockito-inline" testImplementation('org.seleniumhq.selenium:htmlunit-driver') { exclude group: 'commons-logging', module: 'commons-logging' exclude group: 'xml-apis', module: 'xml-apis' diff --git a/config/src/integration-test/java/org/springframework/security/config/annotation/rsocket/RSocketMessageHandlerITests.java b/config/src/integration-test/java/org/springframework/security/config/annotation/rsocket/RSocketMessageHandlerITests.java index ef65fb55c7..2c30dce0f1 100644 --- a/config/src/integration-test/java/org/springframework/security/config/annotation/rsocket/RSocketMessageHandlerITests.java +++ b/config/src/integration-test/java/org/springframework/security/config/annotation/rsocket/RSocketMessageHandlerITests.java @@ -294,7 +294,7 @@ public class RSocketMessageHandlerITests { @MessageMapping({ "secure.send", "send" }) Mono send(Mono payload) { - return payload.doOnNext(this::add).then(Mono.fromRunnable(this::doNotifyAll)); + return payload.doOnNext(this::add).then(Mono.fromRunnable(() -> doNotifyAll())); } private synchronized void doNotifyAll() { diff --git a/config/src/integration-test/java/org/springframework/security/config/ldap/LdapUserServiceBeanDefinitionParserTests.java b/config/src/integration-test/java/org/springframework/security/config/ldap/LdapUserServiceBeanDefinitionParserTests.java index cc195ca2fd..d43bc69d6b 100644 --- a/config/src/integration-test/java/org/springframework/security/config/ldap/LdapUserServiceBeanDefinitionParserTests.java +++ b/config/src/integration-test/java/org/springframework/security/config/ldap/LdapUserServiceBeanDefinitionParserTests.java @@ -87,7 +87,7 @@ public class LdapUserServiceBeanDefinitionParserTests { Set authorities = AuthorityUtils.authorityListToSet(ben.getAuthorities()); assertThat(authorities).hasSize(3); - assertThat(authorities).contains("ROLE_DEVELOPERS"); + assertThat(authorities.contains("ROLE_DEVELOPERS")).isTrue(); } @Test @@ -128,7 +128,7 @@ public class LdapUserServiceBeanDefinitionParserTests { Set authorities = AuthorityUtils.authorityListToSet(ben.getAuthorities()); assertThat(authorities).hasSize(3); - assertThat(authorities).contains("ROLE_DEVELOPER"); + assertThat(authorities.contains("ROLE_DEVELOPER")).isTrue(); } diff --git a/config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java b/config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java index a1921249fb..5ec3815d58 100644 --- a/config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java +++ b/config/src/main/java/org/springframework/security/config/SecurityNamespaceHandler.java @@ -96,7 +96,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler { 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 6.2. Please update your schema declarations to the 6.2 schema.", + + "with Spring Security 6.1. Please update your schema declarations to the 6.1 schema.", element); } String name = pc.getDelegate().getLocalName(element); @@ -221,7 +221,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-6\\.2.*.xsd.*") + return schemaLocation.matches("(?m).*spring-security-6\\.1.*.xsd.*") || schemaLocation.matches("(?m).*spring-security.xsd.*") || !schemaLocation.matches("(?m).*spring-security.*"); } diff --git a/config/src/main/java/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java b/config/src/main/java/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java index 4293926460..d47cbd8491 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java +++ b/config/src/main/java/org/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2013 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,7 +27,6 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.util.Assert; import org.springframework.web.filter.DelegatingFilterProxy; @@ -118,10 +117,7 @@ public abstract class AbstractConfiguredSecurityBuilder> C apply(C configurer) throws Exception { configurer.addObjectPostProcessor(this.objectPostProcessor); @@ -143,23 +139,6 @@ public abstract class AbstractConfiguredSecurityBuilder> B with(C configurer, Customizer customizer) throws Exception { - configurer.addObjectPostProcessor(this.objectPostProcessor); - configurer.setBuilder((B) this); - add(configurer); - customizer.customize(configurer); - return (B) this; - } - /** * Sets an object that is shared by multiple {@link SecurityConfigurer}. * @param sharedType the Class to key the shared object by. diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java index 4b561360a7..9a0d08a902 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java @@ -41,8 +41,7 @@ final class MethodSecuritySelector implements ImportSelector { @Override public String[] selectImports(@NonNull AnnotationMetadata importMetadata) { - if (!importMetadata.hasAnnotation(EnableMethodSecurity.class.getName()) - && !importMetadata.hasMetaAnnotation(EnableMethodSecurity.class.getName())) { + if (!importMetadata.hasAnnotation(EnableMethodSecurity.class.getName())) { return new String[0]; } EnableMethodSecurity annotation = importMetadata.getAnnotations().get(EnableMethodSecurity.class).synthesize(); diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java b/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java index 72dbee5365..c4047dd533 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,6 @@ import java.util.Map; import jakarta.servlet.DispatcherType; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletRegistration; -import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; @@ -204,30 +203,11 @@ public abstract class AbstractRequestMatcherRegistry { if (!hasDispatcherServlet(registrations)) { return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns)); } - ServletRegistration dispatcherServlet = requireOneRootDispatcherServlet(registrations); - if (dispatcherServlet != null) { - if (registrations.size() == 1) { - return requestMatchers(createMvcMatchers(method, patterns).toArray(RequestMatcher[]::new)); - } - List matchers = new ArrayList<>(); - for (String pattern : patterns) { - AntPathRequestMatcher ant = new AntPathRequestMatcher(pattern, (method != null) ? method.name() : null); - MvcRequestMatcher mvc = createMvcMatchers(method, pattern).get(0); - matchers.add(new DispatcherServletDelegatingRequestMatcher(ant, mvc, servletContext)); - } - return requestMatchers(matchers.toArray(new RequestMatcher[0])); + if (registrations.size() > 1) { + String errorMessage = computeErrorMessage(registrations.values()); + throw new IllegalArgumentException(errorMessage); } - dispatcherServlet = requireOnlyPathMappedDispatcherServlet(registrations); - if (dispatcherServlet != null) { - String mapping = dispatcherServlet.getMappings().iterator().next(); - List matchers = createMvcMatchers(method, patterns); - for (MvcRequestMatcher matcher : matchers) { - matcher.setServletPath(mapping.substring(0, mapping.length() - 2)); - } - return requestMatchers(matchers.toArray(new RequestMatcher[0])); - } - String errorMessage = computeErrorMessage(registrations.values()); - throw new IllegalArgumentException(errorMessage); + return requestMatchers(createMvcMatchers(method, patterns).toArray(new RequestMatcher[0])); } private Map mappableServletRegistrations(ServletContext servletContext) { @@ -245,66 +225,22 @@ public abstract class AbstractRequestMatcherRegistry { if (registrations == null) { return false; } + Class dispatcherServlet = ClassUtils.resolveClassName("org.springframework.web.servlet.DispatcherServlet", + null); for (ServletRegistration registration : registrations.values()) { - if (isDispatcherServlet(registration)) { - return true; + try { + Class clazz = Class.forName(registration.getClassName()); + if (dispatcherServlet.isAssignableFrom(clazz)) { + return true; + } + } + catch (ClassNotFoundException ex) { + return false; } } return false; } - private ServletRegistration requireOneRootDispatcherServlet( - Map registrations) { - ServletRegistration rootDispatcherServlet = null; - for (ServletRegistration registration : registrations.values()) { - if (!isDispatcherServlet(registration)) { - continue; - } - if (registration.getMappings().size() > 1) { - return null; - } - if (!"/".equals(registration.getMappings().iterator().next())) { - return null; - } - rootDispatcherServlet = registration; - } - return rootDispatcherServlet; - } - - private ServletRegistration requireOnlyPathMappedDispatcherServlet( - Map registrations) { - ServletRegistration pathDispatcherServlet = null; - for (ServletRegistration registration : registrations.values()) { - if (!isDispatcherServlet(registration)) { - return null; - } - if (registration.getMappings().size() > 1) { - return null; - } - String mapping = registration.getMappings().iterator().next(); - if (!mapping.startsWith("/") || !mapping.endsWith("/*")) { - return null; - } - if (pathDispatcherServlet != null) { - return null; - } - pathDispatcherServlet = registration; - } - return pathDispatcherServlet; - } - - private boolean isDispatcherServlet(ServletRegistration registration) { - Class dispatcherServlet = ClassUtils.resolveClassName("org.springframework.web.servlet.DispatcherServlet", - null); - try { - Class clazz = Class.forName(registration.getClassName()); - return dispatcherServlet.isAssignableFrom(clazz); - } - catch (ClassNotFoundException ex) { - return false; - } - } - private String computeErrorMessage(Collection registrations) { String template = "This method cannot decide whether these patterns are Spring MVC patterns or not. " + "If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); " @@ -444,55 +380,4 @@ public abstract class AbstractRequestMatcherRegistry { } - static class DispatcherServletDelegatingRequestMatcher implements RequestMatcher { - - private final AntPathRequestMatcher ant; - - private final MvcRequestMatcher mvc; - - private final ServletContext servletContext; - - DispatcherServletDelegatingRequestMatcher(AntPathRequestMatcher ant, MvcRequestMatcher mvc, - ServletContext servletContext) { - this.ant = ant; - this.mvc = mvc; - this.servletContext = servletContext; - } - - @Override - public boolean matches(HttpServletRequest request) { - String name = request.getHttpServletMapping().getServletName(); - ServletRegistration registration = this.servletContext.getServletRegistration(name); - Assert.notNull(registration, "Failed to find servlet [" + name + "] in the servlet context"); - if (isDispatcherServlet(registration)) { - return this.mvc.matches(request); - } - return this.ant.matches(request); - } - - @Override - public MatchResult matcher(HttpServletRequest request) { - String name = request.getHttpServletMapping().getServletName(); - ServletRegistration registration = this.servletContext.getServletRegistration(name); - Assert.notNull(registration, "Failed to find servlet [" + name + "] in the servlet context"); - if (isDispatcherServlet(registration)) { - return this.mvc.matcher(request); - } - return this.ant.matcher(request); - } - - private boolean isDispatcherServlet(ServletRegistration registration) { - Class dispatcherServlet = ClassUtils - .resolveClassName("org.springframework.web.servlet.DispatcherServlet", null); - try { - Class clazz = Class.forName(registration.getClassName()); - return dispatcherServlet.isAssignableFrom(clazz); - } - catch (ClassNotFoundException ex) { - return false; - } - } - - } - } diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java index 9d0333d24e..7957e7606a 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java @@ -70,7 +70,6 @@ import org.springframework.security.config.annotation.web.configurers.SessionMan import org.springframework.security.config.annotation.web.configurers.X509Configurer; import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2ClientConfigurer; import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer; -import org.springframework.security.config.annotation.web.configurers.oauth2.client.OidcLogoutConfigurer; import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; import org.springframework.security.config.annotation.web.configurers.saml2.Saml2LoginConfigurer; import org.springframework.security.config.annotation.web.configurers.saml2.Saml2LogoutConfigurer; @@ -2836,16 +2835,6 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder oidcLogout() throws Exception { - return getOrApply(new OidcLogoutConfigurer<>()); - } - - public HttpSecurity oidcLogout(Customizer> oidcLogoutCustomizer) - throws Exception { - oidcLogoutCustomizer.customize(getOrApply(new OidcLogoutConfigurer<>())); - return HttpSecurity.this; - } - /** * Configures OAuth 2.0 Client support. * @return the {@link OAuth2ClientConfigurer} for further customizations diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java index ba962a4fad..3889aa6d11 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,6 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter; import org.springframework.web.accept.ContentNegotiationStrategy; import org.springframework.web.accept.HeaderContentNegotiationStrategy; -import org.springframework.web.cors.CorsConfigurationSource; import static org.springframework.security.config.Customizer.withDefaults; @@ -125,18 +124,10 @@ class HttpSecurityConfiguration { .apply(new DefaultLoginPageConfigurer<>()); http.logout(withDefaults()); // @formatter:on - applyCorsIfAvailable(http); applyDefaultConfigurers(http); return http; } - private void applyCorsIfAvailable(HttpSecurity http) throws Exception { - String[] beanNames = this.context.getBeanNamesForType(CorsConfigurationSource.class); - if (beanNames.length == 1) { - http.cors(withDefaults()); - } - } - private AuthenticationManager authenticationManager() throws Exception { return this.authenticationConfiguration.getAuthenticationManager(); } diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfiguration.java index 668963f9d5..0fc647de67 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,46 +16,19 @@ package org.springframework.security.config.annotation.web.configuration; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.Set; -import java.util.function.Consumer; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.BeanFactoryUtils; -import org.springframework.beans.factory.BeanInitializationException; -import org.springframework.beans.factory.ListableBeanFactory; -import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; -import org.springframework.context.annotation.AnnotationBeanNameGenerator; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportSelector; -import org.springframework.core.ResolvableType; import org.springframework.core.type.AnnotationMetadata; import org.springframework.security.core.context.SecurityContextHolderStrategy; -import org.springframework.security.oauth2.client.AuthorizationCodeOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.ClientCredentialsOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.DelegatingOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.JwtBearerOAuth2AuthorizedClientProvider; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.PasswordOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder; import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest; -import org.springframework.security.oauth2.client.endpoint.OAuth2PasswordGrantRequest; -import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; @@ -75,8 +48,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; * @since 5.1 * @see OAuth2ImportSelector */ -@Import({ OAuth2ClientConfiguration.OAuth2ClientWebMvcImportSelector.class, - OAuth2ClientConfiguration.OAuth2AuthorizedClientManagerConfiguration.class }) +@Import(OAuth2ClientConfiguration.OAuth2ClientWebMvcImportSelector.class) final class OAuth2ClientConfiguration { private static final boolean webMvcPresent; @@ -93,22 +65,8 @@ final class OAuth2ClientConfiguration { if (!webMvcPresent) { return new String[0]; } - return new String[] { - OAuth2ClientConfiguration.class.getName() + ".OAuth2ClientWebMvcSecurityConfiguration" }; - } - - } - - /** - * @author Joe Grandja - * @since 6.2.0 - */ - @Configuration(proxyBeanMethods = false) - static class OAuth2AuthorizedClientManagerConfiguration { - - @Bean - OAuth2AuthorizedClientManagerRegistrar authorizedClientManagerRegistrar() { - return new OAuth2AuthorizedClientManagerRegistrar(); + return new String[] { "org.springframework.security.config.annotation.web.configuration." + + "OAuth2ClientConfiguration.OAuth2ClientWebMvcSecurityConfiguration" }; } } @@ -116,12 +74,16 @@ final class OAuth2ClientConfiguration { @Configuration(proxyBeanMethods = false) static class OAuth2ClientWebMvcSecurityConfiguration implements WebMvcConfigurer { + private ClientRegistrationRepository clientRegistrationRepository; + + private OAuth2AuthorizedClientRepository authorizedClientRepository; + + private OAuth2AccessTokenResponseClient accessTokenResponseClient; + private OAuth2AuthorizedClientManager authorizedClientManager; private SecurityContextHolderStrategy securityContextHolderStrategy; - private OAuth2AuthorizedClientManagerRegistrar authorizedClientManagerRegistrar; - @Override public void addArgumentResolvers(List argumentResolvers) { OAuth2AuthorizedClientManager authorizedClientManager = getAuthorizedClientManager(); @@ -135,6 +97,26 @@ final class OAuth2ClientConfiguration { } } + @Autowired(required = false) + void setClientRegistrationRepository(List clientRegistrationRepositories) { + if (clientRegistrationRepositories.size() == 1) { + this.clientRegistrationRepository = clientRegistrationRepositories.get(0); + } + } + + @Autowired(required = false) + void setAuthorizedClientRepository(List authorizedClientRepositories) { + if (authorizedClientRepositories.size() == 1) { + this.authorizedClientRepository = authorizedClientRepositories.get(0); + } + } + + @Autowired(required = false) + void setAccessTokenResponseClient( + OAuth2AccessTokenResponseClient accessTokenResponseClient) { + this.accessTokenResponseClient = accessTokenResponseClient; + } + @Autowired(required = false) void setAuthorizedClientManager(List authorizedClientManagers) { if (authorizedClientManagers.size() == 1) { @@ -147,262 +129,35 @@ final class OAuth2ClientConfiguration { this.securityContextHolderStrategy = strategy; } - @Autowired - void setAuthorizedClientManagerRegistrar( - OAuth2AuthorizedClientManagerRegistrar authorizedClientManagerRegistrar) { - this.authorizedClientManagerRegistrar = authorizedClientManagerRegistrar; - } - private OAuth2AuthorizedClientManager getAuthorizedClientManager() { if (this.authorizedClientManager != null) { return this.authorizedClientManager; } - return this.authorizedClientManagerRegistrar.getAuthorizedClientManagerIfAvailable(); - } - - } - - /** - * A registrar for registering the default {@link OAuth2AuthorizedClientManager} bean - * definition, if not already present. - * - * @author Joe Grandja - * @author Steve Riesenberg - * @since 6.2.0 - */ - static final class OAuth2AuthorizedClientManagerRegistrar - implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware { - - // @formatter:off - private static final Set> KNOWN_AUTHORIZED_CLIENT_PROVIDERS = Set.of( - AuthorizationCodeOAuth2AuthorizedClientProvider.class, - RefreshTokenOAuth2AuthorizedClientProvider.class, - ClientCredentialsOAuth2AuthorizedClientProvider.class, - PasswordOAuth2AuthorizedClientProvider.class, - JwtBearerOAuth2AuthorizedClientProvider.class - ); - // @formatter:on - - private final AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); - - private ListableBeanFactory beanFactory; - - @Override - public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { - if (getBeanNamesForType(OAuth2AuthorizedClientManager.class).length != 0 - || getBeanNamesForType(ClientRegistrationRepository.class).length != 1 - || getBeanNamesForType(OAuth2AuthorizedClientRepository.class).length != 1) { - return; - } - - BeanDefinition beanDefinition = BeanDefinitionBuilder - .genericBeanDefinition(OAuth2AuthorizedClientManager.class, this::getAuthorizedClientManager) - .getBeanDefinition(); - - registry.registerBeanDefinition(this.beanNameGenerator.generateBeanName(beanDefinition, registry), - beanDefinition); - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - } - - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = (ListableBeanFactory) beanFactory; - } - - OAuth2AuthorizedClientManager getAuthorizedClientManagerIfAvailable() { - if (getBeanNamesForType(ClientRegistrationRepository.class).length != 1 - || getBeanNamesForType(OAuth2AuthorizedClientRepository.class).length != 1) { - return null; - } - return getAuthorizedClientManager(); - } - - private OAuth2AuthorizedClientManager getAuthorizedClientManager() { - ClientRegistrationRepository clientRegistrationRepository = BeanFactoryUtils - .beanOfTypeIncludingAncestors(this.beanFactory, ClientRegistrationRepository.class, true, true); - - OAuth2AuthorizedClientRepository authorizedClientRepository = BeanFactoryUtils - .beanOfTypeIncludingAncestors(this.beanFactory, OAuth2AuthorizedClientRepository.class, true, true); - - Collection authorizedClientProviderBeans = BeanFactoryUtils - .beansOfTypeIncludingAncestors(this.beanFactory, OAuth2AuthorizedClientProvider.class, true, true) - .values(); - - OAuth2AuthorizedClientProvider authorizedClientProvider; - if (hasDelegatingAuthorizedClientProvider(authorizedClientProviderBeans)) { - authorizedClientProvider = authorizedClientProviderBeans.iterator().next(); - } - else { - List authorizedClientProviders = new ArrayList<>(); - authorizedClientProviders - .add(getAuthorizationCodeAuthorizedClientProvider(authorizedClientProviderBeans)); - authorizedClientProviders.add(getRefreshTokenAuthorizedClientProvider(authorizedClientProviderBeans)); - authorizedClientProviders - .add(getClientCredentialsAuthorizedClientProvider(authorizedClientProviderBeans)); - authorizedClientProviders.add(getPasswordAuthorizedClientProvider(authorizedClientProviderBeans)); - - OAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = getJwtBearerAuthorizedClientProvider( - authorizedClientProviderBeans); - if (jwtBearerAuthorizedClientProvider != null) { - authorizedClientProviders.add(jwtBearerAuthorizedClientProvider); + OAuth2AuthorizedClientManager authorizedClientManager = null; + if (this.clientRegistrationRepository != null && this.authorizedClientRepository != null) { + if (this.accessTokenResponseClient != null) { + // @formatter:off + OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder + .builder() + .authorizationCode() + .refreshToken() + .clientCredentials((configurer) -> configurer.accessTokenResponseClient(this.accessTokenResponseClient)) + .password() + .build(); + // @formatter:on + DefaultOAuth2AuthorizedClientManager defaultAuthorizedClientManager = new DefaultOAuth2AuthorizedClientManager( + this.clientRegistrationRepository, this.authorizedClientRepository); + defaultAuthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); + authorizedClientManager = defaultAuthorizedClientManager; + } + else { + authorizedClientManager = new DefaultOAuth2AuthorizedClientManager( + this.clientRegistrationRepository, this.authorizedClientRepository); } - - authorizedClientProviders.addAll(getAdditionalAuthorizedClientProviders(authorizedClientProviderBeans)); - authorizedClientProvider = new DelegatingOAuth2AuthorizedClientProvider(authorizedClientProviders); } - - DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager( - clientRegistrationRepository, authorizedClientRepository); - authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); - - Consumer authorizedClientManagerConsumer = getBeanOfType( - ResolvableType.forClassWithGenerics(Consumer.class, DefaultOAuth2AuthorizedClientManager.class)); - if (authorizedClientManagerConsumer != null) { - authorizedClientManagerConsumer.accept(authorizedClientManager); - } - return authorizedClientManager; } - private boolean hasDelegatingAuthorizedClientProvider( - Collection authorizedClientProviders) { - if (authorizedClientProviders.size() != 1) { - return false; - } - return authorizedClientProviders.iterator().next() instanceof DelegatingOAuth2AuthorizedClientProvider; - } - - private OAuth2AuthorizedClientProvider getAuthorizationCodeAuthorizedClientProvider( - Collection authorizedClientProviders) { - AuthorizationCodeOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType( - authorizedClientProviders, AuthorizationCodeOAuth2AuthorizedClientProvider.class); - if (authorizedClientProvider == null) { - authorizedClientProvider = new AuthorizationCodeOAuth2AuthorizedClientProvider(); - } - - return authorizedClientProvider; - } - - private OAuth2AuthorizedClientProvider getRefreshTokenAuthorizedClientProvider( - Collection authorizedClientProviders) { - RefreshTokenOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType( - authorizedClientProviders, RefreshTokenOAuth2AuthorizedClientProvider.class); - if (authorizedClientProvider == null) { - authorizedClientProvider = new RefreshTokenOAuth2AuthorizedClientProvider(); - } - - OAuth2AccessTokenResponseClient accessTokenResponseClient = getBeanOfType( - ResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class, - OAuth2RefreshTokenGrantRequest.class)); - if (accessTokenResponseClient != null) { - authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient); - } - - return authorizedClientProvider; - } - - private OAuth2AuthorizedClientProvider getClientCredentialsAuthorizedClientProvider( - Collection authorizedClientProviders) { - ClientCredentialsOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType( - authorizedClientProviders, ClientCredentialsOAuth2AuthorizedClientProvider.class); - if (authorizedClientProvider == null) { - authorizedClientProvider = new ClientCredentialsOAuth2AuthorizedClientProvider(); - } - - OAuth2AccessTokenResponseClient accessTokenResponseClient = getBeanOfType( - ResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class, - OAuth2ClientCredentialsGrantRequest.class)); - if (accessTokenResponseClient != null) { - authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient); - } - - return authorizedClientProvider; - } - - private OAuth2AuthorizedClientProvider getPasswordAuthorizedClientProvider( - Collection authorizedClientProviders) { - PasswordOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType( - authorizedClientProviders, PasswordOAuth2AuthorizedClientProvider.class); - if (authorizedClientProvider == null) { - authorizedClientProvider = new PasswordOAuth2AuthorizedClientProvider(); - } - - OAuth2AccessTokenResponseClient accessTokenResponseClient = getBeanOfType( - ResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class, - OAuth2PasswordGrantRequest.class)); - if (accessTokenResponseClient != null) { - authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient); - } - - return authorizedClientProvider; - } - - private OAuth2AuthorizedClientProvider getJwtBearerAuthorizedClientProvider( - Collection authorizedClientProviders) { - JwtBearerOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType( - authorizedClientProviders, JwtBearerOAuth2AuthorizedClientProvider.class); - - OAuth2AccessTokenResponseClient accessTokenResponseClient = getBeanOfType( - ResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class, - JwtBearerGrantRequest.class)); - if (accessTokenResponseClient != null) { - if (authorizedClientProvider == null) { - authorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider(); - } - - authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient); - } - - return authorizedClientProvider; - } - - private List getAdditionalAuthorizedClientProviders( - Collection authorizedClientProviders) { - List additionalAuthorizedClientProviders = new ArrayList<>( - authorizedClientProviders); - additionalAuthorizedClientProviders - .removeIf((provider) -> KNOWN_AUTHORIZED_CLIENT_PROVIDERS.contains(provider.getClass())); - return additionalAuthorizedClientProviders; - } - - private T getAuthorizedClientProviderByType( - Collection authorizedClientProviders, Class providerClass) { - T authorizedClientProvider = null; - for (OAuth2AuthorizedClientProvider current : authorizedClientProviders) { - if (providerClass.isInstance(current)) { - assertAuthorizedClientProviderIsNull(authorizedClientProvider); - authorizedClientProvider = providerClass.cast(current); - } - } - return authorizedClientProvider; - } - - private static void assertAuthorizedClientProviderIsNull( - OAuth2AuthorizedClientProvider authorizedClientProvider) { - if (authorizedClientProvider != null) { - // @formatter:off - throw new BeanInitializationException(String.format( - "Unable to create an %s bean. Expected one bean of type %s, but found multiple. " + - "Please consider defining only a single bean of this type, or define an %s bean yourself.", - OAuth2AuthorizedClientManager.class.getName(), - authorizedClientProvider.getClass().getName(), - OAuth2AuthorizedClientManager.class.getName())); - // @formatter:on - } - } - - private String[] getBeanNamesForType(Class beanClass) { - return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, beanClass, true, true); - } - - private T getBeanOfType(ResolvableType resolvableType) { - ObjectProvider objectProvider = this.beanFactory.getBeanProvider(resolvableType, true); - return objectProvider.getIfAvailable(); - } - } } diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractRequestMatcherBuilderRegistry.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractRequestMatcherBuilderRegistry.java deleted file mode 100644 index 57bd420c3b..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractRequestMatcherBuilderRegistry.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers; - -import org.springframework.context.ApplicationContext; -import org.springframework.http.HttpMethod; -import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry; -import org.springframework.security.web.util.matcher.RequestMatcher; - -abstract class AbstractRequestMatcherBuilderRegistry extends AbstractRequestMatcherRegistry { - - private final RequestMatcherBuilder builder; - - AbstractRequestMatcherBuilderRegistry(ApplicationContext context) { - this(context, RequestMatcherBuilders.createDefault(context)); - } - - AbstractRequestMatcherBuilderRegistry(ApplicationContext context, RequestMatcherBuilder builder) { - setApplicationContext(context); - this.builder = builder; - } - - @Override - public final C requestMatchers(String... patterns) { - return requestMatchers(null, patterns); - } - - @Override - public final C requestMatchers(HttpMethod method, String... patterns) { - return requestMatchers(this.builder.matchers(method, patterns).toArray(RequestMatcher[]::new)); - } - - @Override - public final C requestMatchers(HttpMethod method) { - return requestMatchers(method, "/**"); - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AntPathRequestMatcherBuilder.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AntPathRequestMatcherBuilder.java deleted file mode 100644 index 4026fa2d2f..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AntPathRequestMatcherBuilder.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers; - -import org.springframework.http.HttpMethod; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; - -final class AntPathRequestMatcherBuilder implements RequestMatcherBuilder { - - private final String servletPath; - - private AntPathRequestMatcherBuilder(String servletPath) { - this.servletPath = servletPath; - } - - static AntPathRequestMatcherBuilder absolute() { - return new AntPathRequestMatcherBuilder(null); - } - - static AntPathRequestMatcherBuilder relativeTo(String path) { - return new AntPathRequestMatcherBuilder(path); - } - - @Override - public AntPathRequestMatcher matcher(String pattern) { - return matcher((String) null, pattern); - } - - @Override - public AntPathRequestMatcher matcher(HttpMethod method, String pattern) { - return matcher((method != null) ? method.name() : null, pattern); - } - - private AntPathRequestMatcher matcher(String method, String pattern) { - return new AntPathRequestMatcher(prependServletPath(pattern), method); - } - - private String prependServletPath(String pattern) { - if (this.servletPath == null) { - return pattern; - } - return this.servletPath + pattern; - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java index f67ba9ad4c..a6c6f2b208 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java @@ -16,14 +16,10 @@ package org.springframework.security.config.annotation.web.configurers; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.function.Function; import java.util.function.Supplier; import io.micrometer.observation.ObservationRegistry; -import jakarta.servlet.http.HttpServletMapping; import jakarta.servlet.http.HttpServletRequest; import org.springframework.context.ApplicationContext; @@ -36,22 +32,16 @@ import org.springframework.security.authorization.AuthorizationEventPublisher; import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.ObservationAuthorizationManager; import org.springframework.security.authorization.SpringAuthorizationEventPublisher; -import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry; import org.springframework.security.config.annotation.web.HttpSecurityBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.security.web.access.intercept.AuthorizationFilter; import org.springframework.security.web.access.intercept.RequestAuthorizationContext; import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager; -import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcherEntry; import org.springframework.util.Assert; import org.springframework.util.function.SingletonSupplier; -import org.springframework.web.servlet.DispatcherServlet; /** * Adds a URL based authorization using {@link AuthorizationManager}. @@ -72,8 +62,6 @@ public final class AuthorizeHttpRequestsConfigurer roleHierarchy; - private String rolePrefix = "ROLE_"; - /** * Creates an instance. * @param context the {@link ApplicationContext} to use @@ -88,11 +76,6 @@ public final class AuthorizeHttpRequestsConfigurer (context.getBeanNamesForType(RoleHierarchy.class).length > 0) ? context.getBean(RoleHierarchy.class) : new NullRoleHierarchy()); - String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class); - if (grantedAuthorityDefaultsBeanNames.length > 0) { - GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBean(GrantedAuthorityDefaults.class); - this.rolePrefix = grantedAuthorityDefaults.getRolePrefix(); - } } /** @@ -146,62 +129,41 @@ public final class AuthorizeHttpRequestsConfigurer { + extends AbstractRequestMatcherRegistry { private final RequestMatcherDelegatingAuthorizationManager.Builder managerBuilder = RequestMatcherDelegatingAuthorizationManager .builder(); - List unmappedMatchers; + private List unmappedMatchers; private int mappingCount; private boolean shouldFilterAllDispatcherTypes = true; - private final Map servletPattern = new LinkedHashMap<>(); - - AuthorizationManagerRequestMatcherRegistry(ApplicationContext context) { - super(context); + private AuthorizationManagerRequestMatcherRegistry(ApplicationContext context) { + setApplicationContext(context); } private void addMapping(RequestMatcher matcher, AuthorizationManager manager) { - Assert.isTrue(this.servletPattern.isEmpty(), - "Since you have used forServletPattern, all request matchers must be configured using forServletPattern; alternatively, you can use requestMatchers(RequestMatcher) for all requests."); this.unmappedMatchers = null; this.managerBuilder.add(matcher, manager); this.mappingCount++; } private void addFirst(RequestMatcher matcher, AuthorizationManager manager) { - Assert.isTrue(this.servletPattern.isEmpty(), - "Since you have used forServletPattern, all request matchers must be configured using forServletPattern; alternatively, you can use requestMatchers(RequestMatcher) for all requests."); this.unmappedMatchers = null; this.managerBuilder.mappings((m) -> m.add(0, new RequestMatcherEntry<>(matcher, manager))); this.mappingCount++; } - private AuthorizationManager servletAuthorizationManager() { - for (Map.Entry entry : this.servletPattern - .entrySet()) { - AuthorizationManagerServletRequestMatcherRegistry registry = entry.getValue(); - this.managerBuilder.add(new ServletPatternRequestMatcher(entry.getKey()), - registry.authorizationManager()); - } - return postProcess(this.managerBuilder.build()); - } - - private AuthorizationManager authorizationManager() { + private AuthorizationManager createAuthorizationManager() { Assert.state(this.unmappedMatchers == null, () -> "An incomplete mapping was found for " + this.unmappedMatchers + ". Try completing it with something like requestUrls()..hasRole('USER')"); Assert.state(this.mappingCount > 0, "At least one mapping is required (for example, authorizeHttpRequests().anyRequest().authenticated())"); - return postProcess(this.managerBuilder.build()); - } - - private AuthorizationManager createAuthorizationManager() { - AuthorizationManager manager = (this.servletPattern.isEmpty()) ? authorizationManager() - : servletAuthorizationManager(); ObservationRegistry registry = getObservationRegistry(); + RequestMatcherDelegatingAuthorizationManager manager = postProcess(this.managerBuilder.build()); if (registry.isNoop()) { return manager; } @@ -211,74 +173,7 @@ public final class AuthorizeHttpRequestsConfigurer requestMatchers) { this.unmappedMatchers = requestMatchers; - return new AuthorizedUrl( - (manager) -> AuthorizeHttpRequestsConfigurer.this.addMapping(requestMatchers, manager)); - } - - /** - * Begin registering {@link RequestMatcher}s based on the type of the servlet - * mapped to {@code pattern}. Each registered request matcher will additionally - * check {@link HttpServletMapping#getPattern} against the provided - * {@code pattern}. - * - *

- * If the corresponding servlet is of type {@link DispatcherServlet}, then use a - * {@link AuthorizationManagerServletRequestMatcherRegistry} that registers - * {@link MvcRequestMatcher}s. - * - *

- * Otherwise, use a configurer that registers {@link AntPathRequestMatcher}s. - * - *

- * When doing a path-based pattern, like `/path/*`, registered URIs should leave - * out the matching path. For example, if the target URI is `/path/resource/3`, - * then the configuration should look like this: - * .forServletPattern("/path/*", (path) -> path - * .requestMatchers("/resource/3").hasAuthority(...) - * ) - * - * - *

- * Or, if the pattern is `/path/subpath/*`, and the URI is - * `/path/subpath/resource/3`, then the configuration should look like this: - * - * .forServletPattern("/path/subpath/*", (path) -> path - * .requestMatchers("/resource/3").hasAuthority(...) - * ) - * - * - *

- * For all other patterns, please supply the URI in absolute terms. For example, - * if the target URI is `/js/**` and it matches to the default servlet, then the - * configuration should look like this: - * .forServletPattern("/", (root) -> root - * .requestMatchers("/js/**").hasAuthority(...) - * ) - * - * - *

- * Or, if the target URI is `/views/**`, and it matches to a `*.jsp` extension - * servlet, then the configuration should look like this: - * .forServletPattern("*.jsp", (jsp) -> jsp - * .requestMatchers("/views/**").hasAuthority(...) - * ) - * - * @param customizer a customizer that uses a - * {@link AuthorizationManagerServletRequestMatcherRegistry} for URIs mapped to - * the provided servlet - * @return an {@link AuthorizationManagerServletRequestMatcherRegistry} for - * further configurations - * @since 6.2 - */ - public AuthorizationManagerRequestMatcherRegistry forServletPattern(String pattern, - Customizer customizer) { - ApplicationContext context = getApplicationContext(); - RequestMatcherBuilder builder = RequestMatcherBuilders.createForServletPattern(context, pattern); - AuthorizationManagerServletRequestMatcherRegistry registry = new AuthorizationManagerServletRequestMatcherRegistry( - builder); - customizer.customize(registry); - this.servletPattern.put(pattern, registry); - return this; + return new AuthorizedUrl(requestMatchers); } /** @@ -334,265 +229,6 @@ public final class AuthorizeHttpRequestsConfigurer - * This class is designed primarily for use with the {@link HttpSecurity} DSL. For - * that reason, please use {@link HttpSecurity#authorizeHttpRequests} instead as - * it exposes this class fluently alongside related DSL configurations. - * - *

- * NOTE: In many cases, which kind of request matcher is needed is apparent by the - * servlet configuration, and so you should generally use the methods found in - * {@link AbstractRequestMatcherRegistry} instead of this these. Use this class - * when you want or need to indicate which request matcher URIs belong to which - * servlet. - * - *

- * In all cases, though, you may arrange your request matchers by servlet pattern - * with the {@link AuthorizationManagerRequestMatcherRegistry#forServletPattern} - * method in the {@link HttpSecurity#authorizeHttpRequests} DSL. - * - *

- * Consider, for example, the circumstance where you have Spring MVC configured - * and also Spring Boot H2 Console. Spring MVC registers a servlet of type - * {@link DispatcherServlet} as the default servlet and Spring Boot registers a - * servlet of its own as well at `/h2-console/*`. - * - *

- * Such might have a configuration like this in Spring Security: - * http - * .authorizeHttpRequests((authorize) -> authorize - * .requestMatchers("/js/**", "/css/**").permitAll() - * .requestMatchers("/my/controller/**").hasAuthority("CONTROLLER") - * .requestMatchers("/h2-console/**").hasAuthority("H2") - * ) - * // ... - * - * - *

- * Spring Security by default addresses the above configuration on its own. - * - *

- * However, consider the same situation, but where {@link DispatcherServlet} is - * mapped to a path like `/mvc/*`. In this case, the above configuration is - * ambiguous, and you should use this class to clarify the rest of each MVC URI - * like so: - * http - * .authorizeHttpRequests((authorize) -> authorize - * .forServletPattern("/", (root) -> root - * .requestMatchers("/js/**", "/css/**").permitAll() - * ) - * .forServletPattern("/mvc/*", (mvc) -> mvc - * .requestMatchers("/my/controller/**").hasAuthority("CONTROLLER") - * ) - * .forServletPattern("/h2-console/*", (h2) -> h2 - * .anyRequest().hasAuthority("OTHER") - * ) - * ) - * // ... - * - * - *

- * In the above configuration, it's now clear to Spring Security that the - * following matchers map to these corresponding URIs: - * - *

    - *
  • <default> + `/js/**` ==> `/js/**`
  • - *
  • <default> + `/css/**` ==> `/css/**`
  • - *
  • `/mvc` + `/my/controller/**` ==> - * `/mvc/my/controller/**`
  • - *
  • `/h2-console` + <any request> ==> - * `/h2-console/**`
  • - *
- * - * @author Josh Cummings - * @since 6.2 - * @see AbstractRequestMatcherRegistry - * @see AuthorizeHttpRequestsConfigurer - */ - public final class AuthorizationManagerServletRequestMatcherRegistry - extends AbstractRequestMatcherBuilderRegistry { - - private final RequestMatcherDelegatingAuthorizationManager.Builder managerBuilder = RequestMatcherDelegatingAuthorizationManager - .builder(); - - private List unmappedMatchers; - - AuthorizationManagerServletRequestMatcherRegistry(RequestMatcherBuilder builder) { - super(AuthorizationManagerRequestMatcherRegistry.this.getApplicationContext(), builder); - } - - AuthorizationManager authorizationManager() { - Assert.state(this.unmappedMatchers == null, - () -> "An incomplete mapping was found for " + this.unmappedMatchers - + ". Try completing it with something like requestUrls()..hasRole('USER')"); - AuthorizationManager request = this.managerBuilder.build(); - return (authentication, context) -> request.check(authentication, context.getRequest()); - } - - @Override - protected ServletAuthorizedUrl chainRequestMatchers(List requestMatchers) { - this.unmappedMatchers = requestMatchers; - return new ServletAuthorizedUrl((manager) -> addMapping(requestMatchers, manager)); - } - - private AuthorizationManagerServletRequestMatcherRegistry addMapping(List matchers, - AuthorizationManager manager) { - this.unmappedMatchers = null; - for (RequestMatcher matcher : matchers) { - this.managerBuilder.add(matcher, manager); - } - return this; - } - - } - - /** - * An object that allows configuring the {@link AuthorizationManager} for - * {@link RequestMatcher}s. - * - * @author Josh Cummings - * @since 6.2 - */ - public final class ServletAuthorizedUrl { - - private final Function, AuthorizationManagerServletRequestMatcherRegistry> registrar; - - ServletAuthorizedUrl( - Function, AuthorizationManagerServletRequestMatcherRegistry> registrar) { - this.registrar = registrar; - } - - /** - * Specify that URLs are allowed by anyone. - * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further - * customizations - */ - public AuthorizationManagerServletRequestMatcherRegistry permitAll() { - return access(permitAllAuthorizationManager); - } - - /** - * Specify that URLs are not allowed by anyone. - * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further - * customizations - */ - public AuthorizationManagerServletRequestMatcherRegistry denyAll() { - return access((a, o) -> new AuthorizationDecision(false)); - } - - /** - * Specifies a user requires a role. - * @param role the role that should be required which is prepended with ROLE_ - * automatically (i.e. USER, ADMIN, etc). It should not start with ROLE_ - * @return {@link AuthorizationManagerRequestMatcherRegistry} for further - * customizations - */ - public AuthorizationManagerServletRequestMatcherRegistry hasRole(String role) { - return access(withRoleHierarchy(AuthorityAuthorizationManager - .hasAnyRole(AuthorizeHttpRequestsConfigurer.this.rolePrefix, new String[] { role }))); - } - - /** - * Specifies that a user requires one of many roles. - * @param roles the roles that the user should have at least one of (i.e. - * ADMIN, USER, etc). Each role should not start with ROLE_ since it is - * automatically prepended already - * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further - * customizations - */ - public AuthorizationManagerServletRequestMatcherRegistry hasAnyRole(String... roles) { - return access(withRoleHierarchy(AuthorityAuthorizationManager - .hasAnyRole(AuthorizeHttpRequestsConfigurer.this.rolePrefix, roles))); - } - - /** - * Specifies a user requires an authority. - * @param authority the authority that should be required - * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further - * customizations - */ - public AuthorizationManagerServletRequestMatcherRegistry hasAuthority(String authority) { - return access(withRoleHierarchy(AuthorityAuthorizationManager.hasAuthority(authority))); - } - - /** - * Specifies that a user requires one of many authorities. - * @param authorities the authorities that the user should have at least one - * of (i.e. ROLE_USER, ROLE_ADMIN, etc) - * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further - * customizations - */ - public AuthorizationManagerServletRequestMatcherRegistry hasAnyAuthority(String... authorities) { - return access(withRoleHierarchy(AuthorityAuthorizationManager.hasAnyAuthority(authorities))); - } - - private AuthorityAuthorizationManager withRoleHierarchy( - AuthorityAuthorizationManager manager) { - manager.setRoleHierarchy(AuthorizeHttpRequestsConfigurer.this.roleHierarchy.get()); - return manager; - } - - /** - * Specify that URLs are allowed by any authenticated user. - * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further - * customizations - */ - public AuthorizationManagerServletRequestMatcherRegistry authenticated() { - return access(AuthenticatedAuthorizationManager.authenticated()); - } - - /** - * Specify that URLs are allowed by users who have authenticated and were not - * "remembered". - * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further - * customization - * @see RememberMeConfigurer - */ - public AuthorizationManagerServletRequestMatcherRegistry fullyAuthenticated() { - return access(AuthenticatedAuthorizationManager.fullyAuthenticated()); - } - - /** - * Specify that URLs are allowed by users that have been remembered. - * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further - * customization - * @since 5.8 - * @see RememberMeConfigurer - */ - public AuthorizationManagerServletRequestMatcherRegistry rememberMe() { - return access(AuthenticatedAuthorizationManager.rememberMe()); - } - - /** - * Specify that URLs are allowed by anonymous users. - * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further - * customization - * @since 5.8 - */ - public AuthorizationManagerServletRequestMatcherRegistry anonymous() { - return access(AuthenticatedAuthorizationManager.anonymous()); - } - - /** - * Allows specifying a custom {@link AuthorizationManager}. - * @param manager the {@link AuthorizationManager} to use - * @return the {@link AuthorizationManagerRequestMatcherRegistry} for further - * customizations - */ - public AuthorizationManagerServletRequestMatcherRegistry access( - AuthorizationManager manager) { - Assert.notNull(manager, "manager cannot be null"); - return this.registrar.apply(manager); - } - - } - } /** @@ -603,11 +239,18 @@ public final class AuthorizeHttpRequestsConfigurer, AuthorizationManagerRequestMatcherRegistry> registrar; + private final List matchers; - AuthorizedUrl( - Function, AuthorizationManagerRequestMatcherRegistry> registrar) { - this.registrar = registrar; + /** + * Creates an instance. + * @param matchers the {@link RequestMatcher} instances to map + */ + AuthorizedUrl(List matchers) { + this.matchers = matchers; + } + + protected List getMatchers() { + return this.matchers; } /** @@ -636,8 +279,7 @@ public final class AuthorizeHttpRequestsConfigurer manager) { Assert.notNull(manager, "manager cannot be null"); - return this.registrar.apply(manager); + return AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager); } } diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurer.java index 55c8f81ab8..7288fd486a 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/CsrfConfigurer.java @@ -40,6 +40,7 @@ import org.springframework.security.web.csrf.CsrfLogoutHandler; import org.springframework.security.web.csrf.CsrfTokenRepository; import org.springframework.security.web.csrf.CsrfTokenRequestHandler; import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; +import org.springframework.security.web.csrf.LazyCsrfTokenRepository; import org.springframework.security.web.csrf.MissingCsrfTokenException; import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler; import org.springframework.security.web.session.InvalidSessionStrategy; @@ -82,7 +83,7 @@ import org.springframework.util.Assert; public final class CsrfConfigurer> extends AbstractHttpConfigurer, H> { - private CsrfTokenRepository csrfTokenRepository = new HttpSessionCsrfTokenRepository(); + private CsrfTokenRepository csrfTokenRepository = new LazyCsrfTokenRepository(new HttpSessionCsrfTokenRepository()); private RequestMatcher requireCsrfProtectionMatcher = CsrfFilter.DEFAULT_CSRF_MATCHER; @@ -104,7 +105,7 @@ public final class CsrfConfigurer> /** * Specify the {@link CsrfTokenRepository} to use. The default is an - * {@link HttpSessionCsrfTokenRepository}. + * {@link HttpSessionCsrfTokenRepository} wrapped by {@link LazyCsrfTokenRepository}. * @param csrfTokenRepository the {@link CsrfTokenRepository} to use * @return the {@link CsrfConfigurer} for further customizations */ diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/DispatcherServletDelegatingRequestMatcherBuilder.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/DispatcherServletDelegatingRequestMatcherBuilder.java deleted file mode 100644 index bb300d5703..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/DispatcherServletDelegatingRequestMatcherBuilder.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers; - -import jakarta.servlet.http.HttpServletRequest; - -import org.springframework.http.HttpMethod; -import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.util.Assert; - -final class DispatcherServletDelegatingRequestMatcherBuilder implements RequestMatcherBuilder { - - final MvcRequestMatcherBuilder mvc; - - final AntPathRequestMatcherBuilder ant; - - final ServletRegistrationCollection registrations; - - DispatcherServletDelegatingRequestMatcherBuilder(MvcRequestMatcherBuilder mvc, AntPathRequestMatcherBuilder ant, - ServletRegistrationCollection registrations) { - this.mvc = mvc; - this.ant = ant; - this.registrations = registrations; - } - - @Override - public RequestMatcher matcher(String pattern) { - MvcRequestMatcher mvc = this.mvc.matcher(pattern); - AntPathRequestMatcher ant = this.ant.matcher(pattern); - return new DispatcherServletDelegatingRequestMatcher(mvc, ant, this.registrations); - } - - @Override - public RequestMatcher matcher(HttpMethod method, String pattern) { - MvcRequestMatcher mvc = this.mvc.matcher(method, pattern); - AntPathRequestMatcher ant = this.ant.matcher(method, pattern); - return new DispatcherServletDelegatingRequestMatcher(mvc, ant, this.registrations); - } - - static final class DispatcherServletDelegatingRequestMatcher implements RequestMatcher { - - private final MvcRequestMatcher mvc; - - private final AntPathRequestMatcher ant; - - private final ServletRegistrationCollection registrations; - - private DispatcherServletDelegatingRequestMatcher(MvcRequestMatcher mvc, AntPathRequestMatcher ant, - ServletRegistrationCollection registrations) { - this.mvc = mvc; - this.ant = ant; - this.registrations = registrations; - } - - @Override - public boolean matches(HttpServletRequest request) { - String name = request.getHttpServletMapping().getServletName(); - ServletRegistrationCollection.Registration registration = this.registrations.registrationByName(name); - Assert.notNull(registration, - String.format("Could not find %s in servlet configuration %s", name, this.registrations)); - if (registration.isDispatcherServlet()) { - return this.mvc.matches(request); - } - return this.ant.matches(request); - } - - @Override - public MatchResult matcher(HttpServletRequest request) { - String name = request.getHttpServletMapping().getServletName(); - ServletRegistrationCollection.Registration registration = this.registrations.registrationByName(name); - Assert.notNull(registration, - String.format("Could not find %s in servlet configuration %s", name, this.registrations)); - if (registration.isDispatcherServlet()) { - return this.mvc.matcher(request); - } - return this.ant.matcher(request); - } - - @Override - public String toString() { - return String.format("DispatcherServlet [mvc=[%s], ant=[%s], servlet=[%s]]", this.mvc, this.ant, - this.registrations); - } - - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/MvcRequestMatcherBuilder.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/MvcRequestMatcherBuilder.java deleted file mode 100644 index 60122c35c5..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/MvcRequestMatcherBuilder.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers; - -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.context.ApplicationContext; -import org.springframework.http.HttpMethod; -import org.springframework.security.config.annotation.ObjectPostProcessor; -import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; -import org.springframework.web.servlet.handler.HandlerMappingIntrospector; - -final class MvcRequestMatcherBuilder implements RequestMatcherBuilder { - - private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector"; - - private final HandlerMappingIntrospector introspector; - - private final ObjectPostProcessor objectPostProcessor; - - private final String servletPath; - - private MvcRequestMatcherBuilder(ApplicationContext context, String servletPath) { - if (!context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) { - throw new NoSuchBeanDefinitionException("A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME - + " of type " + HandlerMappingIntrospector.class.getName() - + " is required to use MvcRequestMatcher. Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext."); - } - this.introspector = context.getBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, HandlerMappingIntrospector.class); - this.objectPostProcessor = context.getBean(ObjectPostProcessor.class); - this.servletPath = servletPath; - } - - static MvcRequestMatcherBuilder absolute(ApplicationContext context) { - return new MvcRequestMatcherBuilder(context, null); - } - - static MvcRequestMatcherBuilder relativeTo(ApplicationContext context, String path) { - return new MvcRequestMatcherBuilder(context, path); - } - - @Override - public MvcRequestMatcher matcher(String pattern) { - MvcRequestMatcher matcher = new MvcRequestMatcher(this.introspector, pattern); - this.objectPostProcessor.postProcess(matcher); - if (this.servletPath != null) { - matcher.setServletPath(this.servletPath); - } - return matcher; - } - - @Override - public MvcRequestMatcher matcher(HttpMethod method, String pattern) { - MvcRequestMatcher matcher = new MvcRequestMatcher(this.introspector, pattern); - this.objectPostProcessor.postProcess(matcher); - matcher.setMethod(method); - if (this.servletPath != null) { - matcher.setServletPath(this.servletPath); - } - return matcher; - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/RequestMatcherBuilder.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/RequestMatcherBuilder.java deleted file mode 100644 index 6b95bf8111..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/RequestMatcherBuilder.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers; - -import java.util.ArrayList; -import java.util.List; - -import org.springframework.http.HttpMethod; -import org.springframework.security.web.util.matcher.AnyRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher; - -/** - * An interface that abstracts how matchers are created - * - * @author Josh Cummings - * @since 6.2 - */ -interface RequestMatcherBuilder { - - /** - * Create a request matcher for the given pattern. - * - *

- * For example, you might do something like the following: - * builder.matcher("/controller/**") - * - * @param pattern the pattern to use, typically an Ant path - * @return a {@link RequestMatcher} that matches on the given {@code pattern} - */ - RequestMatcher matcher(String pattern); - - /** - * Create a request matcher for the given pattern. - * - *

- * For example, you might do something like the following: - * builder.matcher(HttpMethod.GET, "/controller/**") - * - * @param method the HTTP method to use - * @param pattern the pattern to use, typically an Ant path - * @return a {@link RequestMatcher} that matches on the given HTTP {@code method} and - * {@code pattern} - */ - RequestMatcher matcher(HttpMethod method, String pattern); - - /** - * Create a request matcher that matches any request - * @return a {@link RequestMatcher} that matches any request - */ - default RequestMatcher any() { - return AnyRequestMatcher.INSTANCE; - } - - /** - * Create an array request matchers, one for each of the given patterns. - * - *

- * For example, you might do something like the following: - * builder.matcher("/controller-one/**", "/controller-two/**") - * - * @param patterns the patterns to use, typically Ant paths - * @return a list of {@link RequestMatcher} that match on the given {@code pattern} - */ - default List matchers(String... patterns) { - List matchers = new ArrayList<>(); - for (String pattern : patterns) { - matchers.add(matcher(pattern)); - } - return matchers; - } - - /** - * Create an array request matchers, one for each of the given patterns. - * - *

- * For example, you might do something like the following: - * builder.matcher(HttpMethod.POST, "/controller-one/**", "/controller-two/**") - * - * @param method the HTTP method to use - * @param patterns the patterns to use, typically Ant paths - * @return a list of {@link RequestMatcher} that match on the given HTTP - * {@code method} and {@code pattern} - */ - default List matchers(HttpMethod method, String... patterns) { - List matchers = new ArrayList<>(); - for (String pattern : patterns) { - matchers.add(matcher(method, pattern)); - } - return matchers; - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/RequestMatcherBuilders.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/RequestMatcherBuilders.java deleted file mode 100644 index f34793320e..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/RequestMatcherBuilders.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.context.ApplicationContext; -import org.springframework.http.HttpMethod; -import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.web.servlet.DispatcherServlet; - -/** - * A factory for constructing {@link RequestMatcherBuilder} instances - * - * @author Josh Cummings - * @since 6.2 - */ -final class RequestMatcherBuilders { - - private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector"; - - private static final String HANDLER_MAPPING_INTROSPECTOR = "org.springframework.web.servlet.handler.HandlerMappingIntrospector"; - - private static final boolean mvcPresent; - - static { - mvcPresent = ClassUtils.isPresent(HANDLER_MAPPING_INTROSPECTOR, RequestMatcherBuilders.class.getClassLoader()); - } - - private static final Log logger = LogFactory.getLog(RequestMatcherBuilders.class); - - private RequestMatcherBuilders() { - - } - - /** - * Create the default {@link RequestMatcherBuilder} for use by Spring Security DSLs. - * - *

- * If Spring MVC is not present on the classpath or if there is no - * {@link DispatcherServlet}, this method will return an Ant-based builder. - * - *

- * If the servlet configuration has only {@link DispatcherServlet} with a single - * mapping (for example `/` or `/path/*`), then this method will return an MVC-based - * builder. - * - *

- * If the servlet configuration maps {@link DispatcherServlet} to a path and also has - * other servlets, this will throw an exception. In that case, an application should - * instead use the {@link RequestMatcherBuilders#createForServletPattern} ideally with - * the associated - * {@link org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer} - * to create builders by servlet path. - * - *

- * Otherwise, (namely if {@link DispatcherServlet} is root), this method will return a - * builder that delegates to an Ant or Mvc builder at runtime. - * @param context the application context - * @return the appropriate {@link RequestMatcherBuilder} based on application - * configuration - */ - static RequestMatcherBuilder createDefault(ApplicationContext context) { - if (!mvcPresent) { - logger.trace("Defaulting to Ant matching since Spring MVC is not on the classpath"); - return AntPathRequestMatcherBuilder.absolute(); - } - if (!context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) { - logger.trace("Defaulting to Ant matching since Spring MVC is not fully configured"); - return AntPathRequestMatcherBuilder.absolute(); - } - ServletRegistrationCollection registrations = ServletRegistrationCollection.registrations(context); - if (registrations.isEmpty()) { - logger.trace("Defaulting to MVC matching since Spring MVC is on the class path and no servlet " - + "information is available"); - return AntPathRequestMatcherBuilder.absolute(); - } - ServletRegistrationCollection dispatcherServlets = registrations.dispatcherServlets(); - if (dispatcherServlets.isEmpty()) { - logger.trace("Defaulting to Ant matching since there is no DispatcherServlet configured"); - return AntPathRequestMatcherBuilder.absolute(); - } - ServletRegistrationCollection.ServletPath servletPath = registrations.deduceOneServletPath(); - if (servletPath != null) { - String message = "Defaulting to MVC matching since DispatcherServlet [%s] is the only servlet mapping"; - logger.trace(String.format(message, servletPath.path())); - return MvcRequestMatcherBuilder.relativeTo(context, servletPath.path()); - } - servletPath = dispatcherServlets.deduceOneServletPath(); - if (servletPath == null) { - logger.trace("Did not choose a default since there is more than one DispatcherServlet mapping"); - String message = String.format(""" - This method cannot decide whether these patterns are Spring MVC patterns or not - since your servlet configuration has multiple Spring MVC servlet mappings. - - For your reference, here is your servlet configuration: %s - - To address this, you need to specify the servlet path for each endpoint. - You can use .forServletPattern in conjunction with requestMatchers do to this - like so: - - @Bean - SecurityFilterChain appSecurity(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests((authorize) -> authorize - .forServletPattern("/mvc-one/*", (one) -> one - .requestMatchers("/controller/**", "/endpoints/**" - )... - .forServletPattern("/mvc-two/*", (two) -> two - .requestMatchers("/other/**", "/controllers/**")... - ) - .forServletPattern("/h2-console/*", (h2) -> h2 - .requestMatchers("/**")... - ) - ) - // ... - return http.build(); - } - """, registrations); - return new ErrorRequestMatcherBuilder(message); - } - if (servletPath.path() != null) { - logger.trace("Did not choose a default since there is a non-root DispatcherServlet mapping"); - String message = String.format(""" - This method cannot decide whether these patterns are Spring MVC patterns or not - since your Spring MVC mapping is mapped to a path and you have other servlet mappings. - - For your reference, here is your servlet configuration: %s - - To address this, you need to specify the servlet path for each endpoint. - You can use .forServletPattern in conjunction with requestMatchers do to this - like so: - - @Bean - SecurityFilterChain appSecurity(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests((authorize) -> authorize - .forServletPattern("/mvc/*", (mvc) -> mvc - .requestMatchers("/controller/**", "/endpoints/**")... - ) - .forServletPattern("/h2-console/*", (h2) -> h2 - .requestMatchers("/**")... - ) - ) - // ... - return http.build(); - } - """, registrations); - return new ErrorRequestMatcherBuilder(message); - } - logger.trace("Defaulting to request-time checker since DispatcherServlet is mapped to root, but there are also " - + "other servlet mappings"); - return new DispatcherServletDelegatingRequestMatcherBuilder(MvcRequestMatcherBuilder.absolute(context), - AntPathRequestMatcherBuilder.absolute(), registrations); - } - - static RequestMatcherBuilder createForServletPattern(ApplicationContext context, String pattern) { - Assert.notNull(pattern, "pattern cannot be null"); - ServletRegistrationCollection registrations = ServletRegistrationCollection.registrations(context); - ServletRegistrationCollection.Registration registration = registrations.registrationByMapping(pattern); - Assert.notNull(registration, () -> String - .format("The given pattern %s doesn't seem to match any configured servlets: %s", pattern, registrations)); - boolean isPathPattern = pattern.startsWith("/") && pattern.endsWith("/*"); - if (isPathPattern) { - String path = pattern.substring(0, pattern.length() - 2); - return (registration.isDispatcherServlet()) ? MvcRequestMatcherBuilder.relativeTo(context, path) - : AntPathRequestMatcherBuilder.relativeTo(path); - } - return (registration.isDispatcherServlet()) ? MvcRequestMatcherBuilder.absolute(context) - : AntPathRequestMatcherBuilder.absolute(); - } - - private static class ErrorRequestMatcherBuilder implements RequestMatcherBuilder { - - private final String errorMessage; - - ErrorRequestMatcherBuilder(String errorMessage) { - this.errorMessage = errorMessage; - } - - @Override - public RequestMatcher matcher(String pattern) { - throw new IllegalArgumentException(this.errorMessage); - } - - @Override - public RequestMatcher matcher(HttpMethod method, String pattern) { - throw new IllegalArgumentException(this.errorMessage); - } - - @Override - public RequestMatcher any() { - throw new IllegalArgumentException(this.errorMessage); - } - - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/ServletPatternRequestMatcher.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/ServletPatternRequestMatcher.java deleted file mode 100644 index 989429a050..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/ServletPatternRequestMatcher.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers; - -import jakarta.servlet.http.HttpServletRequest; - -import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.util.Assert; - -final class ServletPatternRequestMatcher implements RequestMatcher { - - final String pattern; - - ServletPatternRequestMatcher(String pattern) { - Assert.notNull(pattern, "pattern cannot be null"); - this.pattern = pattern; - } - - @Override - public boolean matches(HttpServletRequest request) { - return this.pattern.equals(request.getHttpServletMapping().getPattern()); - } - - @Override - public String toString() { - return String.format("ServletPattern [pattern='%s']", this.pattern); - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/ServletRegistrationCollection.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/ServletRegistrationCollection.java deleted file mode 100644 index 560050c0ca..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/ServletRegistrationCollection.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import jakarta.servlet.ServletContext; -import jakarta.servlet.ServletRegistration; - -import org.springframework.context.ApplicationContext; -import org.springframework.util.ClassUtils; -import org.springframework.util.CollectionUtils; -import org.springframework.web.context.WebApplicationContext; - -final class ServletRegistrationCollection { - - private List registrations; - - private ServletRegistrationCollection() { - this.registrations = Collections.emptyList(); - } - - private ServletRegistrationCollection(List registrations) { - this.registrations = registrations; - } - - static ServletRegistrationCollection registrations(ApplicationContext context) { - if (!(context instanceof WebApplicationContext web)) { - return new ServletRegistrationCollection(); - } - ServletContext servletContext = web.getServletContext(); - if (servletContext == null) { - return new ServletRegistrationCollection(); - } - Map registrations = servletContext.getServletRegistrations(); - if (registrations == null) { - return new ServletRegistrationCollection(); - } - List filtered = new ArrayList<>(); - for (ServletRegistration registration : registrations.values()) { - Collection mappings = registration.getMappings(); - if (!CollectionUtils.isEmpty(mappings)) { - filtered.add(new Registration(registration)); - } - } - return new ServletRegistrationCollection(filtered); - } - - boolean isEmpty() { - return this.registrations.isEmpty(); - } - - Registration registrationByName(String name) { - for (Registration registration : this.registrations) { - if (registration.registration().getName().equals(name)) { - return registration; - } - } - return null; - } - - Registration registrationByMapping(String target) { - for (Registration registration : this.registrations) { - for (String mapping : registration.registration().getMappings()) { - if (target.equals(mapping)) { - return registration; - } - } - } - return null; - } - - ServletRegistrationCollection dispatcherServlets() { - List dispatcherServlets = new ArrayList<>(); - for (Registration registration : this.registrations) { - if (registration.isDispatcherServlet()) { - dispatcherServlets.add(registration); - } - } - return new ServletRegistrationCollection(dispatcherServlets); - } - - ServletPath deduceOneServletPath() { - if (this.registrations.size() > 1) { - return null; - } - ServletRegistration registration = this.registrations.iterator().next().registration(); - if (registration.getMappings().size() > 1) { - return null; - } - String mapping = registration.getMappings().iterator().next(); - if ("/".equals(mapping)) { - return new ServletPath(); - } - if (mapping.endsWith("/*")) { - return new ServletPath(mapping.substring(0, mapping.length() - 2)); - } - return null; - } - - @Override - public String toString() { - Map> mappings = new LinkedHashMap<>(); - for (Registration registration : this.registrations) { - mappings.put(registration.registration().getClassName(), registration.registration().getMappings()); - } - return mappings.toString(); - } - - record Registration(ServletRegistration registration) { - boolean isDispatcherServlet() { - Class dispatcherServlet = ClassUtils - .resolveClassName("org.springframework.web.servlet.DispatcherServlet", null); - try { - Class clazz = Class.forName(this.registration.getClassName()); - if (dispatcherServlet.isAssignableFrom(clazz)) { - return true; - } - } - catch (ClassNotFoundException ex) { - return false; - } - return false; - } - } - - record ServletPath(String path) { - ServletPath() { - this(null); - } - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.java index 504d68262c..1c89d0063f 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurer.java @@ -296,7 +296,7 @@ public final class SessionManagementConfigurer> * @param sessionAuthenticationStrategy * @return the {@link SessionManagementConfigurer} for further customizations */ - public SessionManagementConfigurer addSessionAuthenticationStrategy( + SessionManagementConfigurer addSessionAuthenticationStrategy( SessionAuthenticationStrategy sessionAuthenticationStrategy) { this.sessionAuthenticationStrategies.add(sessionAuthenticationStrategy); return this; diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/DefaultOidcLogoutTokenValidatorFactory.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/DefaultOidcLogoutTokenValidatorFactory.java deleted file mode 100644 index fa7fe8e746..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/DefaultOidcLogoutTokenValidatorFactory.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers.oauth2.client; - -import java.util.function.Function; - -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator; -import org.springframework.security.oauth2.core.OAuth2TokenValidator; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.security.oauth2.jwt.JwtTimestampValidator; - -final class DefaultOidcLogoutTokenValidatorFactory implements Function> { - - @Override - public OAuth2TokenValidator apply(ClientRegistration clientRegistration) { - return new DelegatingOAuth2TokenValidator<>(new JwtTimestampValidator(), - new OidcBackChannelLogoutTokenValidator(clientRegistration)); - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2ClientConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2ClientConfigurer.java index 24b24909c9..77d11ed02c 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2ClientConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2ClientConfigurer.java @@ -16,8 +16,6 @@ package org.springframework.security.config.annotation.web.configurers.oauth2.client; -import org.springframework.context.ApplicationContext; -import org.springframework.core.ResolvableType; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.HttpSecurityBuilder; @@ -309,22 +307,7 @@ public final class OAuth2ClientConfigurer> if (this.accessTokenResponseClient != null) { return this.accessTokenResponseClient; } - ResolvableType resolvableType = ResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class, - OAuth2AuthorizationCodeGrantRequest.class); - OAuth2AccessTokenResponseClient bean = getBeanOrNull(resolvableType); - return (bean != null) ? bean : new DefaultAuthorizationCodeTokenResponseClient(); - } - - @SuppressWarnings("unchecked") - private T getBeanOrNull(ResolvableType type) { - ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class); - if (context != null) { - String[] names = context.getBeanNamesForType(type); - if (names.length == 1) { - return (T) context.getBean(names[0]); - } - } - return null; + return new DefaultAuthorizationCodeTokenResponseClient(); } } diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2ClientConfigurerUtils.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2ClientConfigurerUtils.java index 184042f823..bbf7fe7ef2 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2ClientConfigurerUtils.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2ClientConfigurerUtils.java @@ -25,8 +25,6 @@ import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; -import org.springframework.security.oauth2.client.oidc.session.InMemoryOidcSessionRegistry; -import org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.web.AuthenticatedPrincipalOAuth2AuthorizedClientRepository; import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; @@ -114,13 +112,4 @@ final class OAuth2ClientConfigurerUtils { return (!authorizedClientServiceMap.isEmpty() ? authorizedClientServiceMap.values().iterator().next() : null); } - static > OidcSessionRegistry getOidcSessionRegistry(B builder) { - OidcSessionRegistry sessionRegistry = builder.getSharedObject(OidcSessionRegistry.class); - if (sessionRegistry == null) { - sessionRegistry = new InMemoryOidcSessionRegistry(); - builder.setSharedObject(OidcSessionRegistry.class, sessionRegistry); - } - return sessionRegistry; - } - } diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java index 8f479814a9..250bb0f6bc 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java @@ -22,18 +22,9 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.servlet.http.HttpSession; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.NoUniqueBeanDefinitionException; import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationListener; -import org.springframework.context.event.GenericApplicationListenerAdapter; -import org.springframework.context.event.SmartApplicationListener; import org.springframework.core.ResolvableType; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.config.Customizer; @@ -41,14 +32,9 @@ import org.springframework.security.config.annotation.web.HttpSecurityBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; -import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer; -import org.springframework.security.context.DelegatingApplicationListener; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; -import org.springframework.security.core.session.AbstractSessionEvent; -import org.springframework.security.core.session.SessionDestroyedEvent; -import org.springframework.security.core.session.SessionIdChangedEvent; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationProvider; import org.springframework.security.oauth2.client.authentication.OAuth2LoginAuthenticationToken; @@ -56,9 +42,6 @@ import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationC import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; import org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider; -import org.springframework.security.oauth2.client.oidc.session.InMemoryOidcSessionRegistry; -import org.springframework.security.oauth2.client.oidc.session.OidcSessionInformation; -import org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService; import org.springframework.security.oauth2.client.registration.ClientRegistration; @@ -84,10 +67,7 @@ import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.RedirectStrategy; import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; -import org.springframework.security.web.authentication.session.SessionAuthenticationException; -import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter; -import org.springframework.security.web.csrf.CsrfToken; import org.springframework.security.web.savedrequest.RequestCache; import org.springframework.security.web.util.matcher.AndRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; @@ -144,7 +124,6 @@ import org.springframework.util.ReflectionUtils; *

  • {@link DefaultLoginPageGeneratingFilter} - if {@link #loginPage(String)} is not * configured and {@code DefaultLoginPageGeneratingFilter} is available, then a default * login page will be made available
  • - *
  • {@link OidcSessionRegistry}
  • * * * @author Joe Grandja @@ -223,18 +202,6 @@ public final class OAuth2LoginConfigurer> return this; } - /** - * Sets the registry for managing the OIDC client-provider session link - * @param oidcSessionRegistry the {@link OidcSessionRegistry} to use - * @return the {@link OAuth2LoginConfigurer} for further configuration - * @since 6.2 - */ - public OAuth2LoginConfigurer oidcSessionRegistry(OidcSessionRegistry oidcSessionRegistry) { - Assert.notNull(oidcSessionRegistry, "oidcSessionRegistry cannot be null"); - getBuilder().setSharedObject(OidcSessionRegistry.class, oidcSessionRegistry); - return this; - } - /** * Returns the {@link AuthorizationEndpointConfig} for configuring the Authorization * Server's Authorization Endpoint. @@ -363,7 +330,10 @@ public final class OAuth2LoginConfigurer> super.init(http); } } - OAuth2AccessTokenResponseClient accessTokenResponseClient = getAccessTokenResponseClient(); + OAuth2AccessTokenResponseClient accessTokenResponseClient = this.tokenEndpointConfig.accessTokenResponseClient; + if (accessTokenResponseClient == null) { + accessTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient(); + } OAuth2UserService oauth2UserService = getOAuth2UserService(); OAuth2LoginAuthenticationProvider oauth2LoginAuthenticationProvider = new OAuth2LoginAuthenticationProvider( accessTokenResponseClient, oauth2UserService); @@ -430,7 +400,6 @@ public final class OAuth2LoginConfigurer> authenticationFilter .setAuthorizationRequestRepository(this.authorizationEndpointConfig.authorizationRequestRepository); } - configureOidcSessionRegistry(http); super.configure(http); } @@ -473,16 +442,6 @@ public final class OAuth2LoginConfigurer> return (!grantedAuthoritiesMapperMap.isEmpty() ? grantedAuthoritiesMapperMap.values().iterator().next() : null); } - private OAuth2AccessTokenResponseClient getAccessTokenResponseClient() { - if (this.tokenEndpointConfig.accessTokenResponseClient != null) { - return this.tokenEndpointConfig.accessTokenResponseClient; - } - ResolvableType resolvableType = ResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class, - OAuth2AuthorizationCodeGrantRequest.class); - OAuth2AccessTokenResponseClient bean = getBeanOrNull(resolvableType); - return (bean != null) ? bean : new DefaultAuthorizationCodeTokenResponseClient(); - } - private OAuth2UserService getOidcUserService() { if (this.userInfoEndpointConfig.oidcUserService != null) { return this.userInfoEndpointConfig.oidcUserService; @@ -581,29 +540,6 @@ public final class OAuth2LoginConfigurer> return AnyRequestMatcher.INSTANCE; } - private void configureOidcSessionRegistry(B http) { - OidcSessionRegistry sessionRegistry = OAuth2ClientConfigurerUtils.getOidcSessionRegistry(http); - SessionManagementConfigurer sessionConfigurer = http.getConfigurer(SessionManagementConfigurer.class); - if (sessionConfigurer != null) { - OidcSessionRegistryAuthenticationStrategy sessionAuthenticationStrategy = new OidcSessionRegistryAuthenticationStrategy(); - sessionAuthenticationStrategy.setSessionRegistry(sessionRegistry); - sessionConfigurer.addSessionAuthenticationStrategy(sessionAuthenticationStrategy); - } - OidcClientSessionEventListener listener = new OidcClientSessionEventListener(); - listener.setSessionRegistry(sessionRegistry); - registerDelegateApplicationListener(listener); - } - - private void registerDelegateApplicationListener(ApplicationListener delegate) { - DelegatingApplicationListener delegating = getBeanOrNull( - ResolvableType.forType(DelegatingApplicationListener.class)); - if (delegating == null) { - return; - } - SmartApplicationListener smartListener = new GenericApplicationListenerAdapter(delegate); - delegating.addListener(smartListener); - } - /** * Configuration options for the Authorization Server's Authorization Endpoint. */ @@ -851,88 +787,4 @@ public final class OAuth2LoginConfigurer> } - private static final class OidcClientSessionEventListener implements ApplicationListener { - - private final Log logger = LogFactory.getLog(OidcClientSessionEventListener.class); - - private OidcSessionRegistry sessionRegistry = new InMemoryOidcSessionRegistry(); - - /** - * {@inheritDoc} - */ - @Override - public void onApplicationEvent(AbstractSessionEvent event) { - if (event instanceof SessionDestroyedEvent destroyed) { - this.logger.debug("Received SessionDestroyedEvent"); - this.sessionRegistry.removeSessionInformation(destroyed.getId()); - return; - } - if (event instanceof SessionIdChangedEvent changed) { - this.logger.debug("Received SessionIdChangedEvent"); - OidcSessionInformation information = this.sessionRegistry - .removeSessionInformation(changed.getOldSessionId()); - if (information == null) { - this.logger - .debug("Failed to register new session id since old session id was not found in registry"); - return; - } - this.sessionRegistry.saveSessionInformation(information.withSessionId(changed.getNewSessionId())); - } - } - - /** - * The registry where OIDC Provider sessions are linked to the Client session. - * Defaults to in-memory storage. - * @param sessionRegistry the {@link OidcSessionRegistry} to use - */ - void setSessionRegistry(OidcSessionRegistry sessionRegistry) { - Assert.notNull(sessionRegistry, "sessionRegistry cannot be null"); - this.sessionRegistry = sessionRegistry; - } - - } - - private static final class OidcSessionRegistryAuthenticationStrategy implements SessionAuthenticationStrategy { - - private final Log logger = LogFactory.getLog(getClass()); - - private OidcSessionRegistry sessionRegistry = new InMemoryOidcSessionRegistry(); - - /** - * {@inheritDoc} - */ - @Override - public void onAuthentication(Authentication authentication, HttpServletRequest request, - HttpServletResponse response) throws SessionAuthenticationException { - HttpSession session = request.getSession(false); - if (session == null) { - return; - } - if (!(authentication.getPrincipal() instanceof OidcUser user)) { - return; - } - String sessionId = session.getId(); - CsrfToken csrfToken = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); - Map headers = (csrfToken != null) ? Map.of(csrfToken.getHeaderName(), csrfToken.getToken()) - : Collections.emptyMap(); - OidcSessionInformation registration = new OidcSessionInformation(sessionId, headers, user); - if (this.logger.isTraceEnabled()) { - this.logger - .trace(String.format("Linking a provider [%s] session to this client's session", user.getIssuer())); - } - this.sessionRegistry.saveSessionInformation(registration); - } - - /** - * The registration for linking OIDC Provider Session information to the Client's - * session. Defaults to in-memory storage. - * @param sessionRegistry the {@link OidcSessionRegistry} to use - */ - void setSessionRegistry(OidcSessionRegistry sessionRegistry) { - Assert.notNull(sessionRegistry, "sessionRegistry cannot be null"); - this.sessionRegistry = sessionRegistry; - } - - } - } diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutAuthentication.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutAuthentication.java deleted file mode 100644 index f65b1c11c0..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutAuthentication.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers.oauth2.client; - -import java.util.Collections; - -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken; - -/** - * An {@link org.springframework.security.core.Authentication} implementation that - * represents the result of authenticating an OIDC Logout token for the purposes of - * performing Back-Channel Logout. - * - * @author Josh Cummings - * @since 6.2 - * @see OidcLogoutAuthenticationToken - * @see OIDC Back-Channel - * Logout - */ -class OidcBackChannelLogoutAuthentication extends AbstractAuthenticationToken { - - private final OidcLogoutToken logoutToken; - - /** - * Construct an {@link OidcBackChannelLogoutAuthentication} - * @param logoutToken a deserialized, verified OIDC Logout Token - */ - OidcBackChannelLogoutAuthentication(OidcLogoutToken logoutToken) { - super(Collections.emptyList()); - this.logoutToken = logoutToken; - setAuthenticated(true); - } - - /** - * {@inheritDoc} - */ - @Override - public OidcLogoutToken getPrincipal() { - return this.logoutToken; - } - - /** - * {@inheritDoc} - */ - @Override - public OidcLogoutToken getCredentials() { - return this.logoutToken; - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutAuthenticationProvider.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutAuthenticationProvider.java deleted file mode 100644 index d8a217f263..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutAuthenticationProvider.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers.oauth2.client; - -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory; -import org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.OAuth2ErrorCodes; -import org.springframework.security.oauth2.jwt.BadJwtException; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.security.oauth2.jwt.JwtDecoder; -import org.springframework.security.oauth2.jwt.JwtDecoderFactory; -import org.springframework.util.Assert; - -/** - * An {@link AuthenticationProvider} that authenticates an OIDC Logout Token; namely - * deserializing it, verifying its signature, and validating its claims. - * - *

    - * Intended to be included in a - * {@link org.springframework.security.authentication.ProviderManager} - * - * @author Josh Cummings - * @since 6.2 - * @see OidcLogoutAuthenticationToken - * @see org.springframework.security.authentication.ProviderManager - * @see OIDC Back-Channel - * Logout - */ -final class OidcBackChannelLogoutAuthenticationProvider implements AuthenticationProvider { - - private JwtDecoderFactory logoutTokenDecoderFactory; - - /** - * Construct an {@link OidcBackChannelLogoutAuthenticationProvider} - */ - OidcBackChannelLogoutAuthenticationProvider() { - OidcIdTokenDecoderFactory logoutTokenDecoderFactory = new OidcIdTokenDecoderFactory(); - logoutTokenDecoderFactory.setJwtValidatorFactory(new DefaultOidcLogoutTokenValidatorFactory()); - this.logoutTokenDecoderFactory = logoutTokenDecoderFactory; - } - - /** - * {@inheritDoc} - */ - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - if (!(authentication instanceof OidcLogoutAuthenticationToken token)) { - return null; - } - String logoutToken = token.getLogoutToken(); - ClientRegistration registration = token.getClientRegistration(); - Jwt jwt = decode(registration, logoutToken); - OidcLogoutToken oidcLogoutToken = OidcLogoutToken.withTokenValue(logoutToken) - .claims((claims) -> claims.putAll(jwt.getClaims())) - .build(); - return new OidcBackChannelLogoutAuthentication(oidcLogoutToken); - } - - /** - * {@inheritDoc} - */ - @Override - public boolean supports(Class authentication) { - return OidcLogoutAuthenticationToken.class.isAssignableFrom(authentication); - } - - private Jwt decode(ClientRegistration registration, String token) { - JwtDecoder logoutTokenDecoder = this.logoutTokenDecoderFactory.createDecoder(registration); - try { - return logoutTokenDecoder.decode(token); - } - catch (BadJwtException failed) { - OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, failed.getMessage(), - "https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation"); - throw new OAuth2AuthenticationException(error, failed); - } - catch (Exception failed) { - throw new AuthenticationServiceException(failed.getMessage(), failed); - } - } - - /** - * Use this {@link JwtDecoderFactory} to generate {@link JwtDecoder}s that correspond - * to the {@link ClientRegistration} associated with the OIDC logout token. - * @param logoutTokenDecoderFactory the {@link JwtDecoderFactory} to use - */ - void setLogoutTokenDecoderFactory(JwtDecoderFactory logoutTokenDecoderFactory) { - Assert.notNull(logoutTokenDecoderFactory, "logoutTokenDecoderFactory cannot be null"); - this.logoutTokenDecoderFactory = logoutTokenDecoderFactory; - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutFilter.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutFilter.java deleted file mode 100644 index 0a03ec8383..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutFilter.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers.oauth2.client; - -import java.io.IOException; - -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.http.server.ServletServerHttpResponse; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.OAuth2ErrorCodes; -import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter; -import org.springframework.security.web.authentication.AuthenticationConverter; -import org.springframework.security.web.authentication.logout.LogoutHandler; -import org.springframework.util.Assert; -import org.springframework.web.filter.OncePerRequestFilter; - -/** - * A filter for the Client-side OIDC Back-Channel Logout endpoint - * - * @author Josh Cummings - * @since 6.2 - * @see OIDC Back-Channel Logout - * Spec - */ -class OidcBackChannelLogoutFilter extends OncePerRequestFilter { - - private final Log logger = LogFactory.getLog(getClass()); - - private final AuthenticationConverter authenticationConverter; - - private final AuthenticationManager authenticationManager; - - private final OAuth2ErrorHttpMessageConverter errorHttpMessageConverter = new OAuth2ErrorHttpMessageConverter(); - - private LogoutHandler logoutHandler = new OidcBackChannelLogoutHandler(); - - /** - * Construct an {@link OidcBackChannelLogoutFilter} - * @param authenticationConverter the {@link AuthenticationConverter} for deriving - * Logout Token authentication - * @param authenticationManager the {@link AuthenticationManager} for authenticating - * Logout Tokens - */ - OidcBackChannelLogoutFilter(AuthenticationConverter authenticationConverter, - AuthenticationManager authenticationManager) { - Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); - Assert.notNull(authenticationManager, "authenticationManager cannot be null"); - this.authenticationConverter = authenticationConverter; - this.authenticationManager = authenticationManager; - } - - /** - * {@inheritDoc} - */ - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) - throws ServletException, IOException { - Authentication token; - try { - token = this.authenticationConverter.convert(request); - } - catch (AuthenticationServiceException ex) { - this.logger.debug("Failed to process OIDC Back-Channel Logout", ex); - throw ex; - } - catch (AuthenticationException ex) { - handleAuthenticationFailure(response, ex); - return; - } - if (token == null) { - chain.doFilter(request, response); - return; - } - Authentication authentication; - try { - authentication = this.authenticationManager.authenticate(token); - } - catch (AuthenticationServiceException ex) { - this.logger.debug("Failed to process OIDC Back-Channel Logout", ex); - throw ex; - } - catch (AuthenticationException ex) { - handleAuthenticationFailure(response, ex); - return; - } - this.logoutHandler.logout(request, response, authentication); - } - - private void handleAuthenticationFailure(HttpServletResponse response, Exception ex) throws IOException { - this.logger.debug("Failed to process OIDC Back-Channel Logout", ex); - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - this.errorHttpMessageConverter.write(oauth2Error(ex), null, new ServletServerHttpResponse(response)); - } - - private OAuth2Error oauth2Error(Exception ex) { - if (ex instanceof OAuth2AuthenticationException oauth2) { - return oauth2.getError(); - } - return new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, ex.getMessage(), - "https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation"); - } - - /** - * The strategy for expiring all Client sessions indicated by the logout request. - * Defaults to {@link OidcBackChannelLogoutHandler}. - * @param logoutHandler the {@link LogoutHandler} to use - */ - void setLogoutHandler(LogoutHandler logoutHandler) { - Assert.notNull(logoutHandler, "logoutHandler cannot be null"); - this.logoutHandler = logoutHandler; - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutHandler.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutHandler.java deleted file mode 100644 index 21f6a9d60b..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutHandler.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers.oauth2.client; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.server.ServletServerHttpResponse; -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken; -import org.springframework.security.oauth2.client.oidc.session.InMemoryOidcSessionRegistry; -import org.springframework.security.oauth2.client.oidc.session.OidcSessionInformation; -import org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.http.converter.OAuth2ErrorHttpMessageConverter; -import org.springframework.security.web.authentication.logout.LogoutHandler; -import org.springframework.util.Assert; -import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestOperations; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - -/** - * A {@link LogoutHandler} that locates the sessions associated with a given OIDC - * Back-Channel Logout Token and invalidates each one. - * - * @author Josh Cummings - * @since 6.2 - * @see OIDC Back-Channel Logout - * Spec - */ -final class OidcBackChannelLogoutHandler implements LogoutHandler { - - private final Log logger = LogFactory.getLog(getClass()); - - private OidcSessionRegistry sessionRegistry = new InMemoryOidcSessionRegistry(); - - private RestOperations restOperations = new RestTemplate(); - - private String logoutEndpointName = "/logout"; - - private String sessionCookieName = "JSESSIONID"; - - private final OAuth2ErrorHttpMessageConverter errorHttpMessageConverter = new OAuth2ErrorHttpMessageConverter(); - - @Override - public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { - if (!(authentication instanceof OidcBackChannelLogoutAuthentication token)) { - if (this.logger.isDebugEnabled()) { - String message = "Did not perform OIDC Back-Channel Logout since authentication [%s] was of the wrong type"; - this.logger.debug(String.format(message, authentication.getClass().getSimpleName())); - } - return; - } - Iterable sessions = this.sessionRegistry.removeSessionInformation(token.getPrincipal()); - Collection errors = new ArrayList<>(); - int totalCount = 0; - int invalidatedCount = 0; - for (OidcSessionInformation session : sessions) { - totalCount++; - try { - eachLogout(request, session); - invalidatedCount++; - } - catch (RestClientException ex) { - this.logger.debug("Failed to invalidate session", ex); - errors.add(ex.getMessage()); - this.sessionRegistry.saveSessionInformation(session); - } - } - if (this.logger.isTraceEnabled()) { - this.logger.trace(String.format("Invalidated %d out of %d sessions", invalidatedCount, totalCount)); - } - if (!errors.isEmpty()) { - handleLogoutFailure(response, oauth2Error(errors)); - } - } - - private void eachLogout(HttpServletRequest request, OidcSessionInformation session) { - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.COOKIE, this.sessionCookieName + "=" + session.getSessionId()); - for (Map.Entry credential : session.getAuthorities().entrySet()) { - headers.add(credential.getKey(), credential.getValue()); - } - String url = request.getRequestURL().toString(); - String logout = UriComponentsBuilder.fromHttpUrl(url) - .replacePath(this.logoutEndpointName) - .build() - .toUriString(); - HttpEntity entity = new HttpEntity<>(null, headers); - this.restOperations.postForEntity(logout, entity, Object.class); - } - - private OAuth2Error oauth2Error(Collection errors) { - return new OAuth2Error("partial_logout", "not all sessions were terminated: " + errors, - "https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation"); - } - - private void handleLogoutFailure(HttpServletResponse response, OAuth2Error error) { - response.setStatus(HttpServletResponse.SC_BAD_REQUEST); - try { - this.errorHttpMessageConverter.write(error, null, new ServletServerHttpResponse(response)); - } - catch (IOException ex) { - throw new IllegalStateException(ex); - } - } - - /** - * Use this {@link OidcSessionRegistry} to identify sessions to invalidate. Note that - * this class uses - * {@link OidcSessionRegistry#removeSessionInformation(OidcLogoutToken)} to identify - * sessions. - * @param sessionRegistry the {@link OidcSessionRegistry} to use - */ - void setSessionRegistry(OidcSessionRegistry sessionRegistry) { - Assert.notNull(sessionRegistry, "sessionRegistry cannot be null"); - this.sessionRegistry = sessionRegistry; - } - - /** - * Use this {@link RestOperations} to perform the per-session back-channel logout - * @param restOperations the {@link RestOperations} to use - */ - void setRestOperations(RestOperations restOperations) { - Assert.notNull(restOperations, "restOperations cannot be null"); - this.restOperations = restOperations; - } - - /** - * Use this logout URI for performing per-session logout. Defaults to {@code /logout} - * since that is the default URI for - * {@link org.springframework.security.web.authentication.logout.LogoutFilter}. - * @param logoutUri the URI to use - */ - void setLogoutUri(String logoutUri) { - Assert.hasText(logoutUri, "logoutUri cannot be empty"); - this.logoutEndpointName = logoutUri; - } - - /** - * Use this cookie name for the session identifier. Defaults to {@code JSESSIONID}. - * - *

    - * Note that if you are using Spring Session, this likely needs to change to SESSION. - * @param sessionCookieName the cookie name to use - */ - void setSessionCookieName(String sessionCookieName) { - Assert.hasText(sessionCookieName, "clientSessionCookieName cannot be empty"); - this.sessionCookieName = sessionCookieName; - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutTokenValidator.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutTokenValidator.java deleted file mode 100644 index 7b6634f933..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcBackChannelLogoutTokenValidator.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers.oauth2.client; - -import java.time.Instant; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import org.springframework.security.oauth2.client.oidc.authentication.logout.LogoutTokenClaimAccessor; -import org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.OAuth2ErrorCodes; -import org.springframework.security.oauth2.core.OAuth2TokenValidator; -import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult; -import org.springframework.security.oauth2.jwt.Jwt; - -/** - * A {@link OAuth2TokenValidator} that validates OIDC Logout Token claims in conformance - * with the OIDC Back-Channel Logout Spec. - * - * @author Josh Cummings - * @since 6.2 - * @see OidcLogoutToken - * @see Logout - * Token - * @see the OIDC - * Back-Channel Logout spec - */ -final class OidcBackChannelLogoutTokenValidator implements OAuth2TokenValidator { - - private static final String LOGOUT_VALIDATION_URL = "https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation"; - - private static final String BACK_CHANNEL_LOGOUT_EVENT = "http://schemas.openid.net/event/backchannel-logout"; - - private final String audience; - - private final String issuer; - - OidcBackChannelLogoutTokenValidator(ClientRegistration clientRegistration) { - this.audience = clientRegistration.getClientId(); - this.issuer = clientRegistration.getProviderDetails().getIssuerUri(); - } - - @Override - public OAuth2TokenValidatorResult validate(Jwt jwt) { - Collection errors = new ArrayList<>(); - - LogoutTokenClaimAccessor logoutClaims = jwt::getClaims; - Map events = logoutClaims.getEvents(); - if (events == null) { - errors.add(invalidLogoutToken("events claim must not be null")); - } - else if (events.get(BACK_CHANNEL_LOGOUT_EVENT) == null) { - errors.add(invalidLogoutToken("events claim map must contain \"" + BACK_CHANNEL_LOGOUT_EVENT + "\" key")); - } - - String issuer = logoutClaims.getIssuer().toExternalForm(); - if (issuer == null) { - errors.add(invalidLogoutToken("iss claim must not be null")); - } - else if (!this.issuer.equals(issuer)) { - errors.add(invalidLogoutToken( - "iss claim value must match `ClientRegistration#getProviderDetails#getIssuerUri`")); - } - - List audience = logoutClaims.getAudience(); - if (audience == null) { - errors.add(invalidLogoutToken("aud claim must not be null")); - } - else if (!audience.contains(this.audience)) { - errors.add(invalidLogoutToken("aud claim value must include `ClientRegistration#getClientId`")); - } - - Instant issuedAt = logoutClaims.getIssuedAt(); - if (issuedAt == null) { - errors.add(invalidLogoutToken("iat claim must not be null")); - } - - String jwtId = logoutClaims.getId(); - if (jwtId == null) { - errors.add(invalidLogoutToken("jti claim must not be null")); - } - - if (logoutClaims.getSubject() == null && logoutClaims.getSessionId() == null) { - errors.add(invalidLogoutToken("sub and sid claims must not both be null")); - } - - if (logoutClaims.getClaim("nonce") != null) { - errors.add(invalidLogoutToken("nonce claim must not be present")); - } - - return OAuth2TokenValidatorResult.failure(errors); - } - - private static OAuth2Error invalidLogoutToken(String description) { - return new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, description, LOGOUT_VALIDATION_URL); - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutAuthenticationConverter.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutAuthenticationConverter.java deleted file mode 100644 index 2809991894..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutAuthenticationConverter.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers.oauth2.client; - -import jakarta.servlet.http.HttpServletRequest; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.OAuth2ErrorCodes; -import org.springframework.security.web.authentication.AuthenticationConverter; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.util.Assert; - -/** - * An {@link AuthenticationConverter} that extracts the OIDC Logout Token authentication - * request - * - * @author Josh Cummings - * @since 6.2 - */ -final class OidcLogoutAuthenticationConverter implements AuthenticationConverter { - - private static final String DEFAULT_LOGOUT_URI = "/logout/connect/back-channel/{registrationId}"; - - private final Log logger = LogFactory.getLog(getClass()); - - private final ClientRegistrationRepository clientRegistrationRepository; - - private RequestMatcher requestMatcher = new AntPathRequestMatcher(DEFAULT_LOGOUT_URI, "POST"); - - OidcLogoutAuthenticationConverter(ClientRegistrationRepository clientRegistrationRepository) { - Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null"); - this.clientRegistrationRepository = clientRegistrationRepository; - } - - @Override - public Authentication convert(HttpServletRequest request) { - RequestMatcher.MatchResult result = this.requestMatcher.matcher(request); - if (!result.isMatch()) { - return null; - } - String registrationId = result.getVariables().get("registrationId"); - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId); - if (clientRegistration == null) { - this.logger.debug("Did not process OIDC Back-Channel Logout since no ClientRegistration was found"); - throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST); - } - String logoutToken = request.getParameter("logout_token"); - if (logoutToken == null) { - this.logger.debug("Failed to process OIDC Back-Channel Logout since no logout token was found"); - throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST); - } - return new OidcLogoutAuthenticationToken(logoutToken, clientRegistration); - } - - /** - * The logout endpoint. Defaults to - * {@code /logout/connect/back-channel/{registrationId}}. - * @param requestMatcher the {@link RequestMatcher} to use - */ - void setRequestMatcher(RequestMatcher requestMatcher) { - Assert.notNull(requestMatcher, "requestMatcher cannot be null"); - this.requestMatcher = requestMatcher; - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutAuthenticationToken.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutAuthenticationToken.java deleted file mode 100644 index 4a227e3be8..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutAuthenticationToken.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers.oauth2.client; - -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.oauth2.client.registration.ClientRegistration; - -/** - * An {@link org.springframework.security.core.Authentication} instance that represents a - * request to authenticate an OIDC Logout Token. - * - * @author Josh Cummings - * @since 6.2 - */ -class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken { - - private final String logoutToken; - - private final ClientRegistration clientRegistration; - - /** - * Construct an {@link OidcLogoutAuthenticationToken} - * @param logoutToken a signed, serialized OIDC Logout token - * @param clientRegistration the {@link ClientRegistration client} associated with - * this token; this is usually derived from material in the logout HTTP request - */ - OidcLogoutAuthenticationToken(String logoutToken, ClientRegistration clientRegistration) { - super(AuthorityUtils.NO_AUTHORITIES); - this.logoutToken = logoutToken; - this.clientRegistration = clientRegistration; - } - - /** - * {@inheritDoc} - */ - @Override - public String getCredentials() { - return this.logoutToken; - } - - /** - * {@inheritDoc} - */ - @Override - public String getPrincipal() { - return this.logoutToken; - } - - /** - * Get the signed, serialized OIDC Logout token - * @return the logout token - */ - String getLogoutToken() { - return this.logoutToken; - } - - /** - * Get the {@link ClientRegistration} associated with this logout token - * @return the {@link ClientRegistration} - */ - ClientRegistration getClientRegistration() { - return this.clientRegistration; - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutConfigurer.java deleted file mode 100644 index b22e7e5a25..0000000000 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutConfigurer.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers.oauth2.client; - -import java.util.function.Consumer; - -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.ProviderManager; -import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.web.HttpSecurityBuilder; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; -import org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; -import org.springframework.security.web.authentication.AuthenticationConverter; -import org.springframework.security.web.authentication.logout.LogoutHandler; -import org.springframework.security.web.csrf.CsrfFilter; -import org.springframework.util.Assert; - -/** - * An {@link AbstractHttpConfigurer} for OIDC Logout flows - * - *

    - * OIDC Logout provides an application with the capability to have users log out by using - * their existing account at an OAuth 2.0 or OpenID Connect 1.0 Provider. - * - * - *

    Security Filters

    - * - * The following {@code Filter} is populated: - * - *
      - *
    • {@link OidcBackChannelLogoutFilter}
    • - *
    - * - *

    Shared Objects Used

    - * - * The following shared objects are used: - * - *
      - *
    • {@link ClientRegistrationRepository}
    • - *
    - * - * @author Josh Cummings - * @since 6.2 - * @see HttpSecurity#oidcLogout() - * @see OidcBackChannelLogoutFilter - * @see ClientRegistrationRepository - */ -public final class OidcLogoutConfigurer> - extends AbstractHttpConfigurer, B> { - - private BackChannelLogoutConfigurer backChannel; - - /** - * Sets the repository of client registrations. - * @param clientRegistrationRepository the repository of client registrations - * @return the {@link OAuth2LoginConfigurer} for further configuration - */ - public OidcLogoutConfigurer clientRegistrationRepository( - ClientRegistrationRepository clientRegistrationRepository) { - Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null"); - this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository); - return this; - } - - /** - * Sets the registry for managing the OIDC client-provider session link - * @param oidcSessionRegistry the {@link OidcSessionRegistry} to use - * @return the {@link OAuth2LoginConfigurer} for further configuration - */ - public OidcLogoutConfigurer oidcSessionRegistry(OidcSessionRegistry oidcSessionRegistry) { - Assert.notNull(oidcSessionRegistry, "oidcSessionRegistry cannot be null"); - getBuilder().setSharedObject(OidcSessionRegistry.class, oidcSessionRegistry); - return this; - } - - /** - * Configure OIDC Back-Channel Logout using the provided {@link Consumer} - * @return the {@link OidcLogoutConfigurer} for further configuration - */ - public OidcLogoutConfigurer backChannel(Customizer backChannelLogoutConfigurer) { - if (this.backChannel == null) { - this.backChannel = new BackChannelLogoutConfigurer(); - } - backChannelLogoutConfigurer.customize(this.backChannel); - return this; - } - - @Deprecated(forRemoval = true, since = "6.2") - public B and() { - return getBuilder(); - } - - @Override - public void configure(B builder) throws Exception { - if (this.backChannel != null) { - this.backChannel.configure(builder); - } - } - - /** - * A configurer for configuring OIDC Back-Channel Logout - */ - public final class BackChannelLogoutConfigurer { - - private AuthenticationConverter authenticationConverter; - - private final AuthenticationManager authenticationManager = new ProviderManager( - new OidcBackChannelLogoutAuthenticationProvider()); - - private LogoutHandler logoutHandler; - - private AuthenticationConverter authenticationConverter(B http) { - if (this.authenticationConverter == null) { - ClientRegistrationRepository clientRegistrationRepository = OAuth2ClientConfigurerUtils - .getClientRegistrationRepository(http); - this.authenticationConverter = new OidcLogoutAuthenticationConverter(clientRegistrationRepository); - } - return this.authenticationConverter; - } - - private AuthenticationManager authenticationManager() { - return this.authenticationManager; - } - - private LogoutHandler logoutHandler(B http) { - if (this.logoutHandler == null) { - OidcBackChannelLogoutHandler logoutHandler = new OidcBackChannelLogoutHandler(); - logoutHandler.setSessionRegistry(OAuth2ClientConfigurerUtils.getOidcSessionRegistry(http)); - this.logoutHandler = logoutHandler; - } - return this.logoutHandler; - } - - void configure(B http) { - OidcBackChannelLogoutFilter filter = new OidcBackChannelLogoutFilter(authenticationConverter(http), - authenticationManager()); - filter.setLogoutHandler(logoutHandler(http)); - http.addFilterBefore(filter, CsrfFilter.class); - } - - } - -} diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java index 468afafad4..41455d6625 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java @@ -52,7 +52,6 @@ import org.springframework.security.web.util.matcher.NegatedRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatchers; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -115,9 +114,7 @@ public final class Saml2LoginConfigurer> private Saml2AuthenticationRequestResolver authenticationRequestResolver; - private RequestMatcher loginProcessingUrl = RequestMatchers.anyOf( - new AntPathRequestMatcher(Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI), - new AntPathRequestMatcher("/login/saml2/sso")); + private String loginProcessingUrl = Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI; private RelyingPartyRegistrationRepository relyingPartyRegistrationRepository; @@ -217,7 +214,7 @@ public final class Saml2LoginConfigurer> @Override public Saml2LoginConfigurer loginProcessingUrl(String loginProcessingUrl) { Assert.hasText(loginProcessingUrl, "loginProcessingUrl cannot be empty"); - this.loginProcessingUrl = new AntPathRequestMatcher(loginProcessingUrl); + this.loginProcessingUrl = loginProcessingUrl; return this; } @@ -243,11 +240,12 @@ public final class Saml2LoginConfigurer> public void init(B http) throws Exception { registerDefaultCsrfOverride(http); relyingPartyRegistrationRepository(http); - this.saml2WebSsoAuthenticationFilter = new Saml2WebSsoAuthenticationFilter(getAuthenticationConverter(http)); + this.saml2WebSsoAuthenticationFilter = new Saml2WebSsoAuthenticationFilter(getAuthenticationConverter(http), + this.loginProcessingUrl); this.saml2WebSsoAuthenticationFilter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy()); - this.saml2WebSsoAuthenticationFilter.setRequiresAuthenticationRequestMatcher(this.loginProcessingUrl); setAuthenticationRequestRepository(http, this.saml2WebSsoAuthenticationFilter); setAuthenticationFilter(this.saml2WebSsoAuthenticationFilter); + super.loginProcessingUrl(this.loginProcessingUrl); if (StringUtils.hasText(this.loginPage)) { // Set custom login page super.loginPage(this.loginPage); @@ -354,7 +352,7 @@ public final class Saml2LoginConfigurer> OpenSamlAuthenticationTokenConverter converter = new OpenSamlAuthenticationTokenConverter( this.relyingPartyRegistrationRepository); converter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http)); - converter.setRequestMatcher(this.loginProcessingUrl); + converter.setRequestMatcher(createLoginProcessingUrlMatcher(this.loginProcessingUrl)); return converter; } return authenticationConverterBean; @@ -372,7 +370,7 @@ public final class Saml2LoginConfigurer> if (csrf == null) { return; } - csrf.ignoringRequestMatchers(this.loginProcessingUrl); + csrf.ignoringRequestMatchers(new AntPathRequestMatcher(this.loginProcessingUrl)); } private void initDefaultLoginFilter(B http) { diff --git a/config/src/main/java/org/springframework/security/config/crypto/RsaKeyConversionServicePostProcessor.java b/config/src/main/java/org/springframework/security/config/crypto/RsaKeyConversionServicePostProcessor.java index 69905dfe5e..7b406b9546 100644 --- a/config/src/main/java/org/springframework/security/config/crypto/RsaKeyConversionServicePostProcessor.java +++ b/config/src/main/java/org/springframework/security/config/crypto/RsaKeyConversionServicePostProcessor.java @@ -68,7 +68,8 @@ public class RsaKeyConversionServicePostProcessor implements BeanFactoryPostProc return; } ConversionService service = beanFactory.getConversionService(); - if (service instanceof ConverterRegistry registry) { + if (service instanceof ConverterRegistry) { + ConverterRegistry registry = (ConverterRegistry) service; registry.addConverter(String.class, RSAPrivateKey.class, this.pkcs8); registry.addConverter(String.class, RSAPublicKey.class, this.x509); } diff --git a/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java b/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java index 6a80bafce5..3360f6f64b 100644 --- a/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java +++ b/config/src/main/java/org/springframework/security/config/http/AuthenticationConfigBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -421,8 +421,6 @@ final class AuthenticationConfigBuilder { this.pc.getReaderContext() .registerWithGeneratedName(new RootBeanDefinition(OAuth2ClientWebMvcSecurityPostProcessor.class)); } - this.pc.getReaderContext() - .registerWithGeneratedName(new RootBeanDefinition(OAuth2AuthorizedClientManagerRegistrar.class)); } private void createSaml2LoginFilter(BeanReference authenticationManager, diff --git a/config/src/main/java/org/springframework/security/config/http/ChannelAttributeFactory.java b/config/src/main/java/org/springframework/security/config/http/ChannelAttributeFactory.java index 0030a34dce..6c00919429 100644 --- a/config/src/main/java/org/springframework/security/config/http/ChannelAttributeFactory.java +++ b/config/src/main/java/org/springframework/security/config/http/ChannelAttributeFactory.java @@ -42,12 +42,19 @@ public final class ChannelAttributeFactory { } public static List createChannelAttributes(String requiredChannel) { - String channelConfigAttribute = switch (requiredChannel) { - case OPT_REQUIRES_HTTPS -> "REQUIRES_SECURE_CHANNEL"; - case OPT_REQUIRES_HTTP -> "REQUIRES_INSECURE_CHANNEL"; - case OPT_ANY_CHANNEL -> ChannelDecisionManagerImpl.ANY_CHANNEL; - default -> throw new BeanCreationException("Unknown channel attribute " + requiredChannel); - }; + String channelConfigAttribute; + if (requiredChannel.equals(OPT_REQUIRES_HTTPS)) { + channelConfigAttribute = "REQUIRES_SECURE_CHANNEL"; + } + else if (requiredChannel.equals(OPT_REQUIRES_HTTP)) { + channelConfigAttribute = "REQUIRES_INSECURE_CHANNEL"; + } + else if (requiredChannel.equals(OPT_ANY_CHANNEL)) { + channelConfigAttribute = ChannelDecisionManagerImpl.ANY_CHANNEL; + } + else { + throw new BeanCreationException("Unknown channel attribute " + requiredChannel); + } return SecurityConfig.createList(channelConfigAttribute); } diff --git a/config/src/main/java/org/springframework/security/config/http/CsrfBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/CsrfBeanDefinitionParser.java index e8cbad916d..4970bb35a9 100644 --- a/config/src/main/java/org/springframework/security/config/http/CsrfBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/http/CsrfBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ import org.springframework.security.web.csrf.CsrfAuthenticationStrategy; import org.springframework.security.web.csrf.CsrfFilter; import org.springframework.security.web.csrf.CsrfLogoutHandler; import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository; +import org.springframework.security.web.csrf.LazyCsrfTokenRepository; import org.springframework.security.web.csrf.MissingCsrfTokenException; import org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor; import org.springframework.security.web.session.InvalidSessionAccessDeniedHandler; @@ -108,12 +109,13 @@ public class CsrfBeanDefinitionParser implements BeanDefinitionParser { this.requestHandlerRef = element.getAttribute(ATT_REQUEST_HANDLER); } if (!StringUtils.hasText(this.csrfRepositoryRef)) { - BeanDefinitionBuilder httpSessionCsrfTokenRepository = BeanDefinitionBuilder - .rootBeanDefinition(HttpSessionCsrfTokenRepository.class); - this.csrfRepositoryRef = pc.getReaderContext() - .generateBeanName(httpSessionCsrfTokenRepository.getBeanDefinition()); - pc.registerBeanComponent(new BeanComponentDefinition(httpSessionCsrfTokenRepository.getBeanDefinition(), - this.csrfRepositoryRef)); + RootBeanDefinition csrfTokenRepository = new RootBeanDefinition(HttpSessionCsrfTokenRepository.class); + BeanDefinitionBuilder lazyTokenRepository = BeanDefinitionBuilder + .rootBeanDefinition(LazyCsrfTokenRepository.class); + lazyTokenRepository.addConstructorArgValue(csrfTokenRepository); + this.csrfRepositoryRef = pc.getReaderContext().generateBeanName(lazyTokenRepository.getBeanDefinition()); + pc.registerBeanComponent( + new BeanComponentDefinition(lazyTokenRepository.getBeanDefinition(), this.csrfRepositoryRef)); } BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(CsrfFilter.class); builder.addConstructorArgReference(this.csrfRepositoryRef); diff --git a/config/src/main/java/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrar.java b/config/src/main/java/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrar.java deleted file mode 100644 index 8693333c3c..0000000000 --- a/config/src/main/java/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrar.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright 2002-2023 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.http; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Set; -import java.util.function.Consumer; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.BeanFactoryUtils; -import org.springframework.beans.factory.BeanInitializationException; -import org.springframework.beans.factory.ListableBeanFactory; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionBuilder; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; -import org.springframework.context.annotation.AnnotationBeanNameGenerator; -import org.springframework.core.ResolvableType; -import org.springframework.security.oauth2.client.AuthorizationCodeOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.ClientCredentialsOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.DelegatingOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.JwtBearerOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.PasswordOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest; -import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; -import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest; -import org.springframework.security.oauth2.client.endpoint.OAuth2PasswordGrantRequest; -import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; -import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager; -import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; - -/** - * A registrar for registering the default {@link OAuth2AuthorizedClientManager} bean - * definition, if not already present. - *

    - * Note: This class is a direct copy of - * {@link org.springframework.security.config.annotation.web.configuration.OAuth2ClientConfiguration.OAuth2AuthorizedClientManagerRegistrar}. - * - * @author Joe Grandja - * @author Steve Riesenberg - * @since 6.2.0 - */ -final class OAuth2AuthorizedClientManagerRegistrar implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware { - - // @formatter:off - private static final Set> KNOWN_AUTHORIZED_CLIENT_PROVIDERS = Set.of( - AuthorizationCodeOAuth2AuthorizedClientProvider.class, - RefreshTokenOAuth2AuthorizedClientProvider.class, - ClientCredentialsOAuth2AuthorizedClientProvider.class, - PasswordOAuth2AuthorizedClientProvider.class, - JwtBearerOAuth2AuthorizedClientProvider.class - ); - // @formatter:on - - private final AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); - - private ListableBeanFactory beanFactory; - - @Override - public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { - if (getBeanNamesForType(OAuth2AuthorizedClientManager.class).length != 0 - || getBeanNamesForType(ClientRegistrationRepository.class).length != 1 - || getBeanNamesForType(OAuth2AuthorizedClientRepository.class).length != 1) { - return; - } - - BeanDefinition beanDefinition = BeanDefinitionBuilder - .genericBeanDefinition(OAuth2AuthorizedClientManager.class, this::getAuthorizedClientManager) - .getBeanDefinition(); - - registry.registerBeanDefinition(this.beanNameGenerator.generateBeanName(beanDefinition, registry), - beanDefinition); - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - } - - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = (ListableBeanFactory) beanFactory; - } - - private OAuth2AuthorizedClientManager getAuthorizedClientManager() { - ClientRegistrationRepository clientRegistrationRepository = BeanFactoryUtils - .beanOfTypeIncludingAncestors(this.beanFactory, ClientRegistrationRepository.class, true, true); - - OAuth2AuthorizedClientRepository authorizedClientRepository = BeanFactoryUtils - .beanOfTypeIncludingAncestors(this.beanFactory, OAuth2AuthorizedClientRepository.class, true, true); - - Collection authorizedClientProviderBeans = BeanFactoryUtils - .beansOfTypeIncludingAncestors(this.beanFactory, OAuth2AuthorizedClientProvider.class, true, true) - .values(); - - OAuth2AuthorizedClientProvider authorizedClientProvider; - if (hasDelegatingAuthorizedClientProvider(authorizedClientProviderBeans)) { - authorizedClientProvider = authorizedClientProviderBeans.iterator().next(); - } - else { - List authorizedClientProviders = new ArrayList<>(); - authorizedClientProviders.add(getAuthorizationCodeAuthorizedClientProvider(authorizedClientProviderBeans)); - authorizedClientProviders.add(getRefreshTokenAuthorizedClientProvider(authorizedClientProviderBeans)); - authorizedClientProviders.add(getClientCredentialsAuthorizedClientProvider(authorizedClientProviderBeans)); - authorizedClientProviders.add(getPasswordAuthorizedClientProvider(authorizedClientProviderBeans)); - - OAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = getJwtBearerAuthorizedClientProvider( - authorizedClientProviderBeans); - if (jwtBearerAuthorizedClientProvider != null) { - authorizedClientProviders.add(jwtBearerAuthorizedClientProvider); - } - - authorizedClientProviders.addAll(getAdditionalAuthorizedClientProviders(authorizedClientProviderBeans)); - authorizedClientProvider = new DelegatingOAuth2AuthorizedClientProvider(authorizedClientProviders); - } - - DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager( - clientRegistrationRepository, authorizedClientRepository); - authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); - - Consumer authorizedClientManagerConsumer = getBeanOfType( - ResolvableType.forClassWithGenerics(Consumer.class, DefaultOAuth2AuthorizedClientManager.class)); - if (authorizedClientManagerConsumer != null) { - authorizedClientManagerConsumer.accept(authorizedClientManager); - } - - return authorizedClientManager; - } - - private boolean hasDelegatingAuthorizedClientProvider( - Collection authorizedClientProviders) { - if (authorizedClientProviders.size() != 1) { - return false; - } - return authorizedClientProviders.iterator().next() instanceof DelegatingOAuth2AuthorizedClientProvider; - } - - private OAuth2AuthorizedClientProvider getAuthorizationCodeAuthorizedClientProvider( - Collection authorizedClientProviders) { - AuthorizationCodeOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType( - authorizedClientProviders, AuthorizationCodeOAuth2AuthorizedClientProvider.class); - if (authorizedClientProvider == null) { - authorizedClientProvider = new AuthorizationCodeOAuth2AuthorizedClientProvider(); - } - - return authorizedClientProvider; - } - - private OAuth2AuthorizedClientProvider getRefreshTokenAuthorizedClientProvider( - Collection authorizedClientProviders) { - RefreshTokenOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType( - authorizedClientProviders, RefreshTokenOAuth2AuthorizedClientProvider.class); - if (authorizedClientProvider == null) { - authorizedClientProvider = new RefreshTokenOAuth2AuthorizedClientProvider(); - } - - OAuth2AccessTokenResponseClient accessTokenResponseClient = getBeanOfType( - ResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class, - OAuth2RefreshTokenGrantRequest.class)); - if (accessTokenResponseClient != null) { - authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient); - } - - return authorizedClientProvider; - } - - private OAuth2AuthorizedClientProvider getClientCredentialsAuthorizedClientProvider( - Collection authorizedClientProviders) { - ClientCredentialsOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType( - authorizedClientProviders, ClientCredentialsOAuth2AuthorizedClientProvider.class); - if (authorizedClientProvider == null) { - authorizedClientProvider = new ClientCredentialsOAuth2AuthorizedClientProvider(); - } - - OAuth2AccessTokenResponseClient accessTokenResponseClient = getBeanOfType( - ResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class, - OAuth2ClientCredentialsGrantRequest.class)); - if (accessTokenResponseClient != null) { - authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient); - } - - return authorizedClientProvider; - } - - private OAuth2AuthorizedClientProvider getPasswordAuthorizedClientProvider( - Collection authorizedClientProviders) { - PasswordOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType( - authorizedClientProviders, PasswordOAuth2AuthorizedClientProvider.class); - if (authorizedClientProvider == null) { - authorizedClientProvider = new PasswordOAuth2AuthorizedClientProvider(); - } - - OAuth2AccessTokenResponseClient accessTokenResponseClient = getBeanOfType( - ResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class, - OAuth2PasswordGrantRequest.class)); - if (accessTokenResponseClient != null) { - authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient); - } - - return authorizedClientProvider; - } - - private OAuth2AuthorizedClientProvider getJwtBearerAuthorizedClientProvider( - Collection authorizedClientProviders) { - JwtBearerOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType( - authorizedClientProviders, JwtBearerOAuth2AuthorizedClientProvider.class); - - OAuth2AccessTokenResponseClient accessTokenResponseClient = getBeanOfType(ResolvableType - .forClassWithGenerics(OAuth2AccessTokenResponseClient.class, JwtBearerGrantRequest.class)); - if (accessTokenResponseClient != null) { - if (authorizedClientProvider == null) { - authorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider(); - } - - authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient); - } - - return authorizedClientProvider; - } - - private List getAdditionalAuthorizedClientProviders( - Collection authorizedClientProviders) { - List additionalAuthorizedClientProviders = new ArrayList<>( - authorizedClientProviders); - additionalAuthorizedClientProviders - .removeIf((provider) -> KNOWN_AUTHORIZED_CLIENT_PROVIDERS.contains(provider.getClass())); - return additionalAuthorizedClientProviders; - } - - private T getAuthorizedClientProviderByType( - Collection authorizedClientProviders, Class providerClass) { - T authorizedClientProvider = null; - for (OAuth2AuthorizedClientProvider current : authorizedClientProviders) { - if (providerClass.isInstance(current)) { - assertAuthorizedClientProviderIsNull(authorizedClientProvider); - authorizedClientProvider = providerClass.cast(current); - } - } - return authorizedClientProvider; - } - - private static void assertAuthorizedClientProviderIsNull(OAuth2AuthorizedClientProvider authorizedClientProvider) { - if (authorizedClientProvider != null) { - // @formatter:off - throw new BeanInitializationException(String.format( - "Unable to create an %s bean. Expected one bean of type %s, but found multiple. " + - "Please consider defining only a single bean of this type, or define an %s bean yourself.", - OAuth2AuthorizedClientManager.class.getName(), - authorizedClientProvider.getClass().getName(), - OAuth2AuthorizedClientManager.class.getName())); - // @formatter:on - } - } - - private String[] getBeanNamesForType(Class beanClass) { - return BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, beanClass, false, false); - } - - private T getBeanOfType(ResolvableType resolvableType) { - ObjectProvider objectProvider = this.beanFactory.getBeanProvider(resolvableType, true); - return objectProvider.getIfAvailable(); - } - -} diff --git a/config/src/main/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParser.java index df4dc2d586..5c65d8f28e 100644 --- a/config/src/main/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParser.java @@ -94,7 +94,7 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser { .rootBeanDefinition(OAuth2AuthorizationRequestRedirectFilter.class); String authorizationRequestResolverRef = (authorizationCodeGrantElt != null) ? authorizationCodeGrantElt.getAttribute(ATT_AUTHORIZATION_REQUEST_RESOLVER_REF) : null; - if (StringUtils.hasLength(authorizationRequestResolverRef)) { + if (!StringUtils.isEmpty(authorizationRequestResolverRef)) { authorizationRequestRedirectFilterBuilder.addConstructorArgReference(authorizationRequestResolverRef); } else { @@ -125,7 +125,7 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser { private BeanMetadataElement getAuthorizationRequestRepository(Element element) { String authorizationRequestRepositoryRef = (element != null) ? element.getAttribute(ATT_AUTHORIZATION_REQUEST_REPOSITORY_REF) : null; - if (StringUtils.hasLength(authorizationRequestRepositoryRef)) { + if (!StringUtils.isEmpty(authorizationRequestRepositoryRef)) { return new RuntimeBeanReference(authorizationRequestRepositoryRef); } return BeanDefinitionBuilder @@ -147,7 +147,7 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser { private BeanMetadataElement getAccessTokenResponseClient(Element element) { String accessTokenResponseClientRef = (element != null) ? element.getAttribute(ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF) : null; - if (StringUtils.hasLength(accessTokenResponseClientRef)) { + if (!StringUtils.isEmpty(accessTokenResponseClientRef)) { return new RuntimeBeanReference(accessTokenResponseClientRef); } return BeanDefinitionBuilder diff --git a/config/src/main/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserUtils.java b/config/src/main/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserUtils.java index a4566ebd8f..d75d2d2488 100644 --- a/config/src/main/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserUtils.java +++ b/config/src/main/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserUtils.java @@ -42,7 +42,7 @@ final class OAuth2ClientBeanDefinitionParserUtils { static BeanMetadataElement getClientRegistrationRepository(Element element) { String clientRegistrationRepositoryRef = element.getAttribute(ATT_CLIENT_REGISTRATION_REPOSITORY_REF); - if (StringUtils.hasLength(clientRegistrationRepositoryRef)) { + if (!StringUtils.isEmpty(clientRegistrationRepositoryRef)) { return new RuntimeBeanReference(clientRegistrationRepositoryRef); } return new RuntimeBeanReference(ClientRegistrationRepository.class); @@ -50,7 +50,7 @@ final class OAuth2ClientBeanDefinitionParserUtils { static BeanMetadataElement getAuthorizedClientRepository(Element element) { String authorizedClientRepositoryRef = element.getAttribute(ATT_AUTHORIZED_CLIENT_REPOSITORY_REF); - if (StringUtils.hasLength(authorizedClientRepositoryRef)) { + if (!StringUtils.isEmpty(authorizedClientRepositoryRef)) { return new RuntimeBeanReference(authorizedClientRepositoryRef); } return null; @@ -58,7 +58,7 @@ final class OAuth2ClientBeanDefinitionParserUtils { static BeanMetadataElement getAuthorizedClientService(Element element) { String authorizedClientServiceRef = element.getAttribute(ATT_AUTHORIZED_CLIENT_SERVICE_REF); - if (StringUtils.hasLength(authorizedClientServiceRef)) { + if (!StringUtils.isEmpty(authorizedClientServiceRef)) { return new RuntimeBeanReference(authorizedClientServiceRef); } return null; diff --git a/config/src/main/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParser.java index 48a6151346..19969d9426 100644 --- a/config/src/main/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParser.java @@ -176,7 +176,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { } Object source = parserContext.extractSource(element); String loginProcessingUrl = element.getAttribute(ATT_LOGIN_PROCESSING_URL); - if (StringUtils.hasLength(loginProcessingUrl)) { + if (!StringUtils.isEmpty(loginProcessingUrl)) { WebConfigUtils.validateHttpRedirect(loginProcessingUrl, parserContext, source); oauth2LoginAuthenticationFilterBuilder.addConstructorArgValue(loginProcessingUrl); } @@ -189,7 +189,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { .addConstructorArgValue(accessTokenResponseClient) .addConstructorArgValue(oauth2UserService); String userAuthoritiesMapperRef = element.getAttribute(ATT_USER_AUTHORITIES_MAPPER_REF); - if (StringUtils.hasLength(userAuthoritiesMapperRef)) { + if (!StringUtils.isEmpty(userAuthoritiesMapperRef)) { oauth2LoginAuthenticationProviderBuilder.addPropertyReference("authoritiesMapper", userAuthoritiesMapperRef); } @@ -199,7 +199,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { BeanDefinitionBuilder oauth2AuthorizationRequestRedirectFilterBuilder = BeanDefinitionBuilder .rootBeanDefinition(OAuth2AuthorizationRequestRedirectFilter.class); String authorizationRequestResolverRef = element.getAttribute(ATT_AUTHORIZATION_REQUEST_RESOLVER_REF); - if (StringUtils.hasLength(authorizationRequestResolverRef)) { + if (!StringUtils.isEmpty(authorizationRequestResolverRef)) { oauth2AuthorizationRequestRedirectFilterBuilder.addConstructorArgReference(authorizationRequestResolverRef); } else { @@ -212,7 +212,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { this.oauth2AuthorizationRequestRedirectFilter = oauth2AuthorizationRequestRedirectFilterBuilder .getBeanDefinition(); String authenticationSuccessHandlerRef = element.getAttribute(ATT_AUTHENTICATION_SUCCESS_HANDLER_REF); - if (StringUtils.hasLength(authenticationSuccessHandlerRef)) { + if (!StringUtils.isEmpty(authenticationSuccessHandlerRef)) { oauth2LoginAuthenticationFilterBuilder.addPropertyReference("authenticationSuccessHandler", authenticationSuccessHandlerRef); } @@ -225,7 +225,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { successHandlerBuilder.getBeanDefinition()); } String loginPage = element.getAttribute(ATT_LOGIN_PAGE); - if (StringUtils.hasLength(loginPage)) { + if (!StringUtils.isEmpty(loginPage)) { WebConfigUtils.validateHttpRedirect(loginPage, parserContext, source); this.oauth2LoginAuthenticationEntryPoint = BeanDefinitionBuilder .rootBeanDefinition(LoginUrlAuthenticationEntryPoint.class) @@ -245,7 +245,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { } } String authenticationFailureHandlerRef = element.getAttribute(ATT_AUTHENTICATION_FAILURE_HANDLER_REF); - if (StringUtils.hasLength(authenticationFailureHandlerRef)) { + if (!StringUtils.isEmpty(authenticationFailureHandlerRef)) { oauth2LoginAuthenticationFilterBuilder.addPropertyReference("authenticationFailureHandler", authenticationFailureHandlerRef); } @@ -269,7 +269,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { private BeanMetadataElement getAuthorizationRequestRepository(Element element) { String authorizationRequestRepositoryRef = element.getAttribute(ATT_AUTHORIZATION_REQUEST_REPOSITORY_REF); - if (StringUtils.hasLength(authorizationRequestRepositoryRef)) { + if (!StringUtils.isEmpty(authorizationRequestRepositoryRef)) { return new RuntimeBeanReference(authorizationRequestRepositoryRef); } return BeanDefinitionBuilder @@ -299,11 +299,11 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { "org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeAuthenticationProvider") .addConstructorArgValue(accessTokenResponseClient) .addConstructorArgValue(oidcUserService); - if (StringUtils.hasLength(userAuthoritiesMapperRef)) { + if (!StringUtils.isEmpty(userAuthoritiesMapperRef)) { oidcAuthProviderBuilder.addPropertyReference("authoritiesMapper", userAuthoritiesMapperRef); } String jwtDecoderFactoryRef = element.getAttribute(ATT_JWT_DECODER_FACTORY_REF); - if (StringUtils.hasLength(jwtDecoderFactoryRef)) { + if (!StringUtils.isEmpty(jwtDecoderFactoryRef)) { oidcAuthProviderBuilder.addPropertyReference("jwtDecoderFactory", jwtDecoderFactoryRef); } return oidcAuthProviderBuilder.getBeanDefinition(); @@ -311,7 +311,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { private BeanMetadataElement getOidcUserService(Element element) { String oidcUserServiceRef = element.getAttribute(ATT_OIDC_USER_SERVICE_REF); - if (StringUtils.hasLength(oidcUserServiceRef)) { + if (!StringUtils.isEmpty(oidcUserServiceRef)) { return new RuntimeBeanReference(oidcUserServiceRef); } return BeanDefinitionBuilder @@ -321,7 +321,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { private BeanMetadataElement getOAuth2UserService(Element element) { String oauth2UserServiceRef = element.getAttribute(ATT_USER_SERVICE_REF); - if (StringUtils.hasLength(oauth2UserServiceRef)) { + if (!StringUtils.isEmpty(oauth2UserServiceRef)) { return new RuntimeBeanReference(oauth2UserServiceRef); } return BeanDefinitionBuilder @@ -331,7 +331,7 @@ final class OAuth2LoginBeanDefinitionParser implements BeanDefinitionParser { private BeanMetadataElement getAccessTokenResponseClient(Element element) { String accessTokenResponseClientRef = element.getAttribute(ATT_ACCESS_TOKEN_RESPONSE_CLIENT_REF); - if (StringUtils.hasLength(accessTokenResponseClientRef)) { + if (!StringUtils.isEmpty(accessTokenResponseClientRef)) { return new RuntimeBeanReference(accessTokenResponseClientRef); } return BeanDefinitionBuilder diff --git a/config/src/main/java/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParser.java index 58b896c32b..8c8518bb7e 100644 --- a/config/src/main/java/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParser.java @@ -166,7 +166,7 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa BeanMetadataElement getAuthenticationManagerResolver(Element element) { String authenticationManagerResolverRef = element.getAttribute(AUTHENTICATION_MANAGER_RESOLVER_REF); - if (StringUtils.hasLength(authenticationManagerResolverRef)) { + if (!StringUtils.isEmpty(authenticationManagerResolverRef)) { return new RuntimeBeanReference(authenticationManagerResolverRef); } BeanDefinitionBuilder authenticationManagerResolver = BeanDefinitionBuilder @@ -177,7 +177,7 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa BeanMetadataElement getBearerTokenResolver(Element element) { String bearerTokenResolverRef = element.getAttribute(BEARER_TOKEN_RESOLVER_REF); - if (!StringUtils.hasLength(bearerTokenResolverRef)) { + if (StringUtils.isEmpty(bearerTokenResolverRef)) { return new RootBeanDefinition(DefaultBearerTokenResolver.class); } return new RuntimeBeanReference(bearerTokenResolverRef); @@ -185,7 +185,7 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa BeanMetadataElement getEntryPoint(Element element) { String entryPointRef = element.getAttribute(ENTRY_POINT_REF); - if (!StringUtils.hasLength(entryPointRef)) { + if (StringUtils.isEmpty(entryPointRef)) { return this.authenticationEntryPoint; } return new RuntimeBeanReference(entryPointRef); @@ -224,7 +224,7 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa Object getDecoder(Element element) { String decoderRef = element.getAttribute(DECODER_REF); - if (StringUtils.hasLength(decoderRef)) { + if (!StringUtils.isEmpty(decoderRef)) { return new RuntimeBeanReference(decoderRef); } BeanDefinitionBuilder builder = BeanDefinitionBuilder @@ -235,7 +235,7 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa Object getJwtAuthenticationConverter(Element element) { String jwtDecoderRef = element.getAttribute(JWT_AUTHENTICATION_CONVERTER_REF); - return (StringUtils.hasLength(jwtDecoderRef)) ? new RuntimeBeanReference(jwtDecoderRef) + return (!StringUtils.isEmpty(jwtDecoderRef)) ? new RuntimeBeanReference(jwtDecoderRef) : new JwtAuthenticationConverter(); } @@ -293,7 +293,7 @@ final class OAuth2ResourceServerBeanDefinitionParser implements BeanDefinitionPa BeanMetadataElement getIntrospector(Element element) { String introspectorRef = element.getAttribute(INTROSPECTOR_REF); - if (StringUtils.hasLength(introspectorRef)) { + if (!StringUtils.isEmpty(introspectorRef)) { return new RuntimeBeanReference(introspectorRef); } String introspectionUri = element.getAttribute(INTROSPECTION_URI); diff --git a/config/src/main/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParser.java b/config/src/main/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParser.java index 80ff475579..91a308f68b 100644 --- a/config/src/main/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParser.java +++ b/config/src/main/java/org/springframework/security/config/oauth2/client/ClientRegistrationsBeanDefinitionParser.java @@ -174,7 +174,7 @@ public final class ClientRegistrationsBeanDefinitionParser implements BeanDefini if (providers.containsKey(providerId)) { Map provider = providers.get(providerId); String issuer = provider.get(ATT_ISSUER_URI); - if (StringUtils.hasLength(issuer)) { + if (!StringUtils.isEmpty(issuer)) { ClientRegistration.Builder builder = ClientRegistrations.fromIssuerLocation(issuer) .registrationId(registrationId); return getBuilder(parserContext, builder, provider); diff --git a/config/src/main/java/org/springframework/security/config/web/server/DefaultOidcLogoutTokenValidatorFactory.java b/config/src/main/java/org/springframework/security/config/web/server/DefaultOidcLogoutTokenValidatorFactory.java deleted file mode 100644 index 22c26aa8d7..0000000000 --- a/config/src/main/java/org/springframework/security/config/web/server/DefaultOidcLogoutTokenValidatorFactory.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2002-2023 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.web.server; - -import java.util.function.Function; - -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator; -import org.springframework.security.oauth2.core.OAuth2TokenValidator; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.security.oauth2.jwt.JwtTimestampValidator; - -final class DefaultOidcLogoutTokenValidatorFactory implements Function> { - - @Override - public OAuth2TokenValidator apply(ClientRegistration clientRegistration) { - return new DelegatingOAuth2TokenValidator<>(new JwtTimestampValidator(), - new OidcBackChannelLogoutTokenValidator(clientRegistration)); - } - -} diff --git a/config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutAuthentication.java b/config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutAuthentication.java deleted file mode 100644 index c68063b614..0000000000 --- a/config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutAuthentication.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2002-2023 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.web.server; - -import java.util.Collections; - -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken; - -/** - * An {@link org.springframework.security.core.Authentication} implementation that - * represents the result of authenticating an OIDC Logout token for the purposes of - * performing Back-Channel Logout. - * - * @author Josh Cummings - * @since 6.2 - * @see OidcLogoutAuthenticationToken - * @see OIDC Back-Channel - * Logout - */ -class OidcBackChannelLogoutAuthentication extends AbstractAuthenticationToken { - - private final OidcLogoutToken logoutToken; - - /** - * Construct an {@link OidcBackChannelLogoutAuthentication} - * @param logoutToken a deserialized, verified OIDC Logout Token - */ - OidcBackChannelLogoutAuthentication(OidcLogoutToken logoutToken) { - super(Collections.emptyList()); - this.logoutToken = logoutToken; - setAuthenticated(true); - } - - /** - * {@inheritDoc} - */ - @Override - public OidcLogoutToken getPrincipal() { - return this.logoutToken; - } - - /** - * {@inheritDoc} - */ - @Override - public OidcLogoutToken getCredentials() { - return this.logoutToken; - } - -} diff --git a/config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutReactiveAuthenticationManager.java b/config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutReactiveAuthenticationManager.java deleted file mode 100644 index 1cd87fc830..0000000000 --- a/config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutReactiveAuthenticationManager.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2002-2023 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.web.server; - -import reactor.core.publisher.Mono; - -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.authentication.ReactiveAuthenticationManager; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.oauth2.client.oidc.authentication.ReactiveOidcIdTokenDecoderFactory; -import org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.OAuth2ErrorCodes; -import org.springframework.security.oauth2.jwt.BadJwtException; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.security.oauth2.jwt.JwtDecoder; -import org.springframework.security.oauth2.jwt.JwtDecoderFactory; -import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder; -import org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory; -import org.springframework.util.Assert; - -/** - * An {@link AuthenticationProvider} that authenticates an OIDC Logout Token; namely - * deserializing it, verifying its signature, and validating its claims. - * - *

    - * Intended to be included in a - * {@link org.springframework.security.authentication.ProviderManager} - * - * @author Josh Cummings - * @since 6.2 - * @see OidcLogoutAuthenticationToken - * @see org.springframework.security.authentication.ProviderManager - * @see OIDC Back-Channel - * Logout - */ -final class OidcBackChannelLogoutReactiveAuthenticationManager implements ReactiveAuthenticationManager { - - private ReactiveJwtDecoderFactory logoutTokenDecoderFactory; - - /** - * Construct an {@link OidcBackChannelLogoutReactiveAuthenticationManager} - */ - OidcBackChannelLogoutReactiveAuthenticationManager() { - ReactiveOidcIdTokenDecoderFactory logoutTokenDecoderFactory = new ReactiveOidcIdTokenDecoderFactory(); - logoutTokenDecoderFactory.setJwtValidatorFactory(new DefaultOidcLogoutTokenValidatorFactory()); - this.logoutTokenDecoderFactory = logoutTokenDecoderFactory; - } - - /** - * {@inheritDoc} - */ - @Override - public Mono authenticate(Authentication authentication) throws AuthenticationException { - if (!(authentication instanceof OidcLogoutAuthenticationToken token)) { - return Mono.empty(); - } - String logoutToken = token.getLogoutToken(); - ClientRegistration registration = token.getClientRegistration(); - return decode(registration, logoutToken) - .map((jwt) -> OidcLogoutToken.withTokenValue(logoutToken) - .claims((claims) -> claims.putAll(jwt.getClaims())) - .build()) - .map(OidcBackChannelLogoutAuthentication::new); - } - - private Mono decode(ClientRegistration registration, String token) { - ReactiveJwtDecoder logoutTokenDecoder = this.logoutTokenDecoderFactory.createDecoder(registration); - try { - return logoutTokenDecoder.decode(token); - } - catch (BadJwtException failed) { - OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, failed.getMessage(), - "https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation"); - return Mono.error(new OAuth2AuthenticationException(error, failed)); - } - catch (Exception failed) { - return Mono.error(new AuthenticationServiceException(failed.getMessage(), failed)); - } - } - - /** - * Use this {@link ReactiveJwtDecoderFactory} to generate {@link JwtDecoder}s that - * correspond to the {@link ClientRegistration} associated with the OIDC logout token. - * @param logoutTokenDecoderFactory the {@link JwtDecoderFactory} to use - */ - void setLogoutTokenDecoderFactory(ReactiveJwtDecoderFactory logoutTokenDecoderFactory) { - Assert.notNull(logoutTokenDecoderFactory, "logoutTokenDecoderFactory cannot be null"); - this.logoutTokenDecoderFactory = logoutTokenDecoderFactory; - } - -} diff --git a/config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutTokenValidator.java b/config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutTokenValidator.java deleted file mode 100644 index 7053689171..0000000000 --- a/config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutTokenValidator.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2002-2023 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.web.server; - -import java.time.Instant; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import org.springframework.security.oauth2.client.oidc.authentication.logout.LogoutTokenClaimAccessor; -import org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.OAuth2ErrorCodes; -import org.springframework.security.oauth2.core.OAuth2TokenValidator; -import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult; -import org.springframework.security.oauth2.jwt.Jwt; - -/** - * A {@link OAuth2TokenValidator} that validates OIDC Logout Token claims in conformance - * with the OIDC Back-Channel Logout Spec. - * - * @author Josh Cummings - * @since 6.2 - * @see OidcLogoutToken - * @see Logout - * Token - * @see the OIDC - * Back-Channel Logout spec - */ -final class OidcBackChannelLogoutTokenValidator implements OAuth2TokenValidator { - - private static final String LOGOUT_VALIDATION_URL = "https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation"; - - private static final String BACK_CHANNEL_LOGOUT_EVENT = "http://schemas.openid.net/event/backchannel-logout"; - - private final String audience; - - private final String issuer; - - OidcBackChannelLogoutTokenValidator(ClientRegistration clientRegistration) { - this.audience = clientRegistration.getClientId(); - this.issuer = clientRegistration.getProviderDetails().getIssuerUri(); - } - - @Override - public OAuth2TokenValidatorResult validate(Jwt jwt) { - Collection errors = new ArrayList<>(); - - LogoutTokenClaimAccessor logoutClaims = jwt::getClaims; - Map events = logoutClaims.getEvents(); - if (events == null) { - errors.add(invalidLogoutToken("events claim must not be null")); - } - else if (events.get(BACK_CHANNEL_LOGOUT_EVENT) == null) { - errors.add(invalidLogoutToken("events claim map must contain \"" + BACK_CHANNEL_LOGOUT_EVENT + "\" key")); - } - - String issuer = logoutClaims.getIssuer().toExternalForm(); - if (issuer == null) { - errors.add(invalidLogoutToken("iss claim must not be null")); - } - else if (!this.issuer.equals(issuer)) { - errors.add(invalidLogoutToken( - "iss claim value must match `ClientRegistration#getProviderDetails#getIssuerUri`")); - } - - List audience = logoutClaims.getAudience(); - if (audience == null) { - errors.add(invalidLogoutToken("aud claim must not be null")); - } - else if (!audience.contains(this.audience)) { - errors.add(invalidLogoutToken("aud claim value must include `ClientRegistration#getClientId`")); - } - - Instant issuedAt = logoutClaims.getIssuedAt(); - if (issuedAt == null) { - errors.add(invalidLogoutToken("iat claim must not be null")); - } - - String jwtId = logoutClaims.getId(); - if (jwtId == null) { - errors.add(invalidLogoutToken("jti claim must not be null")); - } - - if (logoutClaims.getSubject() == null && logoutClaims.getSessionId() == null) { - errors.add(invalidLogoutToken("sub and sid claims must not both be null")); - } - - if (logoutClaims.getClaim("nonce") != null) { - errors.add(invalidLogoutToken("nonce claim must not be present")); - } - - return OAuth2TokenValidatorResult.failure(errors); - } - - private static OAuth2Error invalidLogoutToken(String description) { - return new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, description, LOGOUT_VALIDATION_URL); - } - -} diff --git a/config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutWebFilter.java b/config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutWebFilter.java deleted file mode 100644 index 74f5f32e68..0000000000 --- a/config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelLogoutWebFilter.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2002-2023 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.web.server; - -import java.nio.charset.StandardCharsets; - -import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.authentication.ReactiveAuthenticationManager; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.OAuth2ErrorCodes; -import org.springframework.security.web.authentication.AuthenticationConverter; -import org.springframework.security.web.authentication.logout.LogoutHandler; -import org.springframework.security.web.server.WebFilterExchange; -import org.springframework.security.web.server.authentication.ServerAuthenticationConverter; -import org.springframework.security.web.server.authentication.logout.ServerLogoutHandler; -import org.springframework.util.Assert; -import org.springframework.web.server.ServerWebExchange; -import org.springframework.web.server.WebFilter; -import org.springframework.web.server.WebFilterChain; - -/** - * A filter for the Client-side OIDC Back-Channel Logout endpoint - * - * @author Josh Cummings - * @since 6.2 - * @see OIDC Back-Channel Logout - * Spec - */ -class OidcBackChannelLogoutWebFilter implements WebFilter { - - private final Log logger = LogFactory.getLog(getClass()); - - private final ServerAuthenticationConverter authenticationConverter; - - private final ReactiveAuthenticationManager authenticationManager; - - private ServerLogoutHandler logoutHandler = new OidcBackChannelServerLogoutHandler(); - - /** - * Construct an {@link OidcBackChannelLogoutWebFilter} - * @param authenticationConverter the {@link AuthenticationConverter} for deriving - * Logout Token authentication - * @param authenticationManager the {@link AuthenticationManager} for authenticating - * Logout Tokens - */ - OidcBackChannelLogoutWebFilter(ServerAuthenticationConverter authenticationConverter, - ReactiveAuthenticationManager authenticationManager) { - Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); - Assert.notNull(authenticationManager, "authenticationManager cannot be null"); - this.authenticationConverter = authenticationConverter; - this.authenticationManager = authenticationManager; - } - - @Override - public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - return this.authenticationConverter.convert(exchange).onErrorResume(AuthenticationException.class, (ex) -> { - this.logger.debug("Failed to process OIDC Back-Channel Logout", ex); - if (ex instanceof AuthenticationServiceException) { - return Mono.error(ex); - } - return handleAuthenticationFailure(exchange.getResponse(), ex).then(Mono.empty()); - }) - .switchIfEmpty(chain.filter(exchange).then(Mono.empty())) - .flatMap(this.authenticationManager::authenticate) - .onErrorResume(AuthenticationException.class, (ex) -> { - this.logger.debug("Failed to process OIDC Back-Channel Logout", ex); - if (ex instanceof AuthenticationServiceException) { - return Mono.error(ex); - } - return handleAuthenticationFailure(exchange.getResponse(), ex).then(Mono.empty()); - }) - .flatMap((authentication) -> { - WebFilterExchange webFilterExchange = new WebFilterExchange(exchange, chain); - return this.logoutHandler.logout(webFilterExchange, authentication); - }); - } - - private Mono handleAuthenticationFailure(ServerHttpResponse response, Exception ex) { - this.logger.debug("Failed to process OIDC Back-Channel Logout", ex); - response.setRawStatusCode(HttpServletResponse.SC_BAD_REQUEST); - OAuth2Error error = oauth2Error(ex); - byte[] bytes = String.format(""" - { - "error_code": "%s", - "error_description": "%s", - "error_uri: "%s" - } - """, error.getErrorCode(), error.getDescription(), error.getUri()).getBytes(StandardCharsets.UTF_8); - DataBuffer buffer = response.bufferFactory().wrap(bytes); - return response.writeWith(Flux.just(buffer)); - } - - private OAuth2Error oauth2Error(Exception ex) { - if (ex instanceof OAuth2AuthenticationException oauth2) { - return oauth2.getError(); - } - return new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, ex.getMessage(), - "https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation"); - } - - /** - * The strategy for expiring all Client sessions indicated by the logout request. - * Defaults to {@link OidcBackChannelServerLogoutHandler}. - * @param logoutHandler the {@link LogoutHandler} to use - */ - void setLogoutHandler(ServerLogoutHandler logoutHandler) { - Assert.notNull(logoutHandler, "logoutHandler cannot be null"); - this.logoutHandler = logoutHandler; - } - -} diff --git a/config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelServerLogoutHandler.java b/config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelServerLogoutHandler.java deleted file mode 100644 index 9cb49de176..0000000000 --- a/config/src/main/java/org/springframework/security/config/web/server/OidcBackChannelServerLogoutHandler.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2002-2023 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.web.server; - -import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -import jakarta.servlet.http.HttpServletResponse; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken; -import org.springframework.security.oauth2.client.oidc.server.session.InMemoryReactiveOidcSessionRegistry; -import org.springframework.security.oauth2.client.oidc.server.session.ReactiveOidcSessionRegistry; -import org.springframework.security.oauth2.client.oidc.session.OidcSessionInformation; -import org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.web.server.WebFilterExchange; -import org.springframework.security.web.server.authentication.logout.ServerLogoutHandler; -import org.springframework.util.Assert; -import org.springframework.web.reactive.function.client.WebClient; -import org.springframework.web.util.UriComponentsBuilder; - -/** - * A {@link ServerLogoutHandler} that locates the sessions associated with a given OIDC - * Back-Channel Logout Token and invalidates each one. - * - * @author Josh Cummings - * @since 6.2 - * @see OIDC Back-Channel Logout - * Spec - */ -final class OidcBackChannelServerLogoutHandler implements ServerLogoutHandler { - - private final Log logger = LogFactory.getLog(getClass()); - - private ReactiveOidcSessionRegistry sessionRegistry = new InMemoryReactiveOidcSessionRegistry(); - - private WebClient web = WebClient.create(); - - private String logoutEndpointName = "/logout"; - - private String sessionCookieName = "SESSION"; - - @Override - public Mono logout(WebFilterExchange exchange, Authentication authentication) { - if (!(authentication instanceof OidcBackChannelLogoutAuthentication token)) { - return Mono.defer(() -> { - if (this.logger.isDebugEnabled()) { - String message = "Did not perform OIDC Back-Channel Logout since authentication [%s] was of the wrong type"; - this.logger.debug(String.format(message, authentication.getClass().getSimpleName())); - } - return Mono.empty(); - }); - } - AtomicInteger totalCount = new AtomicInteger(0); - AtomicInteger invalidatedCount = new AtomicInteger(0); - return this.sessionRegistry.removeSessionInformation(token.getPrincipal()).concatMap((session) -> { - totalCount.incrementAndGet(); - return eachLogout(exchange, session).flatMap((response) -> { - invalidatedCount.incrementAndGet(); - return Mono.empty(); - }).onErrorResume((ex) -> { - this.logger.debug("Failed to invalidate session", ex); - return this.sessionRegistry.saveSessionInformation(session).then(Mono.just(ex.getMessage())); - }); - }).collectList().flatMap((list) -> { - if (this.logger.isTraceEnabled()) { - this.logger.trace(String.format("Invalidated %d out of %d sessions", invalidatedCount.intValue(), - totalCount.intValue())); - } - if (!list.isEmpty()) { - return handleLogoutFailure(exchange.getExchange().getResponse(), oauth2Error(list)); - } - else { - return Mono.empty(); - } - }); - } - - private Mono> eachLogout(WebFilterExchange exchange, OidcSessionInformation session) { - HttpHeaders headers = new HttpHeaders(); - headers.add(HttpHeaders.COOKIE, this.sessionCookieName + "=" + session.getSessionId()); - for (Map.Entry credential : session.getAuthorities().entrySet()) { - headers.add(credential.getKey(), credential.getValue()); - } - String url = exchange.getExchange().getRequest().getURI().toString(); - String logout = UriComponentsBuilder.fromHttpUrl(url) - .replacePath(this.logoutEndpointName) - .build() - .toUriString(); - return this.web.post().uri(logout).headers((h) -> h.putAll(headers)).retrieve().toBodilessEntity(); - } - - private OAuth2Error oauth2Error(Collection errors) { - return new OAuth2Error("partial_logout", "not all sessions were terminated: " + errors, - "https://openid.net/specs/openid-connect-backchannel-1_0.html#Validation"); - } - - private Mono handleLogoutFailure(ServerHttpResponse response, OAuth2Error error) { - response.setRawStatusCode(HttpServletResponse.SC_BAD_REQUEST); - byte[] bytes = String.format(""" - { - "error_code": "%s", - "error_description": "%s", - "error_uri: "%s" - } - """, error.getErrorCode(), error.getDescription(), error.getUri()).getBytes(StandardCharsets.UTF_8); - DataBuffer buffer = response.bufferFactory().wrap(bytes); - return response.writeWith(Flux.just(buffer)); - } - - /** - * Use this {@link OidcSessionRegistry} to identify sessions to invalidate. Note that - * this class uses - * {@link OidcSessionRegistry#removeSessionInformation(OidcLogoutToken)} to identify - * sessions. - * @param sessionRegistry the {@link OidcSessionRegistry} to use - */ - void setSessionRegistry(ReactiveOidcSessionRegistry sessionRegistry) { - Assert.notNull(sessionRegistry, "sessionRegistry cannot be null"); - this.sessionRegistry = sessionRegistry; - } - - /** - * Use this {@link WebClient} to perform the per-session back-channel logout - * @param web the {@link WebClient} to use - */ - void setWebClient(WebClient web) { - Assert.notNull(web, "web cannot be null"); - this.web = web; - } - - /** - * Use this logout URI for performing per-session logout. Defaults to {@code /logout} - * since that is the default URI for - * {@link org.springframework.security.web.authentication.logout.LogoutFilter}. - * @param logoutUri the URI to use - */ - void setLogoutUri(String logoutUri) { - Assert.hasText(logoutUri, "logoutUri cannot be empty"); - this.logoutEndpointName = logoutUri; - } - - /** - * Use this cookie name for the session identifier. Defaults to {@code JSESSIONID}. - * - *

    - * Note that if you are using Spring Session, this likely needs to change to SESSION. - * @param sessionCookieName the cookie name to use - */ - void setSessionCookieName(String sessionCookieName) { - Assert.hasText(sessionCookieName, "clientSessionCookieName cannot be empty"); - this.sessionCookieName = sessionCookieName; - } - -} diff --git a/config/src/main/java/org/springframework/security/config/web/server/OidcLogoutAuthenticationToken.java b/config/src/main/java/org/springframework/security/config/web/server/OidcLogoutAuthenticationToken.java deleted file mode 100644 index 8d5ab818a5..0000000000 --- a/config/src/main/java/org/springframework/security/config/web/server/OidcLogoutAuthenticationToken.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2002-2023 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.web.server; - -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.oauth2.client.registration.ClientRegistration; - -/** - * An {@link org.springframework.security.core.Authentication} instance that represents a - * request to authenticate an OIDC Logout Token. - * - * @author Josh Cummings - * @since 6.2 - */ -class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken { - - private final String logoutToken; - - private final ClientRegistration clientRegistration; - - /** - * Construct an {@link OidcLogoutAuthenticationToken} - * @param logoutToken a signed, serialized OIDC Logout token - * @param clientRegistration the {@link ClientRegistration client} associated with - * this token; this is usually derived from material in the logout HTTP request - */ - OidcLogoutAuthenticationToken(String logoutToken, ClientRegistration clientRegistration) { - super(AuthorityUtils.NO_AUTHORITIES); - this.logoutToken = logoutToken; - this.clientRegistration = clientRegistration; - } - - /** - * {@inheritDoc} - */ - @Override - public String getCredentials() { - return this.logoutToken; - } - - /** - * {@inheritDoc} - */ - @Override - public String getPrincipal() { - return this.logoutToken; - } - - /** - * Get the signed, serialized OIDC Logout token - * @return the logout token - */ - String getLogoutToken() { - return this.logoutToken; - } - - /** - * Get the {@link ClientRegistration} associated with this logout token - * @return the {@link ClientRegistration} - */ - ClientRegistration getClientRegistration() { - return this.clientRegistration; - } - -} diff --git a/config/src/main/java/org/springframework/security/config/web/server/OidcLogoutServerAuthenticationConverter.java b/config/src/main/java/org/springframework/security/config/web/server/OidcLogoutServerAuthenticationConverter.java deleted file mode 100644 index 02bdeb3472..0000000000 --- a/config/src/main/java/org/springframework/security/config/web/server/OidcLogoutServerAuthenticationConverter.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2002-2023 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.web.server; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import reactor.core.publisher.Mono; - -import org.springframework.http.HttpMethod; -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.OAuth2ErrorCodes; -import org.springframework.security.web.authentication.AuthenticationConverter; -import org.springframework.security.web.server.authentication.ServerAuthenticationConverter; -import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher; -import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; -import org.springframework.util.Assert; -import org.springframework.web.server.ServerWebExchange; - -/** - * An {@link AuthenticationConverter} that extracts the OIDC Logout Token authentication - * request - * - * @author Josh Cummings - * @since 6.2 - */ -final class OidcLogoutServerAuthenticationConverter implements ServerAuthenticationConverter { - - private static final String DEFAULT_LOGOUT_URI = "/logout/connect/back-channel/{registrationId}"; - - private final Log logger = LogFactory.getLog(getClass()); - - private final ReactiveClientRegistrationRepository clientRegistrationRepository; - - private ServerWebExchangeMatcher exchangeMatcher = new PathPatternParserServerWebExchangeMatcher(DEFAULT_LOGOUT_URI, - HttpMethod.POST); - - OidcLogoutServerAuthenticationConverter(ReactiveClientRegistrationRepository clientRegistrationRepository) { - Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null"); - this.clientRegistrationRepository = clientRegistrationRepository; - } - - @Override - public Mono convert(ServerWebExchange exchange) { - return this.exchangeMatcher.matches(exchange) - .filter(ServerWebExchangeMatcher.MatchResult::isMatch) - .flatMap((match) -> { - String registrationId = (String) match.getVariables().get("registrationId"); - return this.clientRegistrationRepository.findByRegistrationId(registrationId) - .switchIfEmpty(Mono.error(() -> { - this.logger - .debug("Did not process OIDC Back-Channel Logout since no ClientRegistration was found"); - return new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST); - })); - }) - .flatMap((clientRegistration) -> exchange.getFormData().map((data) -> { - String logoutToken = data.getFirst("logout_token"); - return new OidcLogoutAuthenticationToken(logoutToken, clientRegistration); - }).switchIfEmpty(Mono.error(() -> { - this.logger.debug("Failed to process OIDC Back-Channel Logout since no logout token was found"); - return new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST); - }))); - } - - /** - * The logout endpoint. Defaults to - * {@code /logout/connect/back-channel/{registrationId}}. - * @param exchangeMatcher the {@link ServerWebExchangeMatcher} to use - */ - void setExchangeMatcher(ServerWebExchangeMatcher exchangeMatcher) { - Assert.notNull(exchangeMatcher, "exchangeMatcher cannot be null"); - this.exchangeMatcher = exchangeMatcher; - } - -} diff --git a/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java b/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java index 3df5775afc..582d98ed69 100644 --- a/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java +++ b/config/src/main/java/org/springframework/security/config/web/server/ServerHttpSecurity.java @@ -21,7 +21,6 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.security.interfaces.RSAPublicKey; import java.time.Duration; -import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -29,13 +28,10 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import io.micrometer.observation.ObservationRegistry; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import reactor.core.publisher.Mono; import reactor.util.context.Context; @@ -71,9 +67,6 @@ import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCo import org.springframework.security.oauth2.client.endpoint.ReactiveOAuth2AccessTokenResponseClient; import org.springframework.security.oauth2.client.endpoint.WebClientReactiveAuthorizationCodeTokenResponseClient; import org.springframework.security.oauth2.client.oidc.authentication.OidcAuthorizationCodeReactiveAuthenticationManager; -import org.springframework.security.oauth2.client.oidc.server.session.InMemoryReactiveOidcSessionRegistry; -import org.springframework.security.oauth2.client.oidc.server.session.ReactiveOidcSessionRegistry; -import org.springframework.security.oauth2.client.oidc.session.OidcSessionInformation; import org.springframework.security.oauth2.client.oidc.userinfo.OidcReactiveOAuth2UserService; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest; import org.springframework.security.oauth2.client.registration.ClientRegistration; @@ -120,7 +113,6 @@ import org.springframework.security.web.server.MatcherSecurityWebFilterChain; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.ServerAuthenticationEntryPoint; import org.springframework.security.web.server.ServerRedirectStrategy; -import org.springframework.security.web.server.WebFilterExchange; import org.springframework.security.web.server.authentication.AnonymousAuthenticationWebFilter; import org.springframework.security.web.server.authentication.AuthenticationConverterServerWebExchangeMatcher; import org.springframework.security.web.server.authentication.AuthenticationWebFilter; @@ -155,7 +147,6 @@ import org.springframework.security.web.server.context.SecurityContextServerWebE import org.springframework.security.web.server.context.ServerSecurityContextRepository; import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository; import org.springframework.security.web.server.csrf.CsrfServerLogoutHandler; -import org.springframework.security.web.server.csrf.CsrfToken; import org.springframework.security.web.server.csrf.CsrfWebFilter; import org.springframework.security.web.server.csrf.ServerCsrfTokenRepository; import org.springframework.security.web.server.csrf.ServerCsrfTokenRequestHandler; @@ -202,10 +193,8 @@ import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.DefaultCorsProcessor; import org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.server.ServerWebExchange; -import org.springframework.web.server.ServerWebExchangeDecorator; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; -import org.springframework.web.server.WebSession; import org.springframework.web.util.pattern.PathPatternParser; /** @@ -306,8 +295,6 @@ public class ServerHttpSecurity { private OAuth2ClientSpec client; - private OidcLogoutSpec oidcLogout; - private LogoutSpec logout = new LogoutSpec(); private LoginPageSpec loginPage = new LoginPageSpec(); @@ -1106,33 +1093,6 @@ public class ServerHttpSecurity { return this; } - /** - * Configures OIDC Connect 1.0 Logout support. - * - *

    -	 *  @Bean
    -	 *  public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
    -	 *      http
    -	 *          // ...
    -	 *          .oidcLogout((logout) -> logout
    -	 *              .backChannel(Customizer.withDefaults())
    -	 *          );
    -	 *      return http.build();
    -	 *  }
    -	 * 
    - * @param oidcLogoutCustomizer the {@link Customizer} to provide more options for the - * {@link OidcLogoutSpec} - * @return the {@link ServerHttpSecurity} to customize - * @since 6.2 - */ - public ServerHttpSecurity oidcLogout(Customizer oidcLogoutCustomizer) { - if (this.oidcLogout == null) { - this.oidcLogout = new OidcLogoutSpec(); - } - oidcLogoutCustomizer.customize(this.oidcLogout); - return this; - } - /** * Configures HTTP Response Headers. The default headers are: * @@ -1577,9 +1537,6 @@ public class ServerHttpSecurity { if (this.resourceServer != null) { this.resourceServer.configure(this); } - if (this.oidcLogout != null) { - this.oidcLogout.configure(this); - } if (this.client != null) { this.client.configure(this); } @@ -3733,8 +3690,6 @@ public class ServerHttpSecurity { private ServerWebExchangeMatcher authenticationMatcher; - private ReactiveOidcSessionRegistry oidcSessionRegistry; - private ServerAuthenticationSuccessHandler authenticationSuccessHandler; private ServerAuthenticationFailureHandler authenticationFailureHandler; @@ -3766,20 +3721,6 @@ public class ServerHttpSecurity { return this; } - /** - * Configures the {@link ReactiveOidcSessionRegistry} to use when logins use OIDC. - * Default is to look the value up as a Bean, or else use an - * {@link InMemoryReactiveOidcSessionRegistry}. - * @param oidcSessionRegistry the registry to use - * @return the {@link OidcLogoutSpec} to customize - * @since 6.2 - */ - public OAuth2LoginSpec oidcSessionRegistry(ReactiveOidcSessionRegistry oidcSessionRegistry) { - Assert.notNull(oidcSessionRegistry, "oidcSessionRegistry cannot be null"); - this.oidcSessionRegistry = oidcSessionRegistry; - return this; - } - /** * The {@link ServerAuthenticationSuccessHandler} used after authentication * success. Defaults to {@link RedirectServerAuthenticationSuccessHandler} @@ -3973,9 +3914,8 @@ public class ServerHttpSecurity { oauthRedirectFilter.setRequestCache(http.requestCache.requestCache); ReactiveAuthenticationManager manager = getAuthenticationManager(); - ReactiveOidcSessionRegistry sessionRegistry = getOidcSessionRegistry(); - AuthenticationWebFilter authenticationFilter = new OidcSessionRegistryAuthenticationWebFilter(manager, - authorizedClientRepository, sessionRegistry); + AuthenticationWebFilter authenticationFilter = new OAuth2LoginAuthenticationWebFilter(manager, + authorizedClientRepository); authenticationFilter.setRequiresAuthenticationMatcher(getAuthenticationMatcher()); authenticationFilter .setServerAuthenticationConverter(getAuthenticationConverter(clientRegistrationRepository)); @@ -3984,8 +3924,6 @@ public class ServerHttpSecurity { authenticationFilter.setSecurityContextRepository(this.securityContextRepository); setDefaultEntryPoints(http); - http.addFilterAfter(new OidcSessionRegistryWebFilter(sessionRegistry), - SecurityWebFiltersOrder.HTTP_HEADERS_WRITER); http.addFilterAt(oauthRedirectFilter, SecurityWebFiltersOrder.HTTP_BASIC); http.addFilterAt(authenticationFilter, SecurityWebFiltersOrder.AUTHENTICATION); } @@ -4030,16 +3968,6 @@ public class ServerHttpSecurity { http.defaultEntryPoints.add(new DelegateEntry(defaultEntryPointMatcher, defaultEntryPoint)); } - private ReactiveOidcSessionRegistry getOidcSessionRegistry() { - if (this.oidcSessionRegistry == null) { - this.oidcSessionRegistry = getBeanOrNull(ReactiveOidcSessionRegistry.class); - } - if (this.oidcSessionRegistry == null) { - this.oidcSessionRegistry = new InMemoryReactiveOidcSessionRegistry(); - } - return this.oidcSessionRegistry; - } - private ServerAuthenticationSuccessHandler getAuthenticationSuccessHandler(ServerHttpSecurity http) { if (this.authenticationSuccessHandler == null) { RedirectServerAuthenticationSuccessHandler handler = new RedirectServerAuthenticationSuccessHandler(); @@ -4156,157 +4084,6 @@ public class ServerHttpSecurity { return new InMemoryReactiveOAuth2AuthorizedClientService(getClientRegistrationRepository()); } - private static final class OidcSessionRegistryWebFilter implements WebFilter { - - private final ReactiveOidcSessionRegistry oidcSessionRegistry; - - OidcSessionRegistryWebFilter(ReactiveOidcSessionRegistry oidcSessionRegistry) { - this.oidcSessionRegistry = oidcSessionRegistry; - } - - @Override - public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { - return chain.filter(new OidcSessionRegistryServerWebExchange(exchange)); - } - - private final class OidcSessionRegistryServerWebExchange extends ServerWebExchangeDecorator { - - private final Mono sessionMono; - - protected OidcSessionRegistryServerWebExchange(ServerWebExchange delegate) { - super(delegate); - this.sessionMono = delegate.getSession().map(OidcSessionRegistryWebSession::new); - } - - @Override - public Mono getSession() { - return this.sessionMono; - } - - private final class OidcSessionRegistryWebSession implements WebSession { - - private final WebSession session; - - OidcSessionRegistryWebSession(WebSession session) { - this.session = session; - } - - @Override - public String getId() { - return this.session.getId(); - } - - @Override - public Map getAttributes() { - return this.session.getAttributes(); - } - - @Override - public void start() { - this.session.start(); - } - - @Override - public boolean isStarted() { - return this.session.isStarted(); - } - - @Override - public Mono changeSessionId() { - String currentId = this.session.getId(); - return this.session.changeSessionId() - .then(Mono.defer(() -> OidcSessionRegistryWebFilter.this.oidcSessionRegistry - .removeSessionInformation(currentId) - .flatMap((information) -> { - information = information.withSessionId(this.session.getId()); - return OidcSessionRegistryWebFilter.this.oidcSessionRegistry - .saveSessionInformation(information); - }))); - } - - @Override - public Mono invalidate() { - String currentId = this.session.getId(); - return this.session.invalidate() - .then(Mono.defer(() -> OidcSessionRegistryWebFilter.this.oidcSessionRegistry - .removeSessionInformation(currentId) - .then(Mono.empty()))); - } - - @Override - public Mono save() { - return this.session.save(); - } - - @Override - public boolean isExpired() { - return this.session.isExpired(); - } - - @Override - public Instant getCreationTime() { - return this.session.getCreationTime(); - } - - @Override - public Instant getLastAccessTime() { - return this.session.getLastAccessTime(); - } - - @Override - public void setMaxIdleTime(Duration maxIdleTime) { - this.session.setMaxIdleTime(maxIdleTime); - } - - @Override - public Duration getMaxIdleTime() { - return this.session.getMaxIdleTime(); - } - - } - - } - - } - - private static final class OidcSessionRegistryAuthenticationWebFilter - extends OAuth2LoginAuthenticationWebFilter { - - private final Log logger = LogFactory.getLog(getClass()); - - private final ReactiveOidcSessionRegistry oidcSessionRegistry; - - OidcSessionRegistryAuthenticationWebFilter(ReactiveAuthenticationManager authenticationManager, - ServerOAuth2AuthorizedClientRepository authorizedClientRepository, - ReactiveOidcSessionRegistry oidcSessionRegistry) { - super(authenticationManager, authorizedClientRepository); - this.oidcSessionRegistry = oidcSessionRegistry; - } - - @Override - protected Mono onAuthenticationSuccess(Authentication authentication, - WebFilterExchange webFilterExchange) { - if (!(authentication.getPrincipal() instanceof OidcUser user)) { - return super.onAuthenticationSuccess(authentication, webFilterExchange); - } - return webFilterExchange.getExchange().getSession().doOnNext((session) -> { - if (this.logger.isTraceEnabled()) { - this.logger.trace(String.format("Linking a provider [%s] session to this client's session", - user.getIssuer())); - } - }).flatMap((session) -> { - Mono csrfToken = webFilterExchange.getExchange().getAttribute(CsrfToken.class.getName()); - return (csrfToken != null) - ? csrfToken.map((token) -> new OidcSessionInformation(session.getId(), - Map.of(token.getHeaderName(), token.getToken()), user)) - : Mono.just(new OidcSessionInformation(session.getId(), Map.of(), user)); - }) - .flatMap(this.oidcSessionRegistry::saveSessionInformation) - .then(super.onAuthenticationSuccess(authentication, webFilterExchange)); - } - - } - } public final class OAuth2ClientSpec { @@ -4979,129 +4756,6 @@ public class ServerHttpSecurity { } - /** - * Configures OIDC 1.0 Logout support - * - * @author Josh Cummings - * @since 6.2 - */ - public final class OidcLogoutSpec { - - private ReactiveClientRegistrationRepository clientRegistrationRepository; - - private ReactiveOidcSessionRegistry sessionRegistry; - - private BackChannelLogoutConfigurer backChannel; - - /** - * Configures the {@link ReactiveClientRegistrationRepository}. Default is to look - * the value up as a Bean. - * @param clientRegistrationRepository the repository to use - * @return the {@link OidcLogoutSpec} to customize - */ - public OidcLogoutSpec clientRegistrationRepository( - ReactiveClientRegistrationRepository clientRegistrationRepository) { - Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null"); - this.clientRegistrationRepository = clientRegistrationRepository; - return this; - } - - /** - * Configures the {@link ReactiveOidcSessionRegistry}. Default is to use the value - * from {@link OAuth2LoginSpec#oidcSessionRegistry}, then look the value up as a - * Bean, or else use an {@link InMemoryReactiveOidcSessionRegistry}. - * @param sessionRegistry the registry to use - * @return the {@link OidcLogoutSpec} to customize - */ - public OidcLogoutSpec oidcSessionRegistry(ReactiveOidcSessionRegistry sessionRegistry) { - Assert.notNull(sessionRegistry, "sessionRegistry cannot be null"); - this.sessionRegistry = sessionRegistry; - return this; - } - - /** - * Configure OIDC Back-Channel Logout using the provided {@link Consumer} - * @return the {@link OidcLogoutSpec} for further configuration - */ - public OidcLogoutSpec backChannel(Customizer backChannelLogoutConfigurer) { - if (this.backChannel == null) { - this.backChannel = new OidcLogoutSpec.BackChannelLogoutConfigurer(); - } - backChannelLogoutConfigurer.customize(this.backChannel); - return this; - } - - @Deprecated(forRemoval = true, since = "6.2") - public ServerHttpSecurity and() { - return ServerHttpSecurity.this; - } - - void configure(ServerHttpSecurity http) { - if (this.backChannel != null) { - this.backChannel.configure(http); - } - } - - private ReactiveClientRegistrationRepository getClientRegistrationRepository() { - if (this.clientRegistrationRepository == null) { - this.clientRegistrationRepository = getBeanOrNull(ReactiveClientRegistrationRepository.class); - } - return this.clientRegistrationRepository; - } - - private ReactiveOidcSessionRegistry getSessionRegistry() { - if (this.sessionRegistry == null && ServerHttpSecurity.this.oauth2Login == null) { - return new InMemoryReactiveOidcSessionRegistry(); - } - if (this.sessionRegistry == null) { - return ServerHttpSecurity.this.oauth2Login.oidcSessionRegistry; - } - return this.sessionRegistry; - } - - /** - * A configurer for configuring OIDC Back-Channel Logout - */ - public final class BackChannelLogoutConfigurer { - - private ServerAuthenticationConverter authenticationConverter; - - private final ReactiveAuthenticationManager authenticationManager = new OidcBackChannelLogoutReactiveAuthenticationManager(); - - private ServerLogoutHandler logoutHandler; - - private ServerAuthenticationConverter authenticationConverter() { - if (this.authenticationConverter == null) { - this.authenticationConverter = new OidcLogoutServerAuthenticationConverter( - OidcLogoutSpec.this.getClientRegistrationRepository()); - } - return this.authenticationConverter; - } - - private ReactiveAuthenticationManager authenticationManager() { - return this.authenticationManager; - } - - private ServerLogoutHandler logoutHandler() { - if (this.logoutHandler == null) { - OidcBackChannelServerLogoutHandler logoutHandler = new OidcBackChannelServerLogoutHandler(); - logoutHandler.setSessionRegistry(OidcLogoutSpec.this.getSessionRegistry()); - this.logoutHandler = logoutHandler; - } - return this.logoutHandler; - } - - void configure(ServerHttpSecurity http) { - OidcBackChannelLogoutWebFilter filter = new OidcBackChannelLogoutWebFilter(authenticationConverter(), - authenticationManager()); - filter.setLogoutHandler(logoutHandler()); - http.addFilterBefore(filter, SecurityWebFiltersOrder.CSRF); - } - - } - - } - /** * Configures anonymous authentication * diff --git a/config/src/main/kotlin/org/springframework/security/config/annotation/web/HttpSecurityDsl.kt b/config/src/main/kotlin/org/springframework/security/config/annotation/web/HttpSecurityDsl.kt index 60342d2af8..2661343415 100644 --- a/config/src/main/kotlin/org/springframework/security/config/annotation/web/HttpSecurityDsl.kt +++ b/config/src/main/kotlin/org/springframework/security/config/annotation/web/HttpSecurityDsl.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,6 @@ package org.springframework.security.config.annotation.web -import jakarta.servlet.Filter -import jakarta.servlet.http.HttpServletRequest -import org.checkerframework.checker.units.qual.C import org.springframework.context.ApplicationContext import org.springframework.security.authentication.AuthenticationManager import org.springframework.security.config.annotation.SecurityConfigurerAdapter @@ -27,6 +24,9 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository import org.springframework.security.web.DefaultSecurityFilterChain import org.springframework.security.web.util.matcher.RequestMatcher +import org.springframework.util.ClassUtils +import jakarta.servlet.Filter +import jakarta.servlet.http.HttpServletRequest /** * Configures [HttpSecurity] using a [HttpSecurity Kotlin DSL][HttpSecurityDsl]. @@ -107,36 +107,6 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu return this.http.apply(configurer).apply(configuration) } - /** - * Applies a [SecurityConfigurerAdapter] to this [HttpSecurity] - * - * Example: - * - * ``` - * @Configuration - * @EnableWebSecurity - * class SecurityConfig { - * - * @Bean - * fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - * http { - * with(CustomSecurityConfigurer()) { - * customProperty = "..." - * } - * } - * return http.build() - * } - * } - * ``` - * - * @param configurer - * the [HttpSecurity] for further customizations - * @since 6.2 - */ - fun > with(configurer: C, configuration: C.() -> Unit = { }): HttpSecurity? { - return this.http.with(configurer, configuration) - } - /** * Allows configuring the [HttpSecurity] to only be invoked when matching the * provided pattern. @@ -868,38 +838,6 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu this.http.oauth2ResourceServer(oauth2ResourceServerCustomizer) } - /** - * Configures OIDC 1.0 logout support. - * - * Example: - * - * ``` - * @Configuration - * @EnableWebSecurity - * class SecurityConfig { - * - * @Bean - * fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - * http { - * oauth2Login { } - * oidcLogout { - * backChannel { } - * } - * } - * return http.build() - * } - * } - * ``` - * - * @param oidcLogoutConfiguration custom configuration to configure the - * OIDC 1.0 logout support - * @see [OidcLogoutDsl] - */ - fun oidcLogout(oidcLogoutConfiguration: OidcLogoutDsl.() -> Unit) { - val oidcLogoutCustomizer = OidcLogoutDsl().apply(oidcLogoutConfiguration).get() - this.http.oidcLogout(oidcLogoutCustomizer) - } - /** * Configures Remember Me authentication. * diff --git a/config/src/main/kotlin/org/springframework/security/config/annotation/web/OidcLogoutDsl.kt b/config/src/main/kotlin/org/springframework/security/config/annotation/web/OidcLogoutDsl.kt deleted file mode 100644 index f9fdd7dc4d..0000000000 --- a/config/src/main/kotlin/org/springframework/security/config/annotation/web/OidcLogoutDsl.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web - -import org.springframework.security.config.annotation.web.builders.HttpSecurity -import org.springframework.security.config.annotation.web.configurers.oauth2.client.OidcLogoutConfigurer -import org.springframework.security.config.annotation.web.oauth2.login.OidcBackChannelLogoutDsl -import org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository - -/** - * A Kotlin DSL to configure [HttpSecurity] OAuth 1.0 Logout using idiomatic Kotlin code. - * - * @author Josh Cummings - * @since 6.2 - */ -@SecurityMarker -class OidcLogoutDsl { - var clientRegistrationRepository: ClientRegistrationRepository? = null - var oidcSessionRegistry: OidcSessionRegistry? = null - - private var backChannel: ((OidcLogoutConfigurer.BackChannelLogoutConfigurer) -> Unit)? = null - - /** - * Configures the OIDC 1.0 Back-Channel endpoint. - * - * Example: - * - * ``` - * @Configuration - * @EnableWebSecurity - * class SecurityConfig { - * - * @Bean - * fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - * http { - * oauth2Login { } - * oidcLogout { - * backChannel { } - * } - * } - * return http.build() - * } - * } - * ``` - * - * @param backChannelConfig custom configurations to configure the back-channel endpoint - * @see [OidcBackChannelLogoutDsl] - */ - fun backChannel(backChannelConfig: OidcBackChannelLogoutDsl.() -> Unit) { - this.backChannel = OidcBackChannelLogoutDsl().apply(backChannelConfig).get() - } - - internal fun get(): (OidcLogoutConfigurer) -> Unit { - return { oidcLogout -> - clientRegistrationRepository?.also { oidcLogout.clientRegistrationRepository(clientRegistrationRepository) } - oidcSessionRegistry?.also { oidcLogout.oidcSessionRegistry(oidcSessionRegistry) } - backChannel?.also { oidcLogout.backChannel(backChannel) } - } - } -} diff --git a/config/src/main/kotlin/org/springframework/security/config/annotation/web/oauth2/login/OidcBackChannelLogoutDsl.kt b/config/src/main/kotlin/org/springframework/security/config/annotation/web/oauth2/login/OidcBackChannelLogoutDsl.kt deleted file mode 100644 index efac77a566..0000000000 --- a/config/src/main/kotlin/org/springframework/security/config/annotation/web/oauth2/login/OidcBackChannelLogoutDsl.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.oauth2.login - -import org.springframework.security.config.annotation.web.builders.HttpSecurity -import org.springframework.security.config.annotation.web.configurers.oauth2.client.OidcLogoutConfigurer - -/** - * A Kotlin DSL to configure the OIDC 1.0 Back-Channel configuration using - * idiomatic Kotlin code. - * - * @author Josh Cummings - * @since 6.2 - */ -@OAuth2LoginSecurityMarker -class OidcBackChannelLogoutDsl { - internal fun get(): (OidcLogoutConfigurer.BackChannelLogoutConfigurer) -> Unit { - return { backChannel -> } - } -} diff --git a/config/src/main/kotlin/org/springframework/security/config/web/server/ServerHttpSecurityDsl.kt b/config/src/main/kotlin/org/springframework/security/config/web/server/ServerHttpSecurityDsl.kt index 300a3d6a60..ce17734444 100644 --- a/config/src/main/kotlin/org/springframework/security/config/web/server/ServerHttpSecurityDsl.kt +++ b/config/src/main/kotlin/org/springframework/security/config/web/server/ServerHttpSecurityDsl.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 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. @@ -650,38 +650,6 @@ class ServerHttpSecurityDsl(private val http: ServerHttpSecurity, private val in this.http.oauth2ResourceServer(oauth2ResourceServerCustomizer) } - /** - * Configures logout support using an OpenID Connect 1.0 Provider. - * A [ReactiveClientRegistrationRepository] is required and must be registered as a Bean or - * configured via [ServerOidcLogoutDsl.clientRegistrationRepository]. - * - * Example: - * - * ``` - * @Configuration - * @EnableWebFluxSecurity - * class SecurityConfig { - * - * @Bean - * fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { - * return http { - * oauth2Login { } - * oidcLogout { - * backChannel { } - * } - * } - * } - * } - * ``` - * - * @param oidcLogoutConfiguration custom configuration to configure the OIDC 1.0 Logout - * @see [ServerOidcLogoutDsl] - */ - fun oidcLogout(oidcLogoutConfiguration: ServerOidcLogoutDsl.() -> Unit) { - val oidcLogoutCustomizer = ServerOidcLogoutDsl().apply(oidcLogoutConfiguration).get() - this.http.oidcLogout(oidcLogoutCustomizer) - } - /** * Apply all configurations to the provided [ServerHttpSecurity] */ diff --git a/config/src/main/kotlin/org/springframework/security/config/web/server/ServerOidcBackChannelLogoutDsl.kt b/config/src/main/kotlin/org/springframework/security/config/web/server/ServerOidcBackChannelLogoutDsl.kt deleted file mode 100644 index 5a245e5092..0000000000 --- a/config/src/main/kotlin/org/springframework/security/config/web/server/ServerOidcBackChannelLogoutDsl.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2002-2023 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.web.server - -/** - * A Kotlin DSL to configure [ServerHttpSecurity] OIDC 1.0 Back-Channel Logout support using idiomatic Kotlin code. - * - * @author Josh Cummings - * @since 6.2 - */ -@ServerSecurityMarker -class ServerOidcBackChannelLogoutDsl { - internal fun get(): (ServerHttpSecurity.OidcLogoutSpec.BackChannelLogoutConfigurer) -> Unit { - return { backChannel -> } - } -} diff --git a/config/src/main/kotlin/org/springframework/security/config/web/server/ServerOidcLogoutDsl.kt b/config/src/main/kotlin/org/springframework/security/config/web/server/ServerOidcLogoutDsl.kt deleted file mode 100644 index 503a5b0c84..0000000000 --- a/config/src/main/kotlin/org/springframework/security/config/web/server/ServerOidcLogoutDsl.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2002-2023 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.web.server - -import org.springframework.security.oauth2.client.oidc.server.session.ReactiveOidcSessionRegistry -import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository - -/** - * A Kotlin DSL to configure [ServerHttpSecurity] OIDC 1.0 login using idiomatic Kotlin code. - * - * @author Josh Cummings - * @since 6.2 - */ -@ServerSecurityMarker -class ServerOidcLogoutDsl { - var clientRegistrationRepository: ReactiveClientRegistrationRepository? = null - var oidcSessionRegistry: ReactiveOidcSessionRegistry? = null - - private var backChannel: ((ServerHttpSecurity.OidcLogoutSpec.BackChannelLogoutConfigurer) -> Unit)? = null - - /** - * Enables OIDC 1.0 Back-Channel Logout support. - * - * Example: - * - * ``` - * @Configuration - * @EnableWebFluxSecurity - * class SecurityConfig { - * - * @Bean - * fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { - * return http { - * oauth2Login { } - * oidcLogout { - * backChannel { } - * } - * } - * } - * } - * ``` - * - * @param backChannelConfig custom configurations to configure OIDC 1.0 Back-Channel Logout support - * @see [ServerOidcBackChannelLogoutDsl] - */ - fun backChannel(backChannelConfig: ServerOidcBackChannelLogoutDsl.() -> Unit) { - this.backChannel = ServerOidcBackChannelLogoutDsl().apply(backChannelConfig).get() - } - - internal fun get(): (ServerHttpSecurity.OidcLogoutSpec) -> Unit { - return { oidcLogout -> - clientRegistrationRepository?.also { oidcLogout.clientRegistrationRepository(clientRegistrationRepository) } - oidcSessionRegistry?.also { oidcLogout.oidcSessionRegistry(oidcSessionRegistry) } - backChannel?.also { oidcLogout.backChannel(backChannel) } - } - } -} diff --git a/config/src/main/resources/META-INF/spring.schemas b/config/src/main/resources/META-INF/spring.schemas index f25b5f682b..0aa29ee6a6 100644 --- a/config/src/main/resources/META-INF/spring.schemas +++ b/config/src/main/resources/META-INF/spring.schemas @@ -1,5 +1,4 @@ -http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.2.xsd -http\://www.springframework.org/schema/security/spring-security-6.2.xsd=org/springframework/security/config/spring-security-6.2.xsd +http\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.1.xsd http\://www.springframework.org/schema/security/spring-security-6.1.xsd=org/springframework/security/config/spring-security-6.1.xsd http\://www.springframework.org/schema/security/spring-security-6.0.xsd=org/springframework/security/config/spring-security-6.0.xsd http\://www.springframework.org/schema/security/spring-security-5.8.xsd=org/springframework/security/config/spring-security-5.8.xsd @@ -22,8 +21,7 @@ http\://www.springframework.org/schema/security/spring-security-2.0.xsd=org/spri http\://www.springframework.org/schema/security/spring-security-2.0.1.xsd=org/springframework/security/config/spring-security-2.0.1.xsd http\://www.springframework.org/schema/security/spring-security-2.0.2.xsd=org/springframework/security/config/spring-security-2.0.2.xsd http\://www.springframework.org/schema/security/spring-security-2.0.4.xsd=org/springframework/security/config/spring-security-2.0.4.xsd -https\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.2.xsd -https\://www.springframework.org/schema/security/spring-security-6.2.xsd=org/springframework/security/config/spring-security-6.2.xsd +https\://www.springframework.org/schema/security/spring-security.xsd=org/springframework/security/config/spring-security-6.1.xsd https\://www.springframework.org/schema/security/spring-security-6.1.xsd=org/springframework/security/config/spring-security-6.1.xsd https\://www.springframework.org/schema/security/spring-security-6.0.xsd=org/springframework/security/config/spring-security-6.0.xsd https\://www.springframework.org/schema/security/spring-security-5.8.xsd=org/springframework/security/config/spring-security-5.8.xsd diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-6.2.rnc b/config/src/main/resources/org/springframework/security/config/spring-security-6.2.rnc deleted file mode 100644 index 7f89ced5af..0000000000 --- a/config/src/main/resources/org/springframework/security/config/spring-security-6.2.rnc +++ /dev/null @@ -1,1346 +0,0 @@ -namespace a = "https://relaxng.org/ns/compatibility/annotations/1.0" -datatypes xsd = "http://www.w3.org/2001/XMLSchema-datatypes" - -default namespace = "http://www.springframework.org/schema/security" - -start = http | ldap-server | authentication-provider | ldap-authentication-provider | any-user-service | ldap-server | ldap-authentication-provider - -hash = - ## Defines the hashing algorithm used on user passwords. Bcrypt is recommended. - attribute hash {"bcrypt"} -base64 = - ## Whether a string should be base64 encoded - attribute base64 {xsd:boolean} -request-matcher = - ## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions. - attribute request-matcher {"mvc" | "ant" | "regex" | "ciRegex"} -port = - ## Specifies an IP port number. Used to configure an embedded LDAP server, for example. - attribute port { xsd:nonNegativeInteger } -url = - ## Specifies a URL. - attribute url { xsd:token } -id = - ## A bean identifier, used for referring to the bean elsewhere in the context. - attribute id {xsd:token} -name = - ## A bean identifier, used for referring to the bean elsewhere in the context. - attribute name {xsd:token} -ref = - ## Defines a reference to a Spring bean Id. - attribute ref {xsd:token} - -cache-ref = - ## Defines a reference to a cache for use with a UserDetailsService. - attribute cache-ref {xsd:token} - -user-service-ref = - ## A reference to a user-service (or UserDetailsService bean) Id - attribute user-service-ref {xsd:token} - -authentication-manager-ref = - ## A reference to an AuthenticationManager bean - attribute authentication-manager-ref {xsd:token} - -data-source-ref = - ## A reference to a DataSource bean - attribute data-source-ref {xsd:token} - - - -debug = - ## Enables Spring Security debugging infrastructure. This will provide human-readable (multi-line) debugging information to monitor requests coming into the security filters. This may include sensitive information, such as request parameters or headers, and should only be used in a development environment. - element debug {empty} - -password-encoder = - ## element which defines a password encoding strategy. Used by an authentication provider to convert submitted passwords to hashed versions, for example. - element password-encoder {password-encoder.attlist} -password-encoder.attlist &= - ref | (hash) - -role-prefix = - ## A non-empty string prefix that will be added to role strings loaded from persistent storage (e.g. "ROLE_"). Use the value "none" for no prefix in cases where the default is non-empty. - attribute role-prefix {xsd:token} - -use-expressions = - ## Enables the use of expressions in the 'access' attributes in elements rather than the traditional list of configuration attributes. Defaults to 'true'. If enabled, each attribute should contain a single boolean expression. If the expression evaluates to 'true', access will be granted. - attribute use-expressions {xsd:boolean} - -ldap-server = - ## Defines an LDAP server location or starts an embedded server. The url indicates the location of a remote server. If no url is given, an embedded server will be started, listening on the supplied port number. The port is optional and defaults to 33389. A Spring LDAP ContextSource bean will be registered for the server with the id supplied. - element ldap-server {ldap-server.attlist} -ldap-server.attlist &= id? -ldap-server.attlist &= (url | port)? -ldap-server.attlist &= - ## Username (DN) of the "manager" user identity which will be used to authenticate to a (non-embedded) LDAP server. If omitted, anonymous access will be used. - attribute manager-dn {xsd:string}? -ldap-server.attlist &= - ## The password for the manager DN. This is required if the manager-dn is specified. - attribute manager-password {xsd:string}? -ldap-server.attlist &= - ## Explicitly specifies an ldif file resource to load into an embedded LDAP server. The default is classpath*:*.ldiff - attribute ldif { xsd:string }? -ldap-server.attlist &= - ## Optional root suffix for the embedded LDAP server. Default is "dc=springframework,dc=org" - attribute root { xsd:string }? -ldap-server.attlist &= - ## Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and 'unboundid'. By default, it will depends if the library is available in the classpath. - attribute mode { "apacheds" | "unboundid" }? - -ldap-server-ref-attribute = - ## The optional server to use. If omitted, and a default LDAP server is registered (using with no Id), that server will be used. - attribute server-ref {xsd:token} - - -group-search-filter-attribute = - ## Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN of the user. - attribute group-search-filter {xsd:token} -group-search-base-attribute = - ## Search base for group membership searches. Defaults to "" (searching from the root). - attribute group-search-base {xsd:token} -user-search-filter-attribute = - ## The LDAP filter used to search for users (optional). For example "(uid={0})". The substituted parameter is the user's login name. - attribute user-search-filter {xsd:token} -user-search-base-attribute = - ## Search base for user searches. Defaults to "". Only used with a 'user-search-filter'. - attribute user-search-base {xsd:token} -group-role-attribute-attribute = - ## The LDAP attribute name which contains the role name which will be used within Spring Security. Defaults to "cn". - attribute group-role-attribute {xsd:token} -user-details-class-attribute = - ## Allows the objectClass of the user entry to be specified. If set, the framework will attempt to load standard attributes for the defined class into the returned UserDetails object - attribute user-details-class {"person" | "inetOrgPerson"} -user-context-mapper-attribute = - ## Allows explicit customization of the loaded user object by specifying a UserDetailsContextMapper bean which will be called with the context information from the user's directory entry - attribute user-context-mapper-ref {xsd:token} - - -ldap-user-service = - ## This element configures a LdapUserDetailsService which is a combination of a FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator. - element ldap-user-service {ldap-us.attlist} -ldap-us.attlist &= id? -ldap-us.attlist &= - ldap-server-ref-attribute? -ldap-us.attlist &= - user-search-filter-attribute? -ldap-us.attlist &= - user-search-base-attribute? -ldap-us.attlist &= - group-search-filter-attribute? -ldap-us.attlist &= - group-search-base-attribute? -ldap-us.attlist &= - group-role-attribute-attribute? -ldap-us.attlist &= - cache-ref? -ldap-us.attlist &= - role-prefix? -ldap-us.attlist &= - (user-details-class-attribute | user-context-mapper-attribute)? - -ldap-authentication-provider = - ## Sets up an ldap authentication provider - element ldap-authentication-provider {ldap-ap.attlist, password-compare-element?} -ldap-ap.attlist &= - ldap-server-ref-attribute? -ldap-ap.attlist &= - user-search-base-attribute? -ldap-ap.attlist &= - user-search-filter-attribute? -ldap-ap.attlist &= - group-search-base-attribute? -ldap-ap.attlist &= - group-search-filter-attribute? -ldap-ap.attlist &= - group-role-attribute-attribute? -ldap-ap.attlist &= - ## A specific pattern used to build the user's DN, for example "uid={0},ou=people". The key "{0}" must be present and will be substituted with the username. - attribute user-dn-pattern {xsd:token}? -ldap-ap.attlist &= - role-prefix? -ldap-ap.attlist &= - (user-details-class-attribute | user-context-mapper-attribute)? - -password-compare-element = - ## Specifies that an LDAP provider should use an LDAP compare operation of the user's password to authenticate the user - element password-compare {password-compare.attlist, password-encoder?} - -password-compare.attlist &= - ## The attribute in the directory which contains the user password. Defaults to "userPassword". - attribute password-attribute {xsd:token}? -password-compare.attlist &= - hash? - -intercept-methods = - ## Can be used inside a bean definition to add a security interceptor to the bean and set up access configuration attributes for the bean's methods - element intercept-methods {intercept-methods.attlist, protect+} -intercept-methods.attlist &= - ## Optional AccessDecisionManager bean ID to be used by the created method security interceptor. - attribute access-decision-manager-ref {xsd:token}? -intercept-methods.attlist &= - ## Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true) - attribute use-authorization-manager {xsd:boolean}? -intercept-methods.attlist &= - ## Use this AuthorizationManager instead of the default (supercedes use-authorization-manager) - attribute authorization-manager-ref {xsd:token}? - -protect = - ## Defines a protected method and the access control configuration attributes that apply to it. We strongly advise you NOT to mix "protect" declarations with any services provided "global-method-security". - element protect {protect.attlist, empty} -protect.attlist &= - ## A method name - attribute method {xsd:token} -protect.attlist &= - ## Access configuration attributes list that applies to the method, e.g. "ROLE_A,ROLE_B". - attribute access {xsd:token} - -method-security-metadata-source = - ## Creates a MethodSecurityMetadataSource instance - element method-security-metadata-source {msmds.attlist, protect+} -msmds.attlist &= id? - -msmds.attlist &= use-expressions? - -method-security = - ## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with Spring Security annotations. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. Interceptors are invoked in the order specified in AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP. Also, annotation-based interception can be overridden by expressions listed in elements. - element method-security {method-security.attlist, expression-handler?, protect-pointcut*} -method-security.attlist &= - ## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to "true". - attribute pre-post-enabled {xsd:boolean}? -method-security.attlist &= - ## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to "false". - attribute secured-enabled {xsd:boolean}? -method-security.attlist &= - ## Specifies whether JSR-250 style attributes are to be used (for example "RolesAllowed"). This will require the javax.annotation.security classes on the classpath. Defaults to "false". - attribute jsr250-enabled {xsd:boolean}? -method-security.attlist &= - ## If true, class-based proxying will be used instead of interface-based proxying. - attribute proxy-target-class {xsd:boolean}? -method-security.attlist &= - ## If set to aspectj, then use AspectJ to intercept method invocation - attribute mode {"aspectj"}? -method-security.attlist &= - ## Specifies the security context holder strategy to use, by default uses a ThreadLocal-based strategy - attribute security-context-holder-strategy-ref {xsd:string}? -method-security.attlist &= - ## Use this ObservationRegistry to collect metrics on various parts of the filter chain - attribute observation-registry-ref {xsd:token}? - -global-method-security = - ## Provides method security for all beans registered in the Spring application context. Specifically, beans will be scanned for matches with the ordered list of "protect-pointcut" sub-elements, Spring Security annotations and/or. Where there is a match, the beans will automatically be proxied and security authorization applied to the methods accordingly. If you use and enable all four sources of method security metadata (ie "protect-pointcut" declarations, expression annotations, @Secured and also JSR250 security annotations), the metadata sources will be queried in that order. In practical terms, this enables you to use XML to override method security metadata expressed in annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize etc.), @Secured and finally JSR-250. - element global-method-security {global-method-security.attlist, (pre-post-annotation-handling | expression-handler)?, protect-pointcut*, after-invocation-provider*} -global-method-security.attlist &= - ## Specifies whether the use of Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this application context. Defaults to "disabled". - attribute pre-post-annotations {"disabled" | "enabled" }? -global-method-security.attlist &= - ## Specifies whether the use of Spring Security's @Secured annotations should be enabled for this application context. Defaults to "disabled". - attribute secured-annotations {"disabled" | "enabled" }? -global-method-security.attlist &= - ## Specifies whether JSR-250 style attributes are to be used (for example "RolesAllowed"). This will require the javax.annotation.security classes on the classpath. Defaults to "disabled". - attribute jsr250-annotations {"disabled" | "enabled" }? -global-method-security.attlist &= - ## Optional AccessDecisionManager bean ID to override the default used for method security. - attribute access-decision-manager-ref {xsd:token}? -global-method-security.attlist &= - ## Optional RunAsmanager implementation which will be used by the configured MethodSecurityInterceptor - attribute run-as-manager-ref {xsd:token}? -global-method-security.attlist &= - ## Allows the advice "order" to be set for the method security interceptor. - attribute order {xsd:token}? -global-method-security.attlist &= - ## If true, class based proxying will be used instead of interface based proxying. - attribute proxy-target-class {xsd:boolean}? -global-method-security.attlist &= - ## Can be used to specify that AspectJ should be used instead of the default Spring AOP. If set, secured classes must be woven with the AnnotationSecurityAspect from the spring-security-aspects module. - attribute mode {"aspectj"}? -global-method-security.attlist &= - ## An external MethodSecurityMetadataSource instance can be supplied which will take priority over other sources (such as the default annotations). - attribute metadata-source-ref {xsd:token}? -global-method-security.attlist &= - authentication-manager-ref? - - -after-invocation-provider = - ## Allows addition of extra AfterInvocationProvider beans which should be called by the MethodSecurityInterceptor created by global-method-security. - element after-invocation-provider {ref} - -pre-post-annotation-handling = - ## Allows the default expression-based mechanism for handling Spring Security's pre and post invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be replace entirely. Only applies if these annotations are enabled. - element pre-post-annotation-handling {invocation-attribute-factory, pre-invocation-advice, post-invocation-advice} - -invocation-attribute-factory = - ## Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and post invocation metadata from the annotated methods. - element invocation-attribute-factory {ref} - -pre-invocation-advice = - ## Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the PreInvocationAuthorizationAdviceVoter for the element. - element pre-invocation-advice {ref} - -post-invocation-advice = - ## Customizes the PostInvocationAdviceProvider with the ref as the PostInvocationAuthorizationAdvice for the element. - element post-invocation-advice {ref} - - -expression-handler = - ## Defines the SecurityExpressionHandler instance which will be used if expression-based access-control is enabled. A default implementation (with no ACL support) will be used if not supplied. - element expression-handler {ref} - -protect-pointcut = - ## Defines a protected pointcut and the access control configuration attributes that apply to it. Every bean registered in the Spring application context that provides a method that matches the pointcut will receive security authorization. - element protect-pointcut {protect-pointcut.attlist, empty} -protect-pointcut.attlist &= - ## An AspectJ expression, including the 'execution' keyword. For example, 'execution(int com.foo.TargetObject.countLength(String))' (without the quotes). - attribute expression {xsd:string} -protect-pointcut.attlist &= - ## Access configuration attributes list that applies to all methods matching the pointcut, e.g. "ROLE_A,ROLE_B" - attribute access {xsd:token} - -websocket-message-broker = - ## Allows securing a Message Broker. There are two modes. If no id is specified: ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that can be manually registered with the clientInboundChannel. - element websocket-message-broker { websocket-message-broker.attrlist, (intercept-message* & expression-handler?) } - -websocket-message-broker.attrlist &= - ## A bean identifier, used for referring to the bean elsewhere in the context. If specified, explicit configuration within clientInboundChannel is required. If not specified, ensures that any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures that the SecurityContextChannelInterceptor is automatically registered for the clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the clientInboundChannel. - attribute id {xsd:token}? -websocket-message-broker.attrlist &= - ## Disables the requirement for CSRF token to be present in the Stomp headers (default false). Changing the default is useful if it is necessary to allow other origins to make SockJS connections. - attribute same-origin-disabled {xsd:boolean}? -websocket-message-broker.attrlist &= - ## Use this AuthorizationManager instead of deriving one from elements - attribute authorization-manager-ref {xsd:string}? -websocket-message-broker.attrlist &= - ## Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true) - attribute use-authorization-manager {xsd:boolean}? -websocket-message-broker.attrlist &= - ## Use this SecurityContextHolderStrategy (note only supported in conjunction with the AuthorizationManager API) - attribute security-context-holder-strategy-ref {xsd:string}? - -intercept-message = - ## Creates an authorization rule for a websocket message. - element intercept-message {intercept-message.attrlist} - -intercept-message.attrlist &= - ## The destination ant pattern which will be mapped to the access attribute. For example, /** matches any message with a destination, /admin/** matches any message that has a destination that starts with admin. - attribute pattern {xsd:token}? -intercept-message.attrlist &= - ## The access configuration attributes that apply for the configured message. For example, permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role 'ROLE_ADMIN'. - attribute access {xsd:token}? -intercept-message.attrlist &= - ## The type of message to match on. Valid values are defined in SimpMessageType (i.e. CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, DISCONNECT_ACK, OTHER). - attribute type {"CONNECT" | "CONNECT_ACK" | "HEARTBEAT" | "MESSAGE" | "SUBSCRIBE"| "UNSUBSCRIBE" | "DISCONNECT" | "DISCONNECT_ACK" | "OTHER"}? - -http-firewall = - ## Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created by the namespace. - element http-firewall {ref} - -http = - ## Container element for HTTP security configuration. Multiple elements can now be defined, each with a specific pattern to which the enclosed security configuration applies. A pattern can also be configured to bypass Spring Security's filters completely by setting the "security" attribute to "none". - element http {http.attlist, (intercept-url* & access-denied-handler? & form-login? & oauth2-login? & oauth2-client? & oauth2-resource-server? & saml2-login? & saml2-logout? & x509? & jee? & http-basic? & logout? & password-management? & session-management & remember-me? & anonymous? & port-mappings & custom-filter* & request-cache? & expression-handler? & headers? & csrf? & cors?) } -http.attlist &= - ## The request URL pattern which will be mapped to the filter chain created by this element. If omitted, the filter chain will match all requests. - attribute pattern {xsd:token}? -http.attlist &= - ## When set to 'none', requests matching the pattern attribute will be ignored by Spring Security. No security filters will be applied and no SecurityContext will be available. If set, the element must be empty, with no children. - attribute security {"none"}? -http.attlist &= - ## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching. - attribute request-matcher-ref { xsd:token }? -http.attlist &= - ## A legacy attribute which automatically registers a login form, BASIC authentication and a logout URL and logout services. If unspecified, defaults to "false". We'd recommend you avoid using this and instead explicitly configure the services you require. - attribute auto-config {xsd:boolean}? -http.attlist &= - use-expressions? -http.attlist &= - ## A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the SecurityContextHolder is stored during a request - attribute security-context-holder-strategy-ref {xsd:token}? -http.attlist &= - ## Controls the eagerness with which an HTTP session is created by Spring Security classes. If not set, defaults to "ifRequired". If "stateless" is used, this implies that the application guarantees that it will not create a session. This differs from the use of "never" which means that Spring Security will not create a session, but will make use of one if the application does. - attribute create-session {"ifRequired" | "always" | "never" | "stateless"}? -http.attlist &= - ## A reference to a SecurityContextRepository bean. This can be used to customize how the SecurityContext is stored between requests. - attribute security-context-repository-ref {xsd:token}? -http.attlist &= - ## Optional attribute that specifies that the SecurityContext should require explicit saving rather than being synchronized from the SecurityContextHolder. Defaults to "true". - attribute security-context-explicit-save {xsd:boolean}? -http.attlist &= - request-matcher? -http.attlist &= - ## Provides versions of HttpServletRequest security methods such as isUserInRole() and getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to "true". - attribute servlet-api-provision {xsd:boolean}? -http.attlist &= - ## If available, runs the request as the Subject acquired from the JaasAuthenticationToken. Defaults to "false". - attribute jaas-api-provision {xsd:boolean}? -http.attlist &= - ## Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true) - attribute use-authorization-manager {xsd:boolean}? -http.attlist &= - ## Use this AuthorizationManager instead of deriving one from elements - attribute authorization-manager-ref {xsd:token}? -http.attlist &= - ## Optional attribute specifying the ID of the AccessDecisionManager implementation which should be used for authorizing HTTP requests. - attribute access-decision-manager-ref {xsd:token}? -http.attlist &= - ## Optional attribute specifying the realm name that will be used for all authentication features that require a realm name (eg BASIC and Digest authentication). If unspecified, defaults to "Spring Security Application". - attribute realm {xsd:token}? -http.attlist &= - ## Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter. - attribute entry-point-ref {xsd:token}? -http.attlist &= - ## Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults to "false" - attribute once-per-request {xsd:boolean}? -http.attlist &= - ## Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not work when use-authorization-manager=false. Defaults to "true". - attribute filter-all-dispatcher-types {xsd:boolean}? -http.attlist &= - ## Prevents the jsessionid parameter from being added to rendered URLs. Defaults to "true" (rewriting is disabled). - attribute disable-url-rewriting {xsd:boolean}? -http.attlist &= - ## Exposes the list of filters defined by this configuration under this bean name in the application context. - name? -http.attlist &= - authentication-manager-ref? -http.attlist &= - ## Use this ObservationRegistry to collect metrics on various parts of the filter chain - attribute observation-registry-ref {xsd:token}? - -access-denied-handler = - ## Defines the access-denied strategy that should be used. An access denied page can be defined or a reference to an AccessDeniedHandler instance. - element access-denied-handler {access-denied-handler.attlist, empty} -access-denied-handler.attlist &= (ref | access-denied-handler-page) - -access-denied-handler-page = - ## The access denied page that an authenticated user will be redirected to if they request a page which they don't have the authority to access. - attribute error-page {xsd:token} - -intercept-url = - ## Specifies the access attributes and/or filter list for a particular set of URLs. - element intercept-url {intercept-url.attlist, empty} -intercept-url.attlist &= - (pattern | request-matcher-ref) -intercept-url.attlist &= - ## The access configuration attributes that apply for the configured path. - attribute access {xsd:token}? -intercept-url.attlist &= - ## The HTTP Method for which the access configuration attributes should apply. If not specified, the attributes will apply to any method. - attribute method {"GET" | "DELETE" | "HEAD" | "OPTIONS" | "POST" | "PUT" | "PATCH" | "TRACE"}? - -intercept-url.attlist &= - ## Used to specify that a URL must be accessed over http or https, or that there is no preference. The value should be "http", "https" or "any", respectively. - attribute requires-channel {xsd:token}? -intercept-url.attlist &= - ## The path to the servlet. This attribute is only applicable when 'request-matcher' is 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are 2 or more HttpServlet's registered in the ServletContext that have mappings starting with '/' and are different; 2) The pattern starts with the same value of a registered HttpServlet path, excluding the default (root) HttpServlet '/'. - attribute servlet-path {xsd:token}? - -logout = - ## Incorporates a logout processing filter. Most web applications require a logout filter, although you may not require one if you write a controller to provider similar logic. - element logout {logout.attlist, empty} -logout.attlist &= - ## Specifies the URL that will cause a logout. Spring Security will initialize a filter that responds to this particular URL. Defaults to /logout if unspecified. - attribute logout-url {xsd:token}? -logout.attlist &= - ## Specifies the URL to display once the user has logged out. If not specified, defaults to /?logout (i.e. /login?logout). - attribute logout-success-url {xsd:token}? -logout.attlist &= - ## Specifies whether a logout also causes HttpSession invalidation, which is generally desirable. If unspecified, defaults to true. - attribute invalidate-session {xsd:boolean}? -logout.attlist &= - ## A reference to a LogoutSuccessHandler implementation which will be used to determine the destination to which the user is taken after logging out. - attribute success-handler-ref {xsd:token}? -logout.attlist &= - ## A comma-separated list of the names of cookies which should be deleted when the user logs out - attribute delete-cookies {xsd:token}? - -request-cache = - ## Allow the RequestCache used for saving requests during the login process to be set - element request-cache {ref} - -form-login = - ## Sets up a form login configuration for authentication with a username and password - element form-login {form-login.attlist, empty} -form-login.attlist &= - ## The URL that the login form is posted to. If unspecified, it defaults to /login. - attribute login-processing-url {xsd:token}? -form-login.attlist &= - ## The name of the request parameter which contains the username. Defaults to 'username'. - attribute username-parameter {xsd:token}? -form-login.attlist &= - ## The name of the request parameter which contains the password. Defaults to 'password'. - attribute password-parameter {xsd:token}? -form-login.attlist &= - ## The URL that will be redirected to after successful authentication, if the user's previous action could not be resumed. This generally happens if the user visits a login page without having first requested a secured operation that triggers authentication. If unspecified, defaults to the root of the application. - attribute default-target-url {xsd:token}? -form-login.attlist &= - ## Whether the user should always be redirected to the default-target-url after login. - attribute always-use-default-target {xsd:boolean}? -form-login.attlist &= - ## The URL for the login page. If no login URL is specified, Spring Security will automatically create a login URL at GET /login and a corresponding filter to render that login URL when requested. - attribute login-page {xsd:token}? -form-login.attlist &= - ## The URL for the login failure page. If no login failure URL is specified, Spring Security will automatically create a failure login URL at /login?error and a corresponding filter to render that login failure URL when requested. - attribute authentication-failure-url {xsd:token}? -form-login.attlist &= - ## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful authentication request. Should not be used in combination with default-target-url (or always-use-default-target-url) as the implementation should always deal with navigation to the subsequent destination - attribute authentication-success-handler-ref {xsd:token}? -form-login.attlist &= - ## Reference to an AuthenticationFailureHandler bean which should be used to handle a failed authentication request. Should not be used in combination with authentication-failure-url as the implementation should always deal with navigation to the subsequent destination - attribute authentication-failure-handler-ref {xsd:token}? -form-login.attlist &= - ## Reference to an AuthenticationDetailsSource which will be used by the authentication filter - attribute authentication-details-source-ref {xsd:token}? -form-login.attlist &= - ## The URL for the ForwardAuthenticationFailureHandler - attribute authentication-failure-forward-url {xsd:token}? -form-login.attlist &= - ## The URL for the ForwardAuthenticationSuccessHandler - attribute authentication-success-forward-url {xsd:token}? - -oauth2-login = - ## Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider. - element oauth2-login {oauth2-login.attlist} -oauth2-login.attlist &= - ## Reference to the ClientRegistrationRepository - attribute client-registration-repository-ref {xsd:token}? -oauth2-login.attlist &= - ## Reference to the OAuth2AuthorizedClientRepository - attribute authorized-client-repository-ref {xsd:token}? -oauth2-login.attlist &= - ## Reference to the OAuth2AuthorizedClientService - attribute authorized-client-service-ref {xsd:token}? -oauth2-login.attlist &= - ## Reference to the AuthorizationRequestRepository - attribute authorization-request-repository-ref {xsd:token}? -oauth2-login.attlist &= - ## Reference to the OAuth2AuthorizationRequestResolver - attribute authorization-request-resolver-ref {xsd:token}? -oauth2-login.attlist &= - ## Reference to the authorization RedirectStrategy - attribute authorization-redirect-strategy-ref {xsd:token}? -oauth2-login.attlist &= - ## Reference to the OAuth2AccessTokenResponseClient - attribute access-token-response-client-ref {xsd:token}? -oauth2-login.attlist &= - ## Reference to the GrantedAuthoritiesMapper - attribute user-authorities-mapper-ref {xsd:token}? -oauth2-login.attlist &= - ## Reference to the OAuth2UserService - attribute user-service-ref {xsd:token}? -oauth2-login.attlist &= - ## Reference to the OpenID Connect OAuth2UserService - attribute oidc-user-service-ref {xsd:token}? -oauth2-login.attlist &= - ## The URI where the filter processes authentication requests - attribute login-processing-url {xsd:token}? -oauth2-login.attlist &= - ## The URI to send users to login - attribute login-page {xsd:token}? -oauth2-login.attlist &= - ## Reference to the AuthenticationSuccessHandler - attribute authentication-success-handler-ref {xsd:token}? -oauth2-login.attlist &= - ## Reference to the AuthenticationFailureHandler - attribute authentication-failure-handler-ref {xsd:token}? -oauth2-login.attlist &= - ## Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider - attribute jwt-decoder-factory-ref {xsd:token}? - -oauth2-client = - ## Configures OAuth 2.0 Client support. - element oauth2-client {oauth2-client.attlist, (authorization-code-grant?) } -oauth2-client.attlist &= - ## Reference to the ClientRegistrationRepository - attribute client-registration-repository-ref {xsd:token}? -oauth2-client.attlist &= - ## Reference to the OAuth2AuthorizedClientRepository - attribute authorized-client-repository-ref {xsd:token}? -oauth2-client.attlist &= - ## Reference to the OAuth2AuthorizedClientService - attribute authorized-client-service-ref {xsd:token}? - -authorization-code-grant = - ## Configures OAuth 2.0 Authorization Code Grant. - element authorization-code-grant {authorization-code-grant.attlist, empty} -authorization-code-grant.attlist &= - ## Reference to the AuthorizationRequestRepository - attribute authorization-request-repository-ref {xsd:token}? -authorization-code-grant.attlist &= - ## Reference to the authorization RedirectStrategy - attribute authorization-redirect-strategy-ref {xsd:token}? -authorization-code-grant.attlist &= - ## Reference to the OAuth2AuthorizationRequestResolver - attribute authorization-request-resolver-ref {xsd:token}? -authorization-code-grant.attlist &= - ## Reference to the OAuth2AccessTokenResponseClient - attribute access-token-response-client-ref {xsd:token}? - -client-registrations = - ## Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 Provider. - element client-registrations {client-registration+, provider*} - -client-registration = - ## Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider. - element client-registration {client-registration.attlist} -client-registration.attlist &= - ## The ID that uniquely identifies the client registration. - attribute registration-id {xsd:token} -client-registration.attlist &= - ## The client identifier. - attribute client-id {xsd:token} -client-registration.attlist &= - ## The client secret. - attribute client-secret {xsd:token}? -client-registration.attlist &= - ## The method used to authenticate the client with the provider. The supported values are client_secret_basic, client_secret_post and none (public clients). - attribute client-authentication-method {"client_secret_basic" | "basic" | "client_secret_post" | "post" | "none"}? -client-registration.attlist &= - ## The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials and password. - attribute authorization-grant-type {"authorization_code" | "client_credentials" | "password"}? -client-registration.attlist &= - ## The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client. - attribute redirect-uri {xsd:token}? -client-registration.attlist &= - ## A comma-separated list of scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile. - attribute scope {xsd:token}? -client-registration.attlist &= - ## A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page. - attribute client-name {xsd:token}? -client-registration.attlist &= - ## A reference to the associated provider. May reference a 'provider' element or use one of the common providers (google, github, facebook, okta). - attribute provider-id {xsd:token} - -provider = - ## The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider. - element provider {provider.attlist} -provider.attlist &= - ## The ID that uniquely identifies the provider. - attribute provider-id {xsd:token} -provider.attlist &= - ## The Authorization Endpoint URI for the Authorization Server. - attribute authorization-uri {xsd:token}? -provider.attlist &= - ## The Token Endpoint URI for the Authorization Server. - attribute token-uri {xsd:token}? -provider.attlist &= - ## The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user. - attribute user-info-uri {xsd:token}? -provider.attlist &= - ## The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query. - attribute user-info-authentication-method {"header" | "form" | "query"}? -provider.attlist &= - ## The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user. - attribute user-info-user-name-attribute {xsd:token}? -provider.attlist &= - ## The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response. - attribute jwk-set-uri {xsd:token}? -provider.attlist &= - ## The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider. - attribute issuer-uri {xsd:token}? - -oauth2-resource-server = - ## Configures authentication support as an OAuth 2.0 Resource Server. - element oauth2-resource-server {oauth2-resource-server.attlist, (jwt? & opaque-token?)} -oauth2-resource-server.attlist &= - ## Reference to an AuthenticationManagerResolver - attribute authentication-manager-resolver-ref {xsd:token}? -oauth2-resource-server.attlist &= - ## Reference to a BearerTokenResolver - attribute bearer-token-resolver-ref {xsd:token}? -oauth2-resource-server.attlist &= - ## Reference to a AuthenticationEntryPoint - attribute entry-point-ref {xsd:token}? - -jwt = - ## Configures JWT authentication - element jwt {jwt.attlist} -jwt.attlist &= - ## The URI to use to collect the JWK Set for verifying JWTs - attribute jwk-set-uri {xsd:token}? -jwt.attlist &= - ## Reference to a JwtDecoder - attribute decoder-ref {xsd:token}? -jwt.attlist &= - ## Reference to a Converter - attribute jwt-authentication-converter-ref {xsd:token}? - -opaque-token = - ## Configuration Opaque Token authentication - element opaque-token {opaque-token.attlist} -opaque-token.attlist &= - ## The URI to use to introspect opaque token attributes - attribute introspection-uri {xsd:token}? -opaque-token.attlist &= - ## The Client ID to use to authenticate the introspection request - attribute client-id {xsd:token}? -opaque-token.attlist &= - ## The Client secret to use to authenticate the introspection request - attribute client-secret {xsd:token}? -opaque-token.attlist &= - ## Reference to an OpaqueTokenIntrospector - attribute introspector-ref {xsd:token}? -opaque-token.attlist &= - ## Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful introspection result into an Authentication. - attribute authentication-converter-ref {xsd:token}? - -saml2-login = - ## Configures authentication support for SAML 2.0 Login - element saml2-login {saml2-login.attlist} -saml2-login.attlist &= - ## Reference to the RelyingPartyRegistrationRepository - attribute relying-party-registration-repository-ref {xsd:token}? -saml2-login.attlist &= - ## Reference to the Saml2AuthenticationRequestRepository - attribute authentication-request-repository-ref {xsd:token}? -saml2-login.attlist &= - ## Reference to the Saml2AuthenticationRequestResolver - attribute authentication-request-resolver-ref {xsd:token}? -saml2-login.attlist &= - ## Reference to the AuthenticationConverter - attribute authentication-converter-ref {xsd:token}? -saml2-login.attlist &= - ## The URI where the filter processes authentication requests - attribute login-processing-url {xsd:token}? -saml2-login.attlist &= - ## The URI to send users to login - attribute login-page {xsd:token}? -saml2-login.attlist &= - ## Reference to the AuthenticationSuccessHandler - attribute authentication-success-handler-ref {xsd:token}? -saml2-login.attlist &= - ## Reference to the AuthenticationFailureHandler - attribute authentication-failure-handler-ref {xsd:token}? -saml2-login.attlist &= - ## Reference to the AuthenticationManager - attribute authentication-manager-ref {xsd:token}? - -saml2-logout = - ## Configures SAML 2.0 Single Logout support - element saml2-logout {saml2-logout.attlist} -saml2-logout.attlist &= - ## The URL by which the relying or asserting party can trigger logout - attribute logout-url {xsd:token}? -saml2-logout.attlist &= - ## The URL by which the asserting party can send a SAML 2.0 Logout Request - attribute logout-request-url {xsd:token}? -saml2-logout.attlist &= - ## The URL by which the asserting party can send a SAML 2.0 Logout Response - attribute logout-response-url {xsd:token}? -saml2-logout.attlist &= - ## Reference to the RelyingPartyRegistrationRepository - attribute relying-party-registration-repository-ref {xsd:token}? -saml2-logout.attlist &= - ## Reference to the Saml2LogoutRequestValidator - attribute logout-request-validator-ref {xsd:token}? -saml2-logout.attlist &= - ## Reference to the Saml2LogoutRequestResolver - attribute logout-request-resolver-ref {xsd:token}? -saml2-logout.attlist &= - ## Reference to the Saml2LogoutRequestRepository - attribute logout-request-repository-ref {xsd:token}? -saml2-logout.attlist &= - ## Reference to the Saml2LogoutResponseValidator - attribute logout-response-validator-ref {xsd:token}? -saml2-logout.attlist &= - ## Reference to the Saml2LogoutResponseResolver - attribute logout-response-resolver-ref {xsd:token}? - -relying-party-registrations = - ## Container element for relying party(ies) registered with a SAML 2.0 identity provider - element relying-party-registrations {relying-party-registration+, asserting-party*} - -relying-party-registration = - ## Represents a relying party registered with a SAML 2.0 identity provider - element relying-party-registration {relying-party-registration.attlist, signing-credential*, decryption-credential*} -relying-party-registration.attlist &= - ## The ID that uniquely identifies the relying party registration. - attribute registration-id {xsd:token} -relying-party-registration.attlist &= - ## The location of the Identity Provider's metadata. - attribute metadata-location {xsd:token}? -relying-party-registration.attlist &= - ## The relying party's EntityID - attribute entity-id {xsd:token}? -relying-party-registration.attlist &= - ## The Assertion Consumer Service Location - attribute assertion-consumer-service-location {xsd:token}? -relying-party-registration.attlist &= - ## The Assertion Consumer Service Binding - attribute assertion-consumer-service-binding {xsd:token}? -relying-party-registration.attlist &= - ## A reference to the associated asserting party. - attribute asserting-party-id {xsd:token}? -relying-party-registration.attlist &= - ## The relying party SingleLogoutService Location - attribute single-logout-service-location {xsd:token}? -relying-party-registration.attlist &= - ## The relying party SingleLogoutService Response Location - attribute single-logout-service-response-location {xsd:token}? -relying-party-registration.attlist &= - ## The relying party SingleLogoutService Binding - attribute single-logout-service-binding {xsd:token}? - -signing-credential = - ## The relying party's signing credential - element signing-credential {signing-credential.attlist} -signing-credential.attlist &= - ## The private key location - attribute private-key-location {xsd:token} -signing-credential.attlist &= - ## The certificate location - attribute certificate-location {xsd:token} - -decryption-credential = - ## The relying party's decryption credential - element decryption-credential {decryption-credential.attlist} -decryption-credential.attlist &= - ## The private key location - attribute private-key-location {xsd:token} -decryption-credential.attlist &= - ## The certificate location - attribute certificate-location {xsd:token} - -asserting-party = - ## The configuration metadata of the Asserting party - element asserting-party {asserting-party.attlist, verification-credential*, encryption-credential*} -asserting-party.attlist &= - ## A unique identifier of the asserting party. - attribute asserting-party-id {xsd:token} -asserting-party.attlist &= - ## The asserting party's EntityID. - attribute entity-id {xsd:token} -asserting-party.attlist &= - ## Indicates the asserting party's preference that relying parties should sign the AuthnRequest before sending - attribute want-authn-requests-signed {xsd:token}? -asserting-party.attlist &= - ## The SingleSignOnService Location. - attribute single-sign-on-service-location {xsd:token} -asserting-party.attlist &= - ## The SingleSignOnService Binding. - attribute single-sign-on-service-binding {xsd:token}? -asserting-party.attlist &= - ## A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this asserting party, in preference order. - attribute signing-algorithms {xsd:token}? -asserting-party.attlist &= - ## The asserting party SingleLogoutService Location - attribute single-logout-service-location {xsd:token}? -asserting-party.attlist &= - ## The asserting party SingleLogoutService Response Location - attribute single-logout-service-response-location {xsd:token}? -asserting-party.attlist &= - ## The asserting party SingleLogoutService Binding - attribute single-logout-service-binding {xsd:token}? - -verification-credential = - ## The relying party's verification credential - element verification-credential {verification-credential.attlist} -verification-credential.attlist &= - ## The private key location - attribute private-key-location {xsd:token} -verification-credential.attlist &= - ## The certificate location - attribute certificate-location {xsd:token} - -encryption-credential = - ## The asserting party's encryption credential - element encryption-credential {encryption-credential.attlist} -encryption-credential.attlist &= - ## The private key location - attribute private-key-location {xsd:token} -encryption-credential.attlist &= - ## The certificate location - attribute certificate-location {xsd:token} - - -filter-chain-map = - ## Used to explicitly configure a FilterChainProxy instance with a FilterChainMap - element filter-chain-map {filter-chain-map.attlist, filter-chain+} -filter-chain-map.attlist &= - request-matcher? - -filter-chain = - ## Used within to define a specific URL pattern and the list of filters which apply to the URLs matching that pattern. When multiple filter-chain elements are assembled in a list in order to configure a FilterChainProxy, the most specific patterns must be placed at the top of the list, with most general ones at the bottom. - element filter-chain {filter-chain.attlist, empty} -filter-chain.attlist &= - (pattern | request-matcher-ref) -filter-chain.attlist &= - ## A comma separated list of bean names that implement Filter that should be processed for this FilterChain. If the value is none, then no Filters will be used for this FilterChain. - attribute filters {xsd:token} - -pattern = - ## The request URL pattern which will be mapped to the FilterChain. - attribute pattern {xsd:token} -request-matcher-ref = - ## Allows a RequestMatcher instance to be used, as an alternative to pattern-matching. - attribute request-matcher-ref {xsd:token} - -filter-security-metadata-source = - ## Used to explicitly configure a FilterSecurityMetadataSource bean for use with a FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy explicitly, rather than using the element. The intercept-url elements used should only contain pattern, method and access attributes. Any others will result in a configuration error. - element filter-security-metadata-source {fsmds.attlist, intercept-url+} -fsmds.attlist &= - use-expressions? -fsmds.attlist &= - id? -fsmds.attlist &= - request-matcher? - -http-basic = - ## Adds support for basic authentication - element http-basic {http-basic.attlist, empty} - -http-basic.attlist &= - ## Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter. - attribute entry-point-ref {xsd:token}? -http-basic.attlist &= - ## Reference to an AuthenticationDetailsSource which will be used by the authentication filter - attribute authentication-details-source-ref {xsd:token}? - -password-management = - ## Adds support for the password management. - element password-management {password-management.attlist, empty} - -password-management.attlist &= - ## The change password page. Defaults to "/change-password". - attribute change-password-page {xsd:string}? - -session-management = - ## Session-management related functionality is implemented by the addition of a SessionManagementFilter to the filter stack. - element session-management {session-management.attlist, concurrency-control?} - -session-management.attlist &= - ## Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy). - attribute authentication-strategy-explicit-invocation {xsd:boolean}? -session-management.attlist &= - ## Indicates how session fixation protection will be applied when a user authenticates. If set to "none", no protection will be applied. "newSession" will create a new empty session, with only Spring Security-related attributes migrated. "migrateSession" will create a new session and copy all session attributes to the new session. In Servlet 3.1 (Java EE 7) and newer containers, specifying "changeSessionId" will keep the existing session and use the container-supplied session fixation protection (HttpServletRequest#changeSessionId()). Defaults to "changeSessionId" in Servlet 3.1 and newer containers, "migrateSession" in older containers. Throws an exception if "changeSessionId" is used in older containers. - attribute session-fixation-protection {"none" | "newSession" | "migrateSession" | "changeSessionId" }? -session-management.attlist &= - ## The URL to which a user will be redirected if they submit an invalid session indentifier. Typically used to detect session timeouts. - attribute invalid-session-url {xsd:token}? -session-management.attlist &= - ## Allows injection of the InvalidSessionStrategy instance used by the SessionManagementFilter - attribute invalid-session-strategy-ref {xsd:token}? -session-management.attlist &= - ## Allows injection of the SessionAuthenticationStrategy instance used by the SessionManagementFilter - attribute session-authentication-strategy-ref {xsd:token}? -session-management.attlist &= - ## Defines the URL of the error page which should be shown when the SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error code will be returned to the client. Note that this attribute doesn't apply if the error occurs during a form-based login, where the URL for authentication failure will take precedence. - attribute session-authentication-error-url {xsd:token}? - - -concurrency-control = - ## Enables concurrent session control, limiting the number of authenticated sessions a user may have at the same time. - element concurrency-control {concurrency-control.attlist, empty} - -concurrency-control.attlist &= - ## The maximum number of sessions a single authenticated user can have open at the same time. Defaults to "1". A negative value denotes unlimited sessions. - attribute max-sessions {xsd:token}? -concurrency-control.attlist &= - ## The URL a user will be redirected to if they attempt to use a session which has been "expired" because they have logged in again. - attribute expired-url {xsd:token}? -concurrency-control.attlist &= - ## Allows injection of the SessionInformationExpiredStrategy instance used by the ConcurrentSessionFilter - attribute expired-session-strategy-ref {xsd:token}? -concurrency-control.attlist &= - ## Specifies that an unauthorized error should be reported when a user attempts to login when they already have the maximum configured sessions open. The default behaviour is to expire the original session. If the session-authentication-error-url attribute is set on the session-management URL, the user will be redirected to this URL. - attribute error-if-maximum-exceeded {xsd:boolean}? -concurrency-control.attlist &= - ## Allows you to define an alias for the SessionRegistry bean in order to access it in your own configuration. - attribute session-registry-alias {xsd:token}? -concurrency-control.attlist &= - ## Allows you to define an external SessionRegistry bean to be used by the concurrency control setup. - attribute session-registry-ref {xsd:token}? - - -remember-me = - ## Sets up remember-me authentication. If used with the "key" attribute (or no attributes) the cookie-only implementation will be used. Specifying "token-repository-ref" or "remember-me-data-source-ref" will use the more secure, persisten token approach. - element remember-me {remember-me.attlist} -remember-me.attlist &= - ## The "key" used to identify cookies from a specific token-based remember-me application. You should set this to a unique value for your application. If unset, it will default to a random value generated by SecureRandom. - attribute key {xsd:token}? - -remember-me.attlist &= - (token-repository-ref | remember-me-data-source-ref | remember-me-services-ref) - -remember-me.attlist &= - user-service-ref? - -remember-me.attlist &= - ## Exports the internally defined RememberMeServices as a bean alias, allowing it to be used by other beans in the application context. - attribute services-alias {xsd:token}? - -remember-me.attlist &= - ## Determines whether the "secure" flag will be set on the remember-me cookie. If set to true, the cookie will only be submitted over HTTPS (recommended). By default, secure cookies will be used if the request is made on a secure connection. - attribute use-secure-cookie {xsd:boolean}? - -remember-me.attlist &= - ## The period (in seconds) for which the remember-me cookie should be valid. - attribute token-validity-seconds {xsd:string}? - -remember-me.attlist &= - ## Reference to an AuthenticationSuccessHandler bean which should be used to handle a successful remember-me authentication. - attribute authentication-success-handler-ref {xsd:token}? -remember-me.attlist &= - ## The name of the request parameter which toggles remember-me authentication. Defaults to 'remember-me'. - attribute remember-me-parameter {xsd:token}? -remember-me.attlist &= - ## The name of cookie which store the token for remember-me authentication. Defaults to 'remember-me'. - attribute remember-me-cookie {xsd:token}? - -token-repository-ref = - ## Reference to a PersistentTokenRepository bean for use with the persistent token remember-me implementation. - attribute token-repository-ref {xsd:token} -remember-me-services-ref = - ## Allows a custom implementation of RememberMeServices to be used. Note that this implementation should return RememberMeAuthenticationToken instances with the same "key" value as specified in the remember-me element. Alternatively it should register its own AuthenticationProvider. It should also implement the LogoutHandler interface, which will be invoked when a user logs out. Typically the remember-me cookie would be removed on logout. - attribute services-ref {xsd:token}? -remember-me-data-source-ref = - ## DataSource bean for the database that contains the token repository schema. - data-source-ref - -anonymous = - ## Adds support for automatically granting all anonymous web requests a particular principal identity and a corresponding granted authority. - element anonymous {anonymous.attlist} -anonymous.attlist &= - ## The key shared between the provider and filter. This generally does not need to be set. If unset, it will default to a random value generated by SecureRandom. - attribute key {xsd:token}? -anonymous.attlist &= - ## The username that should be assigned to the anonymous request. This allows the principal to be identified, which may be important for logging and auditing. if unset, defaults to "anonymousUser". - attribute username {xsd:token}? -anonymous.attlist &= - ## The granted authority that should be assigned to the anonymous request. Commonly this is used to assign the anonymous request particular roles, which can subsequently be used in authorization decisions. If unset, defaults to "ROLE_ANONYMOUS". - attribute granted-authority {xsd:token}? -anonymous.attlist &= - ## With the default namespace setup, the anonymous "authentication" facility is automatically enabled. You can disable it using this property. - attribute enabled {xsd:boolean}? - - -port-mappings = - ## Defines the list of mappings between http and https ports for use in redirects - element port-mappings {port-mappings.attlist, port-mapping+} - -port-mappings.attlist &= empty - -port-mapping = - ## Provides a method to map http ports to https ports when forcing a redirect. - element port-mapping {http-port, https-port} - -http-port = - ## The http port to use. - attribute http {xsd:token} - -https-port = - ## The https port to use. - attribute https {xsd:token} - - -x509 = - ## Adds support for X.509 client authentication. - element x509 {x509.attlist} -x509.attlist &= - ## The regular expression used to obtain the username from the certificate's subject. Defaults to matching on the common name using the pattern "CN=(.*?),". - attribute subject-principal-regex {xsd:token}? -x509.attlist &= - ## Explicitly specifies which user-service should be used to load user data for X.509 authenticated clients. If ommitted, the default user-service will be used. - user-service-ref? -x509.attlist &= - ## Reference to an AuthenticationDetailsSource which will be used by the authentication filter - attribute authentication-details-source-ref {xsd:token}? - -jee = - ## Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration with container authentication. - element jee {jee.attlist} -jee.attlist &= - ## A comma-separate list of roles to look for in the incoming HttpServletRequest. - attribute mappable-roles {xsd:token} -jee.attlist &= - ## Explicitly specifies which user-service should be used to load user data for container authenticated clients. If ommitted, the set of mappable-roles will be used to construct the authorities for the user. - user-service-ref? - -authentication-manager = - ## Registers the AuthenticationManager instance and allows its list of AuthenticationProviders to be defined. Also allows you to define an alias to allow you to reference the AuthenticationManager in your own beans. - element authentication-manager {authman.attlist & authentication-provider* & ldap-authentication-provider*} -authman.attlist &= - id? -authman.attlist &= - ## An alias you wish to use for the AuthenticationManager bean (not required it you are using a specific id) - attribute alias {xsd:token}? -authman.attlist &= - ## If set to true, the AuthenticationManger will attempt to clear any credentials data in the returned Authentication object, once the user has been authenticated. - attribute erase-credentials {xsd:boolean}? -authman.attlist &= - ## Use this ObservationRegistry to collect metrics on various parts of the filter chain - attribute observation-registry-ref {xsd:token}? - -authentication-provider = - ## Indicates that the contained user-service should be used as an authentication source. - element authentication-provider {ap.attlist & any-user-service & password-encoder?} -ap.attlist &= - ## Specifies a reference to a separately configured AuthenticationProvider instance which should be registered within the AuthenticationManager. - ref? -ap.attlist &= - ## Specifies a reference to a separately configured UserDetailsService from which to obtain authentication data. - user-service-ref? - -user-service = - ## Creates an in-memory UserDetailsService from a properties file or a list of "user" child elements. Usernames are converted to lower-case internally to allow for case-insensitive lookups, so this should not be used if case-sensitivity is required. - element user-service {id? & (properties-file | (user*))} -properties-file = - ## The location of a Properties file where each line is in the format of username=password,grantedAuthority[,grantedAuthority][,enabled|disabled] - attribute properties {xsd:token}? - -user = - ## Represents a user in the application. - element user {user.attlist, empty} -user.attlist &= - ## The username assigned to the user. - attribute name {xsd:token} -user.attlist &= - ## The password assigned to the user. This may be hashed if the corresponding authentication provider supports hashing (remember to set the "hash" attribute of the "user-service" element). This attribute be omitted in the case where the data will not be used for authentication, but only for accessing authorities. If omitted, the namespace will generate a random value, preventing its accidental use for authentication. Cannot be empty. - attribute password {xsd:string}? -user.attlist &= - ## One of more authorities granted to the user. Separate authorities with a comma (but no space). For example, "ROLE_USER,ROLE_ADMINISTRATOR" - attribute authorities {xsd:token} -user.attlist &= - ## Can be set to "true" to mark an account as locked and unusable. - attribute locked {xsd:boolean}? -user.attlist &= - ## Can be set to "true" to mark an account as disabled and unusable. - attribute disabled {xsd:boolean}? - -jdbc-user-service = - ## Causes creation of a JDBC-based UserDetailsService. - element jdbc-user-service {id? & jdbc-user-service.attlist} -jdbc-user-service.attlist &= - ## The bean ID of the DataSource which provides the required tables. - attribute data-source-ref {xsd:token} -jdbc-user-service.attlist &= - cache-ref? -jdbc-user-service.attlist &= - ## An SQL statement to query a username, password, and enabled status given a username. Default is "select username,password,enabled from users where username = ?" - attribute users-by-username-query {xsd:token}? -jdbc-user-service.attlist &= - ## An SQL statement to query for a user's granted authorities given a username. The default is "select username, authority from authorities where username = ?" - attribute authorities-by-username-query {xsd:token}? -jdbc-user-service.attlist &= - ## An SQL statement to query user's group authorities given a username. The default is "select g.id, g.group_name, ga.authority from groups g, group_members gm, group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id" - attribute group-authorities-by-username-query {xsd:token}? -jdbc-user-service.attlist &= - role-prefix? - -csrf = -## Element for configuration of the CsrfFilter for protection against CSRF. It also updates the default RequestCache to only replay "GET" requests. - element csrf {csrf-options.attlist} -csrf-options.attlist &= - ## Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is enabled). - attribute disabled {xsd:boolean}? -csrf-options.attlist &= - ## The RequestMatcher instance to be used to determine if CSRF should be applied. Default is any HTTP method except "GET", "TRACE", "HEAD", "OPTIONS" - attribute request-matcher-ref { xsd:token }? -csrf-options.attlist &= - ## The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by LazyCsrfTokenRepository. - attribute token-repository-ref { xsd:token }? -csrf-options.attlist &= - ## The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler. - attribute request-handler-ref { xsd:token }? - -headers = -## Element for configuration of the HeaderWritersFilter. Enables easy setting for the X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers. -element headers { headers-options.attlist, (cache-control? & xss-protection? & hsts? & frame-options? & content-type-options? & hpkp? & content-security-policy? & referrer-policy? & feature-policy? & permissions-policy? & cross-origin-opener-policy? & cross-origin-embedder-policy? & cross-origin-resource-policy? & header*)} -headers-options.attlist &= - ## Specifies if the default headers should be disabled. Default false. - attribute defaults-disabled {xsd:token}? -headers-options.attlist &= - ## Specifies if headers should be disabled. Default false. - attribute disabled {xsd:token}? -hsts = - ## Adds support for HTTP Strict Transport Security (HSTS) - element hsts {hsts-options.attlist} -hsts-options.attlist &= - ## Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false. - attribute disabled {xsd:boolean}? -hsts-options.attlist &= - ## Specifies if subdomains should be included. Default true. - attribute include-subdomains {xsd:boolean}? -hsts-options.attlist &= - ## Specifies the maximum amount of time the host should be considered a Known HSTS Host. Default one year. - attribute max-age-seconds {xsd:integer}? -hsts-options.attlist &= - ## The RequestMatcher instance to be used to determine if the header should be set. Default is if HttpServletRequest.isSecure() is true. - attribute request-matcher-ref { xsd:token }? -hsts-options.attlist &= - ## Specifies if preload should be included. Default false. - attribute preload {xsd:boolean}? - -cors = -## Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is specified a HandlerMappingIntrospector is used as the CorsConfigurationSource -element cors { cors-options.attlist } -cors-options.attlist &= - ref? -cors-options.attlist &= - ## Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to use - attribute configuration-source-ref {xsd:token}? - -hpkp = - ## Adds support for HTTP Public Key Pinning (HPKP). - element hpkp {hpkp.pins,hpkp.attlist} -hpkp.pins = - ## The list with pins - element pins {hpkp.pin+} -hpkp.pin = - ## A pin is specified using the base64-encoded SPKI fingerprint as value and the cryptographic hash algorithm as attribute - element pin { - ## The cryptographic hash algorithm - attribute algorithm { xsd:string }?, - text - } -hpkp.attlist &= - ## Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false. - attribute disabled {xsd:boolean}? -hpkp.attlist &= - ## Specifies if subdomains should be included. Default false. - attribute include-subdomains {xsd:boolean}? -hpkp.attlist &= - ## Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days. - attribute max-age-seconds {xsd:integer}? -hpkp.attlist &= - ## Specifies if the browser should only report pin validation failures. Default true. - attribute report-only {xsd:boolean}? -hpkp.attlist &= - ## Specifies the URI to which the browser should report pin validation failures. - attribute report-uri {xsd:string}? - -content-security-policy = - ## Adds support for Content Security Policy (CSP) - element content-security-policy {csp-options.attlist} -csp-options.attlist &= - ## The security policy directive(s) for the Content-Security-Policy header or if report-only is set to true, then the Content-Security-Policy-Report-Only header is used. - attribute policy-directives {xsd:token}? -csp-options.attlist &= - ## Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy violations only. Defaults to false. - attribute report-only {xsd:boolean}? - -referrer-policy = - ## Adds support for Referrer Policy - element referrer-policy {referrer-options.attlist} -referrer-options.attlist &= - ## The policies for the Referrer-Policy header. - attribute policy {"no-referrer","no-referrer-when-downgrade","same-origin","origin","strict-origin","origin-when-cross-origin","strict-origin-when-cross-origin","unsafe-url"}? - -feature-policy = - ## Adds support for Feature Policy - element feature-policy {feature-options.attlist} -feature-options.attlist &= - ## The security policy directive(s) for the Feature-Policy header. - attribute policy-directives {xsd:token}? - -permissions-policy = - ## Adds support for Permissions Policy - element permissions-policy {permissions-options.attlist} -permissions-options.attlist &= - ## The policies for the Permissions-Policy header. - attribute policy {xsd:token}? - -cache-control = - ## Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for every request - element cache-control {cache-control.attlist} -cache-control.attlist &= - ## Specifies if Cache Control should be disabled. Default false. - attribute disabled {xsd:boolean}? - -frame-options = - ## Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options header. - element frame-options {frame-options.attlist,empty} -frame-options.attlist &= - ## If disabled, the X-Frame-Options header will not be included. Default false. - attribute disabled {xsd:boolean}? -frame-options.attlist &= - ## Specify the policy to use for the X-Frame-Options-Header. - attribute policy {"DENY","SAMEORIGIN","ALLOW-FROM"}? -frame-options.attlist &= - ## Specify the strategy to use when ALLOW-FROM is chosen. - attribute strategy {"static","whitelist","regexp"}? -frame-options.attlist &= - ## Specify a reference to the custom AllowFromStrategy to use when ALLOW-FROM is chosen. - ref? -frame-options.attlist &= - ## Specify a value to use for the chosen strategy. - attribute value {xsd:string}? -frame-options.attlist &= - ## Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' based strategy. Default is 'from'. - ## Deprecated ALLOW-FROM is an obsolete directive that no longer works in modern browsers. Instead use - ## Content-Security-Policy with the - ## frame-ancestors - ## directive. - attribute from-parameter {xsd:string}? - - -xss-protection = - ## Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the X-XSS-Protection header. - element xss-protection {xss-protection.attlist,empty} -xss-protection.attlist &= - ## disable the X-XSS-Protection header. Default is 'false' meaning it is enabled. - attribute disabled {xsd:boolean}? -xss-protection.attlist &= - ## Specify the value for the X-Xss-Protection header. Defaults to "0". - attribute header-value {"0"|"1"|"1; mode=block"}? - -content-type-options = - ## Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'. - element content-type-options {content-type-options.attlist, empty} -content-type-options.attlist &= - ## If disabled, the X-Content-Type-Options header will not be included. Default false. - attribute disabled {xsd:boolean}? - -cross-origin-opener-policy = - ## Adds support for Cross-Origin-Opener-Policy header - element cross-origin-opener-policy {cross-origin-opener-policy-options.attlist,empty} -cross-origin-opener-policy-options.attlist &= - ## The policies for the Cross-Origin-Opener-Policy header. - attribute policy {"unsafe-none","same-origin","same-origin-allow-popups"}? - -cross-origin-embedder-policy = - ## Adds support for Cross-Origin-Embedder-Policy header - element cross-origin-embedder-policy {cross-origin-embedder-policy-options.attlist,empty} -cross-origin-embedder-policy-options.attlist &= - ## The policies for the Cross-Origin-Embedder-Policy header. - attribute policy {"unsafe-none","require-corp"}? - -cross-origin-resource-policy = - ## Adds support for Cross-Origin-Resource-Policy header - element cross-origin-resource-policy {cross-origin-resource-policy-options.attlist,empty} -cross-origin-resource-policy-options.attlist &= - ## The policies for the Cross-Origin-Resource-Policy header. - attribute policy {"cross-origin","same-origin","same-site"}? - -header= - ## Add additional headers to the response. - element header {header.attlist} -header.attlist &= - ## The name of the header to add. - attribute name {xsd:token}? -header.attlist &= - ## The value for the header. - attribute value {xsd:token}? -header.attlist &= - ## Reference to a custom HeaderWriter implementation. - ref? - -any-user-service = user-service | jdbc-user-service | ldap-user-service - -custom-filter = - ## Used to indicate that a filter bean declaration should be incorporated into the security filter chain. - element custom-filter {custom-filter.attlist} - -custom-filter.attlist &= - ref - -custom-filter.attlist &= - (after | before | position) - -after = - ## The filter immediately after which the custom-filter should be placed in the chain. This feature will only be needed by advanced users who wish to mix their own filters into the security filter chain and have some knowledge of the standard Spring Security filters. The filter names map to specific Spring Security implementation filters. - attribute after {named-security-filter} -before = - ## The filter immediately before which the custom-filter should be placed in the chain - attribute before {named-security-filter} -position = - ## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter. - attribute position {named-security-filter} - -named-security-filter = "FIRST" | "DISABLE_ENCODE_URL_FILTER" | "FORCE_EAGER_SESSION_FILTER" | "CHANNEL_FILTER" | "SECURITY_CONTEXT_FILTER" | "CONCURRENT_SESSION_FILTER" | "WEB_ASYNC_MANAGER_FILTER" | "HEADERS_FILTER" | "CORS_FILTER" | "SAML2_LOGOUT_REQUEST_FILTER" | "SAML2_LOGOUT_RESPONSE_FILTER" | "CSRF_FILTER" | "SAML2_LOGOUT_FILTER" | "LOGOUT_FILTER" | "OAUTH2_AUTHORIZATION_REQUEST_FILTER" | "SAML2_AUTHENTICATION_REQUEST_FILTER" | "X509_FILTER" | "PRE_AUTH_FILTER" | "CAS_FILTER" | "OAUTH2_LOGIN_FILTER" | "SAML2_AUTHENTICATION_FILTER" | "FORM_LOGIN_FILTER" | "LOGIN_PAGE_FILTER" |"LOGOUT_PAGE_FILTER" | "DIGEST_AUTH_FILTER" | "BEARER_TOKEN_AUTH_FILTER" | "BASIC_AUTH_FILTER" | "REQUEST_CACHE_FILTER" | "SERVLET_API_SUPPORT_FILTER" | "JAAS_API_SUPPORT_FILTER" | "REMEMBER_ME_FILTER" | "ANONYMOUS_FILTER" | "OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER" | "WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER" | "SESSION_MANAGEMENT_FILTER" | "EXCEPTION_TRANSLATION_FILTER" | "FILTER_SECURITY_INTERCEPTOR" | "SWITCH_USER_FILTER" | "LAST" diff --git a/config/src/main/resources/org/springframework/security/config/spring-security-6.2.xsd b/config/src/main/resources/org/springframework/security/config/spring-security-6.2.xsd deleted file mode 100644 index f123ad830a..0000000000 --- a/config/src/main/resources/org/springframework/security/config/spring-security-6.2.xsd +++ /dev/null @@ -1,3812 +0,0 @@ - - - - - - Defines the hashing algorithm used on user passwords. Bcrypt is recommended. - - - - - - - - - - - - - Whether a string should be base64 encoded - - - - - - - - Defines the strategy use for matching incoming requests. Currently the options are 'mvc' - (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions - and 'ciRegex' for case-insensitive regular expressions. - - - - - - - - - - - - - - - - Specifies an IP port number. Used to configure an embedded LDAP server, for example. - - - - - - - - Specifies a URL. - - - - - - - - A bean identifier, used for referring to the bean elsewhere in the context. - - - - - - - - A bean identifier, used for referring to the bean elsewhere in the context. - - - - - - - - Defines a reference to a Spring bean Id. - - - - - - - - Defines a reference to a cache for use with a UserDetailsService. - - - - - - - - A reference to a user-service (or UserDetailsService bean) Id - - - - - - - - A reference to an AuthenticationManager bean - - - - - - - - A reference to a DataSource bean - - - - - - - Enables Spring Security debugging infrastructure. This will provide human-readable - (multi-line) debugging information to monitor requests coming into the security filters. - This may include sensitive information, such as request parameters or headers, and should - only be used in a development environment. - - - - - - - - - Defines a reference to a Spring bean Id. - - - - - - Defines the hashing algorithm used on user passwords. Bcrypt is recommended. - - - - - - - - - - - - - A non-empty string prefix that will be added to role strings loaded from persistent - storage (e.g. "ROLE_"). Use the value "none" for no prefix in cases where the default is - non-empty. - - - - - - - - Enables the use of expressions in the 'access' attributes in <intercept-url> elements - rather than the traditional list of configuration attributes. Defaults to 'true'. If - enabled, each attribute should contain a single boolean expression. If the expression - evaluates to 'true', access will be granted. - - - - - - - Defines an LDAP server location or starts an embedded server. The url indicates the - location of a remote server. If no url is given, an embedded server will be started, - listening on the supplied port number. The port is optional and defaults to 33389. A - Spring LDAP ContextSource bean will be registered for the server with the id supplied. - - - - - - - - - - A bean identifier, used for referring to the bean elsewhere in the context. - - - - - - Specifies a URL. - - - - - - Specifies an IP port number. Used to configure an embedded LDAP server, for example. - - - - - - Username (DN) of the "manager" user identity which will be used to authenticate to a - (non-embedded) LDAP server. If omitted, anonymous access will be used. - - - - - - The password for the manager DN. This is required if the manager-dn is specified. - - - - - - Explicitly specifies an ldif file resource to load into an embedded LDAP server. The - default is classpath*:*.ldiff - - - - - - Optional root suffix for the embedded LDAP server. Default is "dc=springframework,dc=org" - - - - - - Explicitly specifies which embedded ldap server should use. Values are 'apacheds' and - 'unboundid'. By default, it will depends if the library is available in the classpath. - - - - - - - - - - - - - - The optional server to use. If omitted, and a default LDAP server is registered (using - <ldap-server> with no Id), that server will be used. - - - - - - - - Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN - of the user. - - - - - - - - Search base for group membership searches. Defaults to "" (searching from the root). - - - - - - - - The LDAP filter used to search for users (optional). For example "(uid={0})". The - substituted parameter is the user's login name. - - - - - - - - Search base for user searches. Defaults to "". Only used with a 'user-search-filter'. - - - - - - - - The LDAP attribute name which contains the role name which will be used within Spring - Security. Defaults to "cn". - - - - - - - - Allows the objectClass of the user entry to be specified. If set, the framework will - attempt to load standard attributes for the defined class into the returned UserDetails - object - - - - - - - - - - - - - - Allows explicit customization of the loaded user object by specifying a - UserDetailsContextMapper bean which will be called with the context information from the - user's directory entry - - - - - - - This element configures a LdapUserDetailsService which is a combination of a - FilterBasedLdapUserSearch and a DefaultLdapAuthoritiesPopulator. - - - - - - - - - - A bean identifier, used for referring to the bean elsewhere in the context. - - - - - - The optional server to use. If omitted, and a default LDAP server is registered (using - <ldap-server> with no Id), that server will be used. - - - - - - The LDAP filter used to search for users (optional). For example "(uid={0})". The - substituted parameter is the user's login name. - - - - - - Search base for user searches. Defaults to "". Only used with a 'user-search-filter'. - - - - - - Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN - of the user. - - - - - - Search base for group membership searches. Defaults to "" (searching from the root). - - - - - - The LDAP attribute name which contains the role name which will be used within Spring - Security. Defaults to "cn". - - - - - - Defines a reference to a cache for use with a UserDetailsService. - - - - - - A non-empty string prefix that will be added to role strings loaded from persistent - storage (e.g. "ROLE_"). Use the value "none" for no prefix in cases where the default is - non-empty. - - - - - - Allows the objectClass of the user entry to be specified. If set, the framework will - attempt to load standard attributes for the defined class into the returned UserDetails - object - - - - - - - - - - - - Allows explicit customization of the loaded user object by specifying a - UserDetailsContextMapper bean which will be called with the context information from the - user's directory entry - - - - - - - - - The optional server to use. If omitted, and a default LDAP server is registered (using - <ldap-server> with no Id), that server will be used. - - - - - - Search base for user searches. Defaults to "". Only used with a 'user-search-filter'. - - - - - - The LDAP filter used to search for users (optional). For example "(uid={0})". The - substituted parameter is the user's login name. - - - - - - Search base for group membership searches. Defaults to "" (searching from the root). - - - - - - Group search filter. Defaults to (uniqueMember={0}). The substituted parameter is the DN - of the user. - - - - - - The LDAP attribute name which contains the role name which will be used within Spring - Security. Defaults to "cn". - - - - - - A specific pattern used to build the user's DN, for example "uid={0},ou=people". The key - "{0}" must be present and will be substituted with the username. - - - - - - A non-empty string prefix that will be added to role strings loaded from persistent - storage (e.g. "ROLE_"). Use the value "none" for no prefix in cases where the default is - non-empty. - - - - - - Allows the objectClass of the user entry to be specified. If set, the framework will - attempt to load standard attributes for the defined class into the returned UserDetails - object - - - - - - - - - - - - Allows explicit customization of the loaded user object by specifying a - UserDetailsContextMapper bean which will be called with the context information from the - user's directory entry - - - - - - - - - The attribute in the directory which contains the user password. Defaults to - "userPassword". - - - - - - Defines the hashing algorithm used on user passwords. Bcrypt is recommended. - - - - - - - - - - - - Can be used inside a bean definition to add a security interceptor to the bean and set up - access configuration attributes for the bean's methods - - - - - - - Defines a protected method and the access control configuration attributes that apply to - it. We strongly advise you NOT to mix "protect" declarations with any services provided - "global-method-security". - - - - - - - - - - - - - - Optional AccessDecisionManager bean ID to be used by the created method security - interceptor. - - - - - - Use the AuthorizationManager API instead of AccessDecisionManager (defaults to true) - - - - - - Use this AuthorizationManager instead of the default (supercedes - use-authorization-manager) - - - - - - - - - A method name - - - - - - Access configuration attributes list that applies to the method, e.g. "ROLE_A,ROLE_B". - - - - - - - Creates a MethodSecurityMetadataSource instance - - - - - - - Defines a protected method and the access control configuration attributes that apply to - it. We strongly advise you NOT to mix "protect" declarations with any services provided - "global-method-security". - - - - - - - - - - - - - - A bean identifier, used for referring to the bean elsewhere in the context. - - - - - - Enables the use of expressions in the 'access' attributes in <intercept-url> elements - rather than the traditional list of configuration attributes. Defaults to 'true'. If - enabled, each attribute should contain a single boolean expression. If the expression - evaluates to 'true', access will be granted. - - - - - - - Provides method security for all beans registered in the Spring application context. - Specifically, beans will be scanned for matches with Spring Security annotations. Where - there is a match, the beans will automatically be proxied and security authorization - applied to the methods accordingly. Interceptors are invoked in the order specified in - AuthorizationInterceptorsOrder. Use can create your own interceptors using Spring AOP. - Also, annotation-based interception can be overridden by expressions listed in - <protect-pointcut> elements. - - - - - - - Defines the SecurityExpressionHandler instance which will be used if expression-based - access-control is enabled. A default implementation (with no ACL support) will be used if - not supplied. - - - - - - - - - Defines a protected pointcut and the access control configuration attributes that apply to - it. Every bean registered in the Spring application context that provides a method that - matches the pointcut will receive security authorization. - - - - - - - - - - - - - - Specifies whether the use of Spring Security's pre and post invocation annotations - (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this - application context. Defaults to "true". - - - - - - Specifies whether the use of Spring Security's @Secured annotations should be enabled for - this application context. Defaults to "false". - - - - - - Specifies whether JSR-250 style attributes are to be used (for example "RolesAllowed"). - This will require the javax.annotation.security classes on the classpath. Defaults to - "false". - - - - - - If true, class-based proxying will be used instead of interface-based proxying. - - - - - - If set to aspectj, then use AspectJ to intercept method invocation - - - - - - - - - - - Specifies the security context holder strategy to use, by default uses a ThreadLocal-based - strategy - - - - - - Use this ObservationRegistry to collect metrics on various parts of the filter chain - - - - - - - Provides method security for all beans registered in the Spring application context. - Specifically, beans will be scanned for matches with the ordered list of - "protect-pointcut" sub-elements, Spring Security annotations and/or. Where there is a - match, the beans will automatically be proxied and security authorization applied to the - methods accordingly. If you use and enable all four sources of method security metadata - (ie "protect-pointcut" declarations, expression annotations, @Secured and also JSR250 - security annotations), the metadata sources will be queried in that order. In practical - terms, this enables you to use XML to override method security metadata expressed in - annotations. If using annotations, the order of precedence is EL-based (@PreAuthorize - etc.), @Secured and finally JSR-250. - - - - - - - - Allows the default expression-based mechanism for handling Spring Security's pre and post - invocation annotations (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) to be - replace entirely. Only applies if these annotations are enabled. - - - - - - - Defines the PrePostInvocationAttributeFactory instance which is used to generate pre and - post invocation metadata from the annotated methods. - - - - - - - - - Customizes the PreInvocationAuthorizationAdviceVoter with the ref as the - PreInvocationAuthorizationAdviceVoter for the <pre-post-annotation-handling> element. - - - - - - - - - Customizes the PostInvocationAdviceProvider with the ref as the - PostInvocationAuthorizationAdvice for the <pre-post-annotation-handling> element. - - - - - - - - - - - - Defines the SecurityExpressionHandler instance which will be used if expression-based - access-control is enabled. A default implementation (with no ACL support) will be used if - not supplied. - - - - - - - - - - Defines a protected pointcut and the access control configuration attributes that apply to - it. Every bean registered in the Spring application context that provides a method that - matches the pointcut will receive security authorization. - - - - - - - - - Allows addition of extra AfterInvocationProvider beans which should be called by the - MethodSecurityInterceptor created by global-method-security. - - - - - - - - - - - - - - Specifies whether the use of Spring Security's pre and post invocation annotations - (@PreFilter, @PreAuthorize, @PostFilter, @PostAuthorize) should be enabled for this - application context. Defaults to "disabled". - - - - - - - - - - - - Specifies whether the use of Spring Security's @Secured annotations should be enabled for - this application context. Defaults to "disabled". - - - - - - - - - - - - Specifies whether JSR-250 style attributes are to be used (for example "RolesAllowed"). - This will require the javax.annotation.security classes on the classpath. Defaults to - "disabled". - - - - - - - - - - - - Optional AccessDecisionManager bean ID to override the default used for method security. - - - - - - Optional RunAsmanager implementation which will be used by the configured - MethodSecurityInterceptor - - - - - - Allows the advice "order" to be set for the method security interceptor. - - - - - - If true, class based proxying will be used instead of interface based proxying. - - - - - - Can be used to specify that AspectJ should be used instead of the default Spring AOP. If - set, secured classes must be woven with the AnnotationSecurityAspect from the - spring-security-aspects module. - - - - - - - - - - - An external MethodSecurityMetadataSource instance can be supplied which will take priority - over other sources (such as the default annotations). - - - - - - A reference to an AuthenticationManager bean - - - - - - - - - - - - - - - An AspectJ expression, including the 'execution' keyword. For example, 'execution(int - com.foo.TargetObject.countLength(String))' (without the quotes). - - - - - - Access configuration attributes list that applies to all methods matching the pointcut, - e.g. "ROLE_A,ROLE_B" - - - - - - - Allows securing a Message Broker. There are two modes. If no id is specified: ensures that - any SimpAnnotationMethodMessageHandler has the AuthenticationPrincipalArgumentResolver - registered as a custom argument resolver; ensures that the - SecurityContextChannelInterceptor is automatically registered for the - clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the - clientInboundChannel. If the id is specified, creates a ChannelSecurityInterceptor that - can be manually registered with the clientInboundChannel. - - - - - - - - Defines the SecurityExpressionHandler instance which will be used if expression-based - access-control is enabled. A default implementation (with no ACL support) will be used if - not supplied. - - - - - - - - - - - - - - A bean identifier, used for referring to the bean elsewhere in the context. If specified, - explicit configuration within clientInboundChannel is required. If not specified, ensures - that any SimpAnnotationMethodMessageHandler has the - AuthenticationPrincipalArgumentResolver registered as a custom argument resolver; ensures - that the SecurityContextChannelInterceptor is automatically registered for the - clientInboundChannel; and that a ChannelSecurityInterceptor is registered with the - clientInboundChannel. - - - - - - Disables the requirement for CSRF token to be present in the Stomp headers (default - false). Changing the default is useful if it is necessary to allow other origins to make - SockJS connections. - - - - - - Use this AuthorizationManager instead of deriving one from <intercept-message> elements - - - - - - Use AuthorizationManager API instead of SecurityMetadatasource (defaults to true) - - - - - - Use this SecurityContextHolderStrategy (note only supported in conjunction with the - AuthorizationManager API) - - - - - - - Creates an authorization rule for a websocket message. - - - - - - - - - - The destination ant pattern which will be mapped to the access attribute. For example, /** - matches any message with a destination, /admin/** matches any message that has a - destination that starts with admin. - - - - - - The access configuration attributes that apply for the configured message. For example, - permitAll grants access to anyone, hasRole('ROLE_ADMIN') requires the user have the role - 'ROLE_ADMIN'. - - - - - - The type of message to match on. Valid values are defined in SimpMessageType (i.e. - CONNECT, CONNECT_ACK, HEARTBEAT, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, - DISCONNECT_ACK, OTHER). - - - - - - - - - - - - - - - - - - - - Allows a custom instance of HttpFirewall to be injected into the FilterChainProxy created - by the namespace. - - - - - - - - - Container element for HTTP security configuration. Multiple elements can now be defined, - each with a specific pattern to which the enclosed security configuration applies. A - pattern can also be configured to bypass Spring Security's filters completely by setting - the "security" attribute to "none". - - - - - - - Specifies the access attributes and/or filter list for a particular set of URLs. - - - - - - - - - Defines the access-denied strategy that should be used. An access denied page can be - defined or a reference to an AccessDeniedHandler instance. - - - - - - - - - Sets up a form login configuration for authentication with a username and password - - - - - - - - - - - - Configures authentication support for SAML 2.0 Login - - - - - - - - - Configures SAML 2.0 Single Logout support - - - - - - - - - Adds support for X.509 client authentication. - - - - - - - - - - Adds support for basic authentication - - - - - - - - - Incorporates a logout processing filter. Most web applications require a logout filter, - although you may not require one if you write a controller to provider similar logic. - - - - - - - - - - Session-management related functionality is implemented by the addition of a - SessionManagementFilter to the filter stack. - - - - - - - Enables concurrent session control, limiting the number of authenticated sessions a user - may have at the same time. - - - - - - - - - - - - - Sets up remember-me authentication. If used with the "key" attribute (or no attributes) - the cookie-only implementation will be used. Specifying "token-repository-ref" or - "remember-me-data-source-ref" will use the more secure, persisten token approach. - - - - - - - - - Adds support for automatically granting all anonymous web requests a particular principal - identity and a corresponding granted authority. - - - - - - - - - Defines the list of mappings between http and https ports for use in redirects - - - - - - - Provides a method to map http ports to https ports when forcing a redirect. - - - - - - - - - - - - - - - Defines the SecurityExpressionHandler instance which will be used if expression-based - access-control is enabled. A default implementation (with no ACL support) will be used if - not supplied. - - - - - - - - - - - - - - - - - The request URL pattern which will be mapped to the filter chain created by this <http> - element. If omitted, the filter chain will match all requests. - - - - - - When set to 'none', requests matching the pattern attribute will be ignored by Spring - Security. No security filters will be applied and no SecurityContext will be available. If - set, the <http> element must be empty, with no children. - - - - - - - - - - - Allows a RequestMatcher instance to be used, as an alternative to pattern-matching. - - - - - - A legacy attribute which automatically registers a login form, BASIC authentication and a - logout URL and logout services. If unspecified, defaults to "false". We'd recommend you - avoid using this and instead explicitly configure the services you require. - - - - - - Enables the use of expressions in the 'access' attributes in <intercept-url> elements - rather than the traditional list of configuration attributes. Defaults to 'true'. If - enabled, each attribute should contain a single boolean expression. If the expression - evaluates to 'true', access will be granted. - - - - - - A reference to a SecurityContextHolderStrategy bean. This can be used to customize how the - SecurityContextHolder is stored during a request - - - - - - Controls the eagerness with which an HTTP session is created by Spring Security classes. - If not set, defaults to "ifRequired". If "stateless" is used, this implies that the - application guarantees that it will not create a session. This differs from the use of - "never" which means that Spring Security will not create a session, but will make use of - one if the application does. - - - - - - - - - - - - - - A reference to a SecurityContextRepository bean. This can be used to customize how the - SecurityContext is stored between requests. - - - - - - Optional attribute that specifies that the SecurityContext should require explicit saving - rather than being synchronized from the SecurityContextHolder. Defaults to "true". - - - - - - Defines the strategy use for matching incoming requests. Currently the options are 'mvc' - (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions - and 'ciRegex' for case-insensitive regular expressions. - - - - - - - - - - - - - - Provides versions of HttpServletRequest security methods such as isUserInRole() and - getPrincipal() which are implemented by accessing the Spring SecurityContext. Defaults to - "true". - - - - - - If available, runs the request as the Subject acquired from the JaasAuthenticationToken. - Defaults to "false". - - - - - - Use AuthorizationManager API instead of SecurityMetadataSource (defaults to true) - - - - - - Use this AuthorizationManager instead of deriving one from <intercept-url> elements - - - - - - Optional attribute specifying the ID of the AccessDecisionManager implementation which - should be used for authorizing HTTP requests. - - - - - - Optional attribute specifying the realm name that will be used for all authentication - features that require a realm name (eg BASIC and Digest authentication). If unspecified, - defaults to "Spring Security Application". - - - - - - Allows a customized AuthenticationEntryPoint to be set on the ExceptionTranslationFilter. - - - - - - Corresponds to the observeOncePerRequest property of FilterSecurityInterceptor. Defaults - to "false" - - - - - - Corresponds to the shouldFilterAllDispatcherTypes property of AuthorizationFilter. Do not - work when use-authorization-manager=false. Defaults to "true". - - - - - - Prevents the jsessionid parameter from being added to rendered URLs. Defaults to "true" - (rewriting is disabled). - - - - - - A bean identifier, used for referring to the bean elsewhere in the context. - - - - - - A reference to an AuthenticationManager bean - - - - - - Use this ObservationRegistry to collect metrics on various parts of the filter chain - - - - - - - - - Defines a reference to a Spring bean Id. - - - - - - The access denied page that an authenticated user will be redirected to if they request a - page which they don't have the authority to access. - - - - - - - - The access denied page that an authenticated user will be redirected to if they request a - page which they don't have the authority to access. - - - - - - - - - The request URL pattern which will be mapped to the FilterChain. - - - - - - Allows a RequestMatcher instance to be used, as an alternative to pattern-matching. - - - - - - The access configuration attributes that apply for the configured path. - - - - - - The HTTP Method for which the access configuration attributes should apply. If not - specified, the attributes will apply to any method. - - - - - - - - - - - - - - - - - - Used to specify that a URL must be accessed over http or https, or that there is no - preference. The value should be "http", "https" or "any", respectively. - - - - - - The path to the servlet. This attribute is only applicable when 'request-matcher' is - 'mvc'. In addition, the value is only required in the following 2 use cases: 1) There are - 2 or more HttpServlet's registered in the ServletContext that have mappings starting with - '/' and are different; 2) The pattern starts with the same value of a registered - HttpServlet path, excluding the default (root) HttpServlet '/'. - - - - - - - - - Specifies the URL that will cause a logout. Spring Security will initialize a filter that - responds to this particular URL. Defaults to /logout if unspecified. - - - - - - Specifies the URL to display once the user has logged out. If not specified, defaults to - <form-login-login-page>/?logout (i.e. /login?logout). - - - - - - Specifies whether a logout also causes HttpSession invalidation, which is generally - desirable. If unspecified, defaults to true. - - - - - - A reference to a LogoutSuccessHandler implementation which will be used to determine the - destination to which the user is taken after logging out. - - - - - - A comma-separated list of the names of cookies which should be deleted when the user logs - out - - - - - - - Allow the RequestCache used for saving requests during the login process to be set - - - - - - - - - - - The URL that the login form is posted to. If unspecified, it defaults to /login. - - - - - - The name of the request parameter which contains the username. Defaults to 'username'. - - - - - - The name of the request parameter which contains the password. Defaults to 'password'. - - - - - - The URL that will be redirected to after successful authentication, if the user's previous - action could not be resumed. This generally happens if the user visits a login page - without having first requested a secured operation that triggers authentication. If - unspecified, defaults to the root of the application. - - - - - - Whether the user should always be redirected to the default-target-url after login. - - - - - - The URL for the login page. If no login URL is specified, Spring Security will - automatically create a login URL at GET /login and a corresponding filter to render that - login URL when requested. - - - - - - The URL for the login failure page. If no login failure URL is specified, Spring Security - will automatically create a failure login URL at /login?error and a corresponding filter - to render that login failure URL when requested. - - - - - - Reference to an AuthenticationSuccessHandler bean which should be used to handle a - successful authentication request. Should not be used in combination with - default-target-url (or always-use-default-target-url) as the implementation should always - deal with navigation to the subsequent destination - - - - - - Reference to an AuthenticationFailureHandler bean which should be used to handle a failed - authentication request. Should not be used in combination with authentication-failure-url - as the implementation should always deal with navigation to the subsequent destination - - - - - - Reference to an AuthenticationDetailsSource which will be used by the authentication - filter - - - - - - The URL for the ForwardAuthenticationFailureHandler - - - - - - The URL for the ForwardAuthenticationSuccessHandler - - - - - - - Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0 Provider. - - - - - - - - - - Reference to the ClientRegistrationRepository - - - - - - Reference to the OAuth2AuthorizedClientRepository - - - - - - Reference to the OAuth2AuthorizedClientService - - - - - - Reference to the AuthorizationRequestRepository - - - - - - Reference to the OAuth2AuthorizationRequestResolver - - - - - - Reference to the authorization RedirectStrategy - - - - - - Reference to the OAuth2AccessTokenResponseClient - - - - - - Reference to the GrantedAuthoritiesMapper - - - - - - Reference to the OAuth2UserService - - - - - - Reference to the OpenID Connect OAuth2UserService - - - - - - The URI where the filter processes authentication requests - - - - - - The URI to send users to login - - - - - - Reference to the AuthenticationSuccessHandler - - - - - - Reference to the AuthenticationFailureHandler - - - - - - Reference to the JwtDecoderFactory used by OidcAuthorizationCodeAuthenticationProvider - - - - - - - Configures OAuth 2.0 Client support. - - - - - - - - - - - - - Reference to the ClientRegistrationRepository - - - - - - Reference to the OAuth2AuthorizedClientRepository - - - - - - Reference to the OAuth2AuthorizedClientService - - - - - - - Configures OAuth 2.0 Authorization Code Grant. - - - - - - - - - - Reference to the AuthorizationRequestRepository - - - - - - Reference to the authorization RedirectStrategy - - - - - - Reference to the OAuth2AuthorizationRequestResolver - - - - - - Reference to the OAuth2AccessTokenResponseClient - - - - - - - Container element for client(s) registered with an OAuth 2.0 or OpenID Connect 1.0 - Provider. - - - - - - - - - - - - Represents a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider. - - - - - - - - - - The ID that uniquely identifies the client registration. - - - - - - The client identifier. - - - - - - The client secret. - - - - - - The method used to authenticate the client with the provider. The supported values are - client_secret_basic, client_secret_post and none (public clients). - - - - - - - - - - - - - - - The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The - supported values are authorization_code, client_credentials and password. - - - - - - - - - - - - - The client’s registered redirect URI that the Authorization Server redirects the - end-user’s user-agent to after the end-user has authenticated and authorized access to the - client. - - - - - - A comma-separated list of scope(s) requested by the client during the Authorization - Request flow, such as openid, email, or profile. - - - - - - A descriptive name used for the client. The name may be used in certain scenarios, such as - when displaying the name of the client in the auto-generated login page. - - - - - - A reference to the associated provider. May reference a 'provider' element or use one of - the common providers (google, github, facebook, okta). - - - - - - - The configuration information for an OAuth 2.0 or OpenID Connect 1.0 Provider. - - - - - - - - - - The ID that uniquely identifies the provider. - - - - - - The Authorization Endpoint URI for the Authorization Server. - - - - - - The Token Endpoint URI for the Authorization Server. - - - - - - The UserInfo Endpoint URI used to access the claims/attributes of the authenticated - end-user. - - - - - - The authentication method used when sending the access token to the UserInfo Endpoint. The - supported values are header, form and query. - - - - - - - - - - - - - The name of the attribute returned in the UserInfo Response that references the Name or - Identifier of the end-user. - - - - - - The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which - contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID - Token and optionally the UserInfo Response. - - - - - - The URI used to discover the configuration information for an OAuth 2.0 or OpenID Connect - 1.0 Provider. - - - - - - - Configures authentication support as an OAuth 2.0 Resource Server. - - - - - - - - - - - - - - Reference to an AuthenticationManagerResolver - - - - - - Reference to a BearerTokenResolver - - - - - - Reference to a AuthenticationEntryPoint - - - - - - - Configures JWT authentication - - - - - - - - - - The URI to use to collect the JWK Set for verifying JWTs - - - - - - Reference to a JwtDecoder - - - - - - Reference to a Converter<Jwt, AbstractAuthenticationToken> - - - - - - - Configuration Opaque Token authentication - - - - - - - - - - The URI to use to introspect opaque token attributes - - - - - - The Client ID to use to authenticate the introspection request - - - - - - The Client secret to use to authenticate the introspection request - - - - - - Reference to an OpaqueTokenIntrospector - - - - - - Reference to an OpaqueTokenAuthenticationConverter responsible for converting successful - introspection result into an Authentication. - - - - - - - - - Reference to the RelyingPartyRegistrationRepository - - - - - - Reference to the Saml2AuthenticationRequestRepository - - - - - - Reference to the Saml2AuthenticationRequestResolver - - - - - - Reference to the AuthenticationConverter - - - - - - The URI where the filter processes authentication requests - - - - - - The URI to send users to login - - - - - - Reference to the AuthenticationSuccessHandler - - - - - - Reference to the AuthenticationFailureHandler - - - - - - Reference to the AuthenticationManager - - - - - - - - - The URL by which the relying or asserting party can trigger logout - - - - - - The URL by which the asserting party can send a SAML 2.0 Logout Request - - - - - - The URL by which the asserting party can send a SAML 2.0 Logout Response - - - - - - Reference to the RelyingPartyRegistrationRepository - - - - - - Reference to the Saml2LogoutRequestValidator - - - - - - Reference to the Saml2LogoutRequestResolver - - - - - - Reference to the Saml2LogoutRequestRepository - - - - - - Reference to the Saml2LogoutResponseValidator - - - - - - Reference to the Saml2LogoutResponseResolver - - - - - - - Container element for relying party(ies) registered with a SAML 2.0 identity provider - - - - - - - - - - - - Represents a relying party registered with a SAML 2.0 identity provider - - - - - - - - - - - - - - The ID that uniquely identifies the relying party registration. - - - - - - The location of the Identity Provider's metadata. - - - - - - The relying party's EntityID - - - - - - The Assertion Consumer Service Location - - - - - - The Assertion Consumer Service Binding - - - - - - A reference to the associated asserting party. - - - - - - The relying party <a - href="https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7">SingleLogoutService - Location</a> - - - - - - The relying party <a - href="https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7">SingleLogoutService - Response Location</a> - - - - - - The relying party <a - href="https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7">SingleLogoutService - Binding</a> - - - - - - - The relying party's signing credential - - - - - - - - - - The private key location - - - - - - The certificate location - - - - - - - The relying party's decryption credential - - - - - - - - - - The private key location - - - - - - The certificate location - - - - - - - The configuration metadata of the Asserting party - - - - - - - - - - - - - - A unique identifier of the asserting party. - - - - - - The asserting party's EntityID. - - - - - - Indicates the asserting party's preference that relying parties should sign the - AuthnRequest before sending - - - - - - The <a - href="https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint">SingleSignOnService</a> - Location. - - - - - - The <a - href="https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.5%20Endpoint">SingleSignOnService</a> - Binding. - - - - - - A comma separated list of org.opensaml.saml.ext.saml2alg.SigningMethod Algorithms for this - asserting party, in preference order. - - - - - - The asserting party <a - href="https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7">SingleLogoutService - Location</a> - - - - - - The asserting party <a - href="https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7">SingleLogoutService - Response Location</a> - - - - - - The asserting party <a - href="https://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf#page=7">SingleLogoutService - Binding</a> - - - - - - - The relying party's verification credential - - - - - - - - - - The private key location - - - - - - The certificate location - - - - - - - The asserting party's encryption credential - - - - - - - - - - The private key location - - - - - - The certificate location - - - - - - - Used to explicitly configure a FilterChainProxy instance with a FilterChainMap - - - - - - - - - - - - - Defines the strategy use for matching incoming requests. Currently the options are 'mvc' - (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions - and 'ciRegex' for case-insensitive regular expressions. - - - - - - - - - - - - - - - Used within to define a specific URL pattern and the list of filters which apply to the - URLs matching that pattern. When multiple filter-chain elements are assembled in a list in - order to configure a FilterChainProxy, the most specific patterns must be placed at the - top of the list, with most general ones at the bottom. - - - - - - - - - - The request URL pattern which will be mapped to the FilterChain. - - - - - - Allows a RequestMatcher instance to be used, as an alternative to pattern-matching. - - - - - - A comma separated list of bean names that implement Filter that should be processed for - this FilterChain. If the value is none, then no Filters will be used for this FilterChain. - - - - - - - - The request URL pattern which will be mapped to the FilterChain. - - - - - - - - Allows a RequestMatcher instance to be used, as an alternative to pattern-matching. - - - - - - - Used to explicitly configure a FilterSecurityMetadataSource bean for use with a - FilterSecurityInterceptor. Usually only needed if you are configuring a FilterChainProxy - explicitly, rather than using the <http> element. The intercept-url elements used should - only contain pattern, method and access attributes. Any others will result in a - configuration error. - - - - - - - Specifies the access attributes and/or filter list for a particular set of URLs. - - - - - - - - - - - - - - Enables the use of expressions in the 'access' attributes in <intercept-url> elements - rather than the traditional list of configuration attributes. Defaults to 'true'. If - enabled, each attribute should contain a single boolean expression. If the expression - evaluates to 'true', access will be granted. - - - - - - A bean identifier, used for referring to the bean elsewhere in the context. - - - - - - Defines the strategy use for matching incoming requests. Currently the options are 'mvc' - (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions - and 'ciRegex' for case-insensitive regular expressions. - - - - - - - - - - - - - - - - - Sets the AuthenticationEntryPoint which is used by the BasicAuthenticationFilter. - - - - - - Reference to an AuthenticationDetailsSource which will be used by the authentication - filter - - - - - - - Adds support for the password management. - - - - - - - - - - The change password page. Defaults to "/change-password". - - - - - - - - - Specifies that SessionAuthenticationStrategy must be explicitly invoked. Default false - (i.e. SessionManagementFilter will implicitly invoke SessionAuthenticationStrategy). - - - - - - Indicates how session fixation protection will be applied when a user authenticates. If - set to "none", no protection will be applied. "newSession" will create a new empty - session, with only Spring Security-related attributes migrated. "migrateSession" will - create a new session and copy all session attributes to the new session. In Servlet 3.1 - (Java EE 7) and newer containers, specifying "changeSessionId" will keep the existing - session and use the container-supplied session fixation protection - (HttpServletRequest#changeSessionId()). Defaults to "changeSessionId" in Servlet 3.1 and - newer containers, "migrateSession" in older containers. Throws an exception if - "changeSessionId" is used in older containers. - - - - - - - - - - - - - - The URL to which a user will be redirected if they submit an invalid session indentifier. - Typically used to detect session timeouts. - - - - - - Allows injection of the InvalidSessionStrategy instance used by the - SessionManagementFilter - - - - - - Allows injection of the SessionAuthenticationStrategy instance used by the - SessionManagementFilter - - - - - - Defines the URL of the error page which should be shown when the - SessionAuthenticationStrategy raises an exception. If not set, an unauthorized (401) error - code will be returned to the client. Note that this attribute doesn't apply if the error - occurs during a form-based login, where the URL for authentication failure will take - precedence. - - - - - - - - - The maximum number of sessions a single authenticated user can have open at the same time. - Defaults to "1". A negative value denotes unlimited sessions. - - - - - - The URL a user will be redirected to if they attempt to use a session which has been - "expired" because they have logged in again. - - - - - - Allows injection of the SessionInformationExpiredStrategy instance used by the - ConcurrentSessionFilter - - - - - - Specifies that an unauthorized error should be reported when a user attempts to login when - they already have the maximum configured sessions open. The default behaviour is to expire - the original session. If the session-authentication-error-url attribute is set on the - session-management URL, the user will be redirected to this URL. - - - - - - Allows you to define an alias for the SessionRegistry bean in order to access it in your - own configuration. - - - - - - Allows you to define an external SessionRegistry bean to be used by the concurrency - control setup. - - - - - - - - - The "key" used to identify cookies from a specific token-based remember-me application. - You should set this to a unique value for your application. If unset, it will default to a - random value generated by SecureRandom. - - - - - - Reference to a PersistentTokenRepository bean for use with the persistent token - remember-me implementation. - - - - - - A reference to a DataSource bean - - - - - - - A reference to a user-service (or UserDetailsService bean) Id - - - - - - Exports the internally defined RememberMeServices as a bean alias, allowing it to be used - by other beans in the application context. - - - - - - Determines whether the "secure" flag will be set on the remember-me cookie. If set to - true, the cookie will only be submitted over HTTPS (recommended). By default, secure - cookies will be used if the request is made on a secure connection. - - - - - - The period (in seconds) for which the remember-me cookie should be valid. - - - - - - Reference to an AuthenticationSuccessHandler bean which should be used to handle a - successful remember-me authentication. - - - - - - The name of the request parameter which toggles remember-me authentication. Defaults to - 'remember-me'. - - - - - - The name of cookie which store the token for remember-me authentication. Defaults to - 'remember-me'. - - - - - - - - Reference to a PersistentTokenRepository bean for use with the persistent token - remember-me implementation. - - - - - - - - Allows a custom implementation of RememberMeServices to be used. Note that this - implementation should return RememberMeAuthenticationToken instances with the same "key" - value as specified in the remember-me element. Alternatively it should register its own - AuthenticationProvider. It should also implement the LogoutHandler interface, which will - be invoked when a user logs out. Typically the remember-me cookie would be removed on - logout. - - - - - - - - - - - - The key shared between the provider and filter. This generally does not need to be set. If - unset, it will default to a random value generated by SecureRandom. - - - - - - The username that should be assigned to the anonymous request. This allows the principal - to be identified, which may be important for logging and auditing. if unset, defaults to - "anonymousUser". - - - - - - The granted authority that should be assigned to the anonymous request. Commonly this is - used to assign the anonymous request particular roles, which can subsequently be used in - authorization decisions. If unset, defaults to "ROLE_ANONYMOUS". - - - - - - With the default namespace setup, the anonymous "authentication" facility is automatically - enabled. You can disable it using this property. - - - - - - - - - - The http port to use. - - - - - - - - The https port to use. - - - - - - - - - The regular expression used to obtain the username from the certificate's subject. - Defaults to matching on the common name using the pattern "CN=(.*?),". - - - - - - A reference to a user-service (or UserDetailsService bean) Id - - - - - - Reference to an AuthenticationDetailsSource which will be used by the authentication - filter - - - - - - - Adds a J2eePreAuthenticatedProcessingFilter to the filter chain to provide integration - with container authentication. - - - - - - - - - - A comma-separate list of roles to look for in the incoming HttpServletRequest. - - - - - - A reference to a user-service (or UserDetailsService bean) Id - - - - - - - Registers the AuthenticationManager instance and allows its list of - AuthenticationProviders to be defined. Also allows you to define an alias to allow you to - reference the AuthenticationManager in your own beans. - - - - - - - Indicates that the contained user-service should be used as an authentication source. - - - - - - - - element which defines a password encoding strategy. Used by an authentication provider to - convert submitted passwords to hashed versions, for example. - - - - - - - - - - - - - Sets up an ldap authentication provider - - - - - - - Specifies that an LDAP provider should use an LDAP compare operation of the user's - password to authenticate the user - - - - - - - element which defines a password encoding strategy. Used by an authentication provider to - convert submitted passwords to hashed versions, for example. - - - - - - - - - - - - - - - - - - - - - - A bean identifier, used for referring to the bean elsewhere in the context. - - - - - - An alias you wish to use for the AuthenticationManager bean (not required it you are using - a specific id) - - - - - - If set to true, the AuthenticationManger will attempt to clear any credentials data in the - returned Authentication object, once the user has been authenticated. - - - - - - Use this ObservationRegistry to collect metrics on various parts of the filter chain - - - - - - - - - Defines a reference to a Spring bean Id. - - - - - - A reference to a user-service (or UserDetailsService bean) Id - - - - - - - Creates an in-memory UserDetailsService from a properties file or a list of "user" child - elements. Usernames are converted to lower-case internally to allow for case-insensitive - lookups, so this should not be used if case-sensitivity is required. - - - - - - - Represents a user in the application. - - - - - - - - - - A bean identifier, used for referring to the bean elsewhere in the context. - - - - - - - - - - The location of a Properties file where each line is in the format of - username=password,grantedAuthority[,grantedAuthority][,enabled|disabled] - - - - - - - - - The username assigned to the user. - - - - - - The password assigned to the user. This may be hashed if the corresponding authentication - provider supports hashing (remember to set the "hash" attribute of the "user-service" - element). This attribute be omitted in the case where the data will not be used for - authentication, but only for accessing authorities. If omitted, the namespace will - generate a random value, preventing its accidental use for authentication. Cannot be - empty. - - - - - - One of more authorities granted to the user. Separate authorities with a comma (but no - space). For example, "ROLE_USER,ROLE_ADMINISTRATOR" - - - - - - Can be set to "true" to mark an account as locked and unusable. - - - - - - Can be set to "true" to mark an account as disabled and unusable. - - - - - - - Causes creation of a JDBC-based UserDetailsService. - - - - - - A bean identifier, used for referring to the bean elsewhere in the context. - - - - - - - - - - The bean ID of the DataSource which provides the required tables. - - - - - - Defines a reference to a cache for use with a UserDetailsService. - - - - - - An SQL statement to query a username, password, and enabled status given a username. - Default is "select username,password,enabled from users where username = ?" - - - - - - An SQL statement to query for a user's granted authorities given a username. The default - is "select username, authority from authorities where username = ?" - - - - - - An SQL statement to query user's group authorities given a username. The default is - "select g.id, g.group_name, ga.authority from groups g, group_members gm, - group_authorities ga where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id" - - - - - - A non-empty string prefix that will be added to role strings loaded from persistent - storage (e.g. "ROLE_"). Use the value "none" for no prefix in cases where the default is - non-empty. - - - - - - - Element for configuration of the CsrfFilter for protection against CSRF. It also updates - the default RequestCache to only replay "GET" requests. - - - - - - - - - - Specifies if csrf protection should be disabled. Default false (i.e. CSRF protection is - enabled). - - - - - - The RequestMatcher instance to be used to determine if CSRF should be applied. Default is - any HTTP method except "GET", "TRACE", "HEAD", "OPTIONS" - - - - - - The CsrfTokenRepository to use. The default is HttpSessionCsrfTokenRepository wrapped by - LazyCsrfTokenRepository. - - - - - - The CsrfTokenRequestHandler to use. The default is CsrfTokenRequestAttributeHandler. - - - - - - - Element for configuration of the HeaderWritersFilter. Enables easy setting for the - X-Frame-Options, X-XSS-Protection and X-Content-Type-Options headers. - - - - - - - - - - - - - - - - - - - - - - - - - - Specifies if the default headers should be disabled. Default false. - - - - - - Specifies if headers should be disabled. Default false. - - - - - - - Adds support for HTTP Strict Transport Security (HSTS) - - - - - - - - - - Specifies if HTTP Strict Transport Security (HSTS) should be disabled. Default false. - - - - - - Specifies if subdomains should be included. Default true. - - - - - - Specifies the maximum amount of time the host should be considered a Known HSTS Host. - Default one year. - - - - - - The RequestMatcher instance to be used to determine if the header should be set. Default - is if HttpServletRequest.isSecure() is true. - - - - - - Specifies if preload should be included. Default false. - - - - - - - Element for configuration of CorsFilter. If no CorsFilter or CorsConfigurationSource is - specified a HandlerMappingIntrospector is used as the CorsConfigurationSource - - - - - - - - - - Defines a reference to a Spring bean Id. - - - - - - Specifies a bean id that is a CorsConfigurationSource used to construct the CorsFilter to - use - - - - - - - Adds support for HTTP Public Key Pinning (HPKP). - - - - - - - - - - - - - - - - - - The list with pins - - - - - - - - - - - A pin is specified using the base64-encoded SPKI fingerprint as value and the - cryptographic hash algorithm as attribute - - - - - - The cryptographic hash algorithm - - - - - - - - - Specifies if HTTP Public Key Pinning (HPKP) should be disabled. Default false. - - - - - - Specifies if subdomains should be included. Default false. - - - - - - Sets the value for the max-age directive of the Public-Key-Pins header. Default 60 days. - - - - - - Specifies if the browser should only report pin validation failures. Default true. - - - - - - Specifies the URI to which the browser should report pin validation failures. - - - - - - - Adds support for Content Security Policy (CSP) - - - - - - - - - - The security policy directive(s) for the Content-Security-Policy header or if report-only - is set to true, then the Content-Security-Policy-Report-Only header is used. - - - - - - Set to true, to enable the Content-Security-Policy-Report-Only header for reporting policy - violations only. Defaults to false. - - - - - - - Adds support for Referrer Policy - - - - - - - - - - The policies for the Referrer-Policy header. - - - - - - - - - - - - - - - - - - - Adds support for Feature Policy - - - - - - - - - - The security policy directive(s) for the Feature-Policy header. - - - - - - - Adds support for Permissions Policy - - - - - - - - - - The policies for the Permissions-Policy header. - - - - - - - Adds Cache-Control no-cache, no-store, must-revalidate, Pragma no-cache, and Expires 0 for - every request - - - - - - - - - - Specifies if Cache Control should be disabled. Default false. - - - - - - - Enable basic clickjacking support for newer browsers (IE8+), will set the X-Frame-Options - header. - - - - - - - - - - If disabled, the X-Frame-Options header will not be included. Default false. - - - - - - Specify the policy to use for the X-Frame-Options-Header. - - - - - - - - - - - - - Specify the strategy to use when ALLOW-FROM is chosen. - - - - - - - - - - - - - Defines a reference to a Spring bean Id. - - - - - - Specify a value to use for the chosen strategy. - - - - - - Specify the request parameter to use for the origin when using a 'whitelist' or 'regexp' - based strategy. Default is 'from'. Deprecated ALLOW-FROM is an obsolete directive that no - longer works in modern browsers. Instead use Content-Security-Policy with the <a - href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors">frame-ancestors</a> - directive. - - - - - - - Enable basic XSS browser protection, supported by newer browsers (IE8+), will set the - X-XSS-Protection header. - - - - - - - - - - disable the X-XSS-Protection header. Default is 'false' meaning it is enabled. - - - - - - Specify the value for the X-Xss-Protection header. Defaults to "0". - - - - - - - - - - - - - - Add a X-Content-Type-Options header to the resopnse. Value is always 'nosniff'. - - - - - - - - - - If disabled, the X-Content-Type-Options header will not be included. Default false. - - - - - - - Adds support for Cross-Origin-Opener-Policy header - - - - - - - - - - The policies for the Cross-Origin-Opener-Policy header. - - - - - - - - - - - - - - Adds support for Cross-Origin-Embedder-Policy header - - - - - - - - - - The policies for the Cross-Origin-Embedder-Policy header. - - - - - - - - - - - - - Adds support for Cross-Origin-Resource-Policy header - - - - - - - - - - The policies for the Cross-Origin-Resource-Policy header. - - - - - - - - - - - - - - Add additional headers to the response. - - - - - - - - - - The name of the header to add. - - - - - - The value for the header. - - - - - - Defines a reference to a Spring bean Id. - - - - - - - - Used to indicate that a filter bean declaration should be incorporated into the security - filter chain. - - - - - - - - - - - The filter immediately after which the custom-filter should be placed in the chain. This - feature will only be needed by advanced users who wish to mix their own filters into the - security filter chain and have some knowledge of the standard Spring Security filters. The - filter names map to specific Spring Security implementation filters. - - - - - - The filter immediately before which the custom-filter should be placed in the chain - - - - - - The explicit position at which the custom-filter should be placed in the chain. Use if you - are replacing a standard filter. - - - - - - - - The filter immediately after which the custom-filter should be placed in the chain. This - feature will only be needed by advanced users who wish to mix their own filters into the - security filter chain and have some knowledge of the standard Spring Security filters. The - filter names map to specific Spring Security implementation filters. - - - - - - - - The filter immediately before which the custom-filter should be placed in the chain - - - - - - - - The explicit position at which the custom-filter should be placed in the chain. Use if you - are replacing a standard filter. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/config/src/test/java/org/springframework/security/config/MockServletContext.java b/config/src/test/java/org/springframework/security/config/MockServletContext.java index d819d4c798..67b7c396e7 100644 --- a/config/src/test/java/org/springframework/security/config/MockServletContext.java +++ b/config/src/test/java/org/springframework/security/config/MockServletContext.java @@ -55,11 +55,6 @@ public class MockServletContext extends org.springframework.mock.web.MockServlet return this.registrations; } - @Override - public ServletRegistration getServletRegistration(String servletName) { - return this.registrations.get(servletName); - } - private static class MockServletRegistration implements ServletRegistration.Dynamic { private final String name; diff --git a/config/src/test/java/org/springframework/security/config/TestMockHttpServletMappings.java b/config/src/test/java/org/springframework/security/config/TestMockHttpServletMappings.java deleted file mode 100644 index 3f1f7f797b..0000000000 --- a/config/src/test/java/org/springframework/security/config/TestMockHttpServletMappings.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2002-2023 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; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.MappingMatch; - -import org.springframework.mock.web.MockHttpServletMapping; - -public final class TestMockHttpServletMappings { - - private TestMockHttpServletMappings() { - - } - - public static MockHttpServletMapping extension(HttpServletRequest request, String extension) { - String uri = request.getRequestURI(); - String matchValue = uri.substring(0, uri.lastIndexOf(extension)); - return new MockHttpServletMapping(matchValue, "*" + extension, "extension", MappingMatch.EXTENSION); - } - - public static MockHttpServletMapping path(HttpServletRequest request, String path) { - String uri = request.getRequestURI(); - String matchValue = uri.substring(path.length()); - return new MockHttpServletMapping(matchValue, path + "/*", "path", MappingMatch.PATH); - } - - public static MockHttpServletMapping defaultMapping() { - return new MockHttpServletMapping("", "/", "default", MappingMatch.DEFAULT); - } - -} diff --git a/config/src/test/java/org/springframework/security/config/annotation/authentication/configurers/provisioning/UserDetailsManagerConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/authentication/configurers/provisioning/UserDetailsManagerConfigurerTests.java index 06935c82cd..de5240661a 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/authentication/configurers/provisioning/UserDetailsManagerConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/authentication/configurers/provisioning/UserDetailsManagerConfigurerTests.java @@ -17,13 +17,11 @@ package org.springframework.security.config.annotation.authentication.configurers.provisioning; import java.util.Arrays; -import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.provisioning.InMemoryUserDetailsManager; @@ -75,7 +73,7 @@ public class UserDetailsManagerConfigurerTests { .authorities(authority) .build(); // @formatter:on - assertThat((Optional) userDetails.getAuthorities().stream().findFirst()).contains(authority); + assertThat(userDetails.getAuthorities().stream().findFirst().get()).isEqualTo(authority); } @Test @@ -101,7 +99,7 @@ public class UserDetailsManagerConfigurerTests { .authorities(Arrays.asList(authority)) .build(); // @formatter:on - assertThat((Optional) userDetails.getAuthorities().stream().findFirst()).contains(authority); + assertThat(userDetails.getAuthorities().stream().findFirst().get()).isEqualTo(authority); } private UserDetailsManagerConfigurer> configurer() { diff --git a/config/src/test/java/org/springframework/security/config/annotation/issue50/SecurityConfig.java b/config/src/test/java/org/springframework/security/config/annotation/issue50/SecurityConfig.java index ba7d4a616c..e3b1d8c42e 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/issue50/SecurityConfig.java +++ b/config/src/test/java/org/springframework/security/config/annotation/issue50/SecurityConfig.java @@ -66,7 +66,7 @@ public class SecurityConfig { @Bean public AuthenticationProvider authenticationProvider() { - Assert.notNull(this.myUserRepository, "myUserRepository cannot be null"); + Assert.notNull(this.myUserRepository); return new AuthenticationProvider() { @Override public boolean supports(Class authentication) { diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/EnableCustomMethodSecurity.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/EnableCustomMethodSecurity.java deleted file mode 100644 index 3853c0ad08..0000000000 --- a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/EnableCustomMethodSecurity.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.method.configuration; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.context.annotation.AdviceMode; -import org.springframework.core.annotation.AliasFor; - -/** - * @author Evgeniy Cheban - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@EnableMethodSecurity -public @interface EnableCustomMethodSecurity { - - @AliasFor(annotation = EnableMethodSecurity.class, attribute = "proxyTargetClass") - boolean proxyTargetClass() default false; - - @AliasFor(annotation = EnableMethodSecurity.class, attribute = "mode") - AdviceMode mode() default AdviceMode.PROXY; - -} diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java index 7e049bed7c..b6668108e8 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java @@ -95,21 +95,6 @@ public class PrePostMethodSecurityConfigurationTests { @Autowired(required = false) BusinessService businessService; - @WithMockUser - @Test - public void customMethodSecurityPreAuthorizeAdminWhenRoleUserThenAccessDeniedException() { - this.spring.register(CustomMethodSecurityServiceConfig.class).autowire(); - assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorizeAdmin) - .withMessage("Access Denied"); - } - - @WithMockUser(roles = "ADMIN") - @Test - public void customMethodSecurityPreAuthorizeAdminWhenRoleAdminThenPasses() { - this.spring.register(CustomMethodSecurityServiceConfig.class).autowire(); - this.methodSecurityService.preAuthorizeAdmin(); - } - @WithMockUser(roles = "ADMIN") @Test public void preAuthorizeWhenRoleAdminThenAccessDeniedException() { @@ -451,17 +436,6 @@ public class PrePostMethodSecurityConfigurationTests { return (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false); } - @Configuration - @EnableCustomMethodSecurity - static class CustomMethodSecurityServiceConfig { - - @Bean - MethodSecurityService methodSecurityService() { - return new MethodSecurityServiceImpl(); - } - - } - @Configuration @EnableMethodSecurity static class MethodSecurityServiceConfig { diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/AbstractConfiguredSecurityBuilderTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/AbstractConfiguredSecurityBuilderTests.java index 9c2c1f0a14..2d49ddb242 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/AbstractConfiguredSecurityBuilderTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/AbstractConfiguredSecurityBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2018 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. @@ -21,7 +21,6 @@ import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.SecurityConfigurer; @@ -150,19 +149,6 @@ public class AbstractConfiguredSecurityBuilderTests { assertThat(builder.getConfigurers(DelegateSecurityConfigurer.class)).hasSize(2); } - @Test - public void withWhenConfigurerThenConfigurerAdded() throws Exception { - this.builder.with(new TestSecurityConfigurer(), Customizer.withDefaults()); - assertThat(this.builder.getConfigurers(TestSecurityConfigurer.class)).hasSize(1); - } - - @Test - public void withWhenDuplicateConfigurerAddedThenDuplicateConfigurerRemoved() throws Exception { - this.builder.with(new TestSecurityConfigurer(), Customizer.withDefaults()); - this.builder.with(new TestSecurityConfigurer(), Customizer.withDefaults()); - assertThat(this.builder.getConfigurers(TestSecurityConfigurer.class)).hasSize(1); - } - private static class ApplyAndRemoveSecurityConfigurer extends SecurityConfigurerAdapter { diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryNoMvcTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryNoMvcTests.java index ec39518324..38ef3176e0 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryNoMvcTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryNoMvcTests.java @@ -25,12 +25,8 @@ import org.springframework.http.HttpMethod; import org.springframework.security.test.support.ClassPathExclusions; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.web.context.WebApplicationContext; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; /** * Tests for {@link AbstractRequestMatcherRegistry} with no Spring MVC in the classpath @@ -45,16 +41,13 @@ public class AbstractRequestMatcherRegistryNoMvcTests { @BeforeEach public void setUp() { this.matcherRegistry = new TestRequestMatcherRegistry(); - WebApplicationContext context = mock(WebApplicationContext.class); - given(context.getBeanNamesForType((Class) any())).willReturn(new String[0]); - this.matcherRegistry.setApplicationContext(context); } @Test public void requestMatchersWhenPatternAndMvcNotPresentThenReturnAntPathRequestMatcherType() { List requestMatchers = this.matcherRegistry.requestMatchers("/path"); assertThat(requestMatchers).isNotEmpty(); - assertThat(requestMatchers).hasSize(1); + assertThat(requestMatchers.size()).isEqualTo(1); assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class); } @@ -62,7 +55,7 @@ public class AbstractRequestMatcherRegistryNoMvcTests { public void requestMatchersWhenHttpMethodAndPatternAndMvcNotPresentThenReturnAntPathRequestMatcherType() { List requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET, "/path"); assertThat(requestMatchers).isNotEmpty(); - assertThat(requestMatchers).hasSize(1); + assertThat(requestMatchers.size()).isEqualTo(1); assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class); } @@ -70,7 +63,7 @@ public class AbstractRequestMatcherRegistryNoMvcTests { public void requestMatchersWhenHttpMethodAndMvcNotPresentThenReturnAntPathMatcherType() { List requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET); assertThat(requestMatchers).isNotEmpty(); - assertThat(requestMatchers).hasSize(1); + assertThat(requestMatchers.size()).isEqualTo(1); assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class); } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java index ddcddcd93c..3aa9df13b9 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,11 +26,8 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.http.HttpMethod; -import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.security.config.MockServletContext; -import org.springframework.security.config.TestMockHttpServletMappings; import org.springframework.security.config.annotation.ObjectPostProcessor; -import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry.DispatcherServletDelegatingRequestMatcher; import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher; @@ -43,9 +40,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; /** * Tests for {@link AbstractRequestMatcherRegistry}. @@ -81,7 +75,7 @@ public class AbstractRequestMatcherRegistryTests { List requestMatchers = this.matcherRegistry .requestMatchers(new RegexRequestMatcher("/a.*", HttpMethod.GET.name())); assertThat(requestMatchers).isNotEmpty(); - assertThat(requestMatchers).hasSize(1); + assertThat(requestMatchers.size()).isEqualTo(1); assertThat(requestMatchers.get(0)).isExactlyInstanceOf(RegexRequestMatcher.class); } @@ -90,7 +84,7 @@ public class AbstractRequestMatcherRegistryTests { List requestMatchers = this.matcherRegistry .requestMatchers(new RegexRequestMatcher("/a.*", null)); assertThat(requestMatchers).isNotEmpty(); - assertThat(requestMatchers).hasSize(1); + assertThat(requestMatchers.size()).isEqualTo(1); assertThat(requestMatchers.get(0)).isExactlyInstanceOf(RegexRequestMatcher.class); } @@ -99,7 +93,7 @@ public class AbstractRequestMatcherRegistryTests { List requestMatchers = this.matcherRegistry .requestMatchers(new AntPathRequestMatcher("/a.*", HttpMethod.GET.name())); assertThat(requestMatchers).isNotEmpty(); - assertThat(requestMatchers).hasSize(1); + assertThat(requestMatchers.size()).isEqualTo(1); assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class); } @@ -107,7 +101,7 @@ public class AbstractRequestMatcherRegistryTests { public void antMatchersWhenPatternParamThenReturnAntPathRequestMatcherType() { List requestMatchers = this.matcherRegistry.requestMatchers(new AntPathRequestMatcher("/a.*")); assertThat(requestMatchers).isNotEmpty(); - assertThat(requestMatchers).hasSize(1); + assertThat(requestMatchers.size()).isEqualTo(1); assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class); } @@ -116,7 +110,7 @@ public class AbstractRequestMatcherRegistryTests { List requestMatchers = this.matcherRegistry.dispatcherTypeMatchers(HttpMethod.GET, DispatcherType.ASYNC); assertThat(requestMatchers).isNotEmpty(); - assertThat(requestMatchers).hasSize(1); + assertThat(requestMatchers.size()).isEqualTo(1); assertThat(requestMatchers.get(0)).isExactlyInstanceOf(DispatcherTypeRequestMatcher.class); } @@ -124,7 +118,7 @@ public class AbstractRequestMatcherRegistryTests { public void dispatcherMatchersWhenPatternParamThenReturnAntPathRequestMatcherType() { List requestMatchers = this.matcherRegistry.dispatcherTypeMatchers(DispatcherType.INCLUDE); assertThat(requestMatchers).isNotEmpty(); - assertThat(requestMatchers).hasSize(1); + assertThat(requestMatchers.size()).isEqualTo(1); assertThat(requestMatchers.get(0)).isExactlyInstanceOf(DispatcherTypeRequestMatcher.class); } @@ -132,7 +126,7 @@ public class AbstractRequestMatcherRegistryTests { public void requestMatchersWhenPatternAndMvcPresentThenReturnMvcRequestMatcherType() { List requestMatchers = this.matcherRegistry.requestMatchers("/path"); assertThat(requestMatchers).isNotEmpty(); - assertThat(requestMatchers).hasSize(1); + assertThat(requestMatchers.size()).isEqualTo(1); assertThat(requestMatchers.get(0)).isExactlyInstanceOf(MvcRequestMatcher.class); } @@ -140,7 +134,7 @@ public class AbstractRequestMatcherRegistryTests { public void requestMatchersWhenHttpMethodAndPatternAndMvcPresentThenReturnMvcRequestMatcherType() { List requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET, "/path"); assertThat(requestMatchers).isNotEmpty(); - assertThat(requestMatchers).hasSize(1); + assertThat(requestMatchers.size()).isEqualTo(1); assertThat(requestMatchers.get(0)).isExactlyInstanceOf(MvcRequestMatcher.class); } @@ -148,7 +142,7 @@ public class AbstractRequestMatcherRegistryTests { public void requestMatchersWhenHttpMethodAndMvcPresentThenReturnMvcRequestMatcherType() { List requestMatchers = this.matcherRegistry.requestMatchers(HttpMethod.GET); assertThat(requestMatchers).isNotEmpty(); - assertThat(requestMatchers).hasSize(1); + assertThat(requestMatchers.size()).isEqualTo(1); assertThat(requestMatchers.get(0)).isExactlyInstanceOf(MvcRequestMatcher.class); } @@ -165,12 +159,16 @@ public class AbstractRequestMatcherRegistryTests { public void requestMatchersWhenNoDispatcherServletThenAntPathRequestMatcherType() { MockServletContext servletContext = new MockServletContext(); given(this.context.getServletContext()).willReturn(servletContext); - servletContext.addServlet("servletOne", Servlet.class).addMapping("/one"); - servletContext.addServlet("servletTwo", Servlet.class).addMapping("/two"); List requestMatchers = this.matcherRegistry.requestMatchers("/**"); assertThat(requestMatchers).isNotEmpty(); assertThat(requestMatchers).hasSize(1); assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class); + servletContext.addServlet("servletOne", Servlet.class); + servletContext.addServlet("servletTwo", Servlet.class); + requestMatchers = this.matcherRegistry.requestMatchers("/**"); + assertThat(requestMatchers).isNotEmpty(); + assertThat(requestMatchers).hasSize(1); + assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class); } @Test @@ -178,26 +176,7 @@ public class AbstractRequestMatcherRegistryTests { MockServletContext servletContext = new MockServletContext(); given(this.context.getServletContext()).willReturn(servletContext); servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/"); - servletContext.addServlet("servletTwo", DispatcherServlet.class).addMapping("/servlet/*"); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> this.matcherRegistry.requestMatchers("/**")); - } - - @Test - public void requestMatchersWhenMultipleDispatcherServletMappingsThenException() { - MockServletContext servletContext = new MockServletContext(); - given(this.context.getServletContext()).willReturn(servletContext); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/", "/mvc/*"); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> this.matcherRegistry.requestMatchers("/**")); - } - - @Test - public void requestMatchersWhenPathDispatcherServletAndOtherServletsThenException() { - MockServletContext servletContext = new MockServletContext(); - given(this.context.getServletContext()).willReturn(servletContext); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*"); - servletContext.addServlet("default", Servlet.class).addMapping("/"); + servletContext.addServlet("servletTwo", Servlet.class).addMapping("/servlet/**"); assertThatExceptionOfType(IllegalArgumentException.class) .isThrownBy(() -> this.matcherRegistry.requestMatchers("/**")); } @@ -214,67 +193,6 @@ public class AbstractRequestMatcherRegistryTests { assertThat(requestMatchers.get(0)).isInstanceOf(MvcRequestMatcher.class); } - @Test - public void requestMatchersWhenOnlyDispatcherServletThenAllows() { - MockServletContext servletContext = new MockServletContext(); - given(this.context.getServletContext()).willReturn(servletContext); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*"); - List requestMatchers = this.matcherRegistry.requestMatchers("/**"); - assertThat(requestMatchers).hasSize(1); - assertThat(requestMatchers.get(0)).isInstanceOf(MvcRequestMatcher.class); - } - - @Test - public void requestMatchersWhenImplicitServletsThenAllows() { - mockMvcIntrospector(true); - MockServletContext servletContext = new MockServletContext(); - given(this.context.getServletContext()).willReturn(servletContext); - servletContext.addServlet("defaultServlet", Servlet.class); - servletContext.addServlet("jspServlet", Servlet.class).addMapping("*.jsp", "*.jspx"); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/"); - List requestMatchers = this.matcherRegistry.requestMatchers("/**"); - assertThat(requestMatchers).hasSize(1); - assertThat(requestMatchers.get(0)).isInstanceOf(DispatcherServletDelegatingRequestMatcher.class); - } - - @Test - public void requestMatchersWhenPathBasedNonDispatcherServletThenAllows() { - MockServletContext servletContext = new MockServletContext(); - given(this.context.getServletContext()).willReturn(servletContext); - servletContext.addServlet("path", Servlet.class).addMapping("/services/*"); - servletContext.addServlet("default", DispatcherServlet.class).addMapping("/"); - List requestMatchers = this.matcherRegistry.requestMatchers("/services/*"); - assertThat(requestMatchers).hasSize(1); - assertThat(requestMatchers.get(0)).isInstanceOf(DispatcherServletDelegatingRequestMatcher.class); - MockHttpServletRequest request = new MockHttpServletRequest("GET", "/services/endpoint"); - request.setHttpServletMapping(TestMockHttpServletMappings.defaultMapping()); - assertThat(requestMatchers.get(0).matcher(request).isMatch()).isTrue(); - request.setHttpServletMapping(TestMockHttpServletMappings.path(request, "/services")); - request.setServletPath("/services"); - request.setPathInfo("/endpoint"); - assertThat(requestMatchers.get(0).matcher(request).isMatch()).isTrue(); - } - - @Test - public void matchesWhenDispatcherServletThenMvc() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("default", DispatcherServlet.class).addMapping("/"); - servletContext.addServlet("path", Servlet.class).addMapping("/services/*"); - MvcRequestMatcher mvc = mock(MvcRequestMatcher.class); - AntPathRequestMatcher ant = mock(AntPathRequestMatcher.class); - DispatcherServletDelegatingRequestMatcher requestMatcher = new DispatcherServletDelegatingRequestMatcher(ant, - mvc, servletContext); - MockHttpServletRequest request = new MockHttpServletRequest("GET", "/services/endpoint"); - request.setHttpServletMapping(TestMockHttpServletMappings.defaultMapping()); - assertThat(requestMatcher.matches(request)).isFalse(); - verify(mvc).matches(request); - verifyNoInteractions(ant); - request.setHttpServletMapping(TestMockHttpServletMappings.path(request, "/services")); - assertThat(requestMatcher.matches(request)).isFalse(); - verify(ant).matches(request); - verifyNoMoreInteractions(mvc); - } - private void mockMvcIntrospector(boolean isPresent) { ApplicationContext context = this.matcherRegistry.getApplicationContext(); given(context.containsBean("mvcHandlerMappingIntrospector")).willReturn(isPresent); diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfigurationTests.java index 2d95161fc6..94a775dc82 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -47,7 +47,6 @@ import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authentication.event.AbstractAuthenticationEvent; import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent; import org.springframework.security.authentication.event.AuthenticationSuccessEvent; -import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; @@ -64,11 +63,9 @@ import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.test.web.servlet.RequestCacheResultMatcher; import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter; import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter; import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter; -import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; @@ -76,10 +73,6 @@ import org.springframework.web.accept.ContentNegotiationStrategy; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.CorsConfigurationSource; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -92,8 +85,6 @@ import static org.springframework.security.test.web.servlet.request.SecurityMock import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; -import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; -import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.unauthenticated; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -361,40 +352,6 @@ public class HttpSecurityConfigurationTests { DefaultLogoutPageGeneratingFilter.class); } - @Test - public void configureWhenCorsConfigurationSourceThenApplyCors() { - this.spring.register(CorsConfigurationSourceConfig.class, DefaultWithFilterChainConfig.class).autowire(); - SecurityFilterChain filterChain = this.spring.getContext().getBean(SecurityFilterChain.class); - CorsFilter corsFilter = (CorsFilter) filterChain.getFilters() - .stream() - .filter((f) -> f instanceof CorsFilter) - .findFirst() - .get(); - Object configSource = ReflectionTestUtils.getField(corsFilter, "configSource"); - assertThat(configSource).isInstanceOf(UrlBasedCorsConfigurationSource.class); - } - - @Test - public void configureWhenAddingCustomDslUsingWithThenApplied() throws Exception { - this.spring.register(WithCustomDslConfig.class, UserDetailsConfig.class).autowire(); - SecurityFilterChain filterChain = this.spring.getContext().getBean(SecurityFilterChain.class); - List filters = filterChain.getFilters(); - assertThat(filters).hasAtLeastOneElementOfType(UsernamePasswordAuthenticationFilter.class); - this.mockMvc.perform(formLogin()).andExpectAll(redirectedUrl("/"), authenticated()); - } - - @Test - public void configureWhenCustomDslAddedFromFactoriesAndDisablingUsingWithThenNotApplied() throws Exception { - this.springFactoriesLoader - .when(() -> SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, getClass().getClassLoader())) - .thenReturn(List.of(new WithCustomDsl())); - this.spring.register(WithCustomDslDisabledConfig.class, UserDetailsConfig.class).autowire(); - SecurityFilterChain filterChain = this.spring.getContext().getBean(SecurityFilterChain.class); - List filters = filterChain.getFilters(); - assertThat(filters).doesNotHaveAnyElementsOfTypes(UsernamePasswordAuthenticationFilter.class); - this.mockMvc.perform(formLogin()).andExpectAll(status().isNotFound(), unauthenticated()); - } - @RestController static class NameController { @@ -659,20 +616,6 @@ public class HttpSecurityConfigurationTests { } - @Configuration - static class CorsConfigurationSourceConfig { - - @Bean - CorsConfigurationSource corsConfigurationSource() { - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - CorsConfiguration corsConfiguration = new CorsConfiguration(); - corsConfiguration.setAllowedOrigins(List.of("http://localhost:8080")); - source.registerCorsConfiguration("/**", corsConfiguration); - return source; - } - - } - static class DefaultConfigurer extends AbstractHttpConfigurer { boolean init; @@ -691,45 +634,4 @@ public class HttpSecurityConfigurationTests { } - @Configuration - @EnableWebSecurity - static class WithCustomDslConfig { - - @Bean - SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - // @formatter:off - http - .with(new WithCustomDsl(), Customizer.withDefaults()) - .httpBasic(Customizer.withDefaults()); - // @formatter:on - return http.build(); - } - - } - - @Configuration - @EnableWebSecurity - static class WithCustomDslDisabledConfig { - - @Bean - SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - // @formatter:off - http - .with(new WithCustomDsl(), (dsl) -> dsl.disable()) - .httpBasic(Customizer.withDefaults()); - // @formatter:on - return http.build(); - } - - } - - static class WithCustomDsl extends AbstractHttpConfigurer { - - @Override - public void init(HttpSecurity builder) throws Exception { - builder.formLogin(Customizer.withDefaults()); - } - - } - } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizedClientManagerConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizedClientManagerConfigurationTests.java deleted file mode 100644 index 45d69c2323..0000000000 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizedClientManagerConfigurationTests.java +++ /dev/null @@ -1,547 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configuration; - -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.function.Consumer; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.oauth2.client.CommonOAuth2Provider; -import org.springframework.security.config.test.SpringTestContext; -import org.springframework.security.oauth2.client.AuthorizationCodeOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.ClientAuthorizationRequiredException; -import org.springframework.security.oauth2.client.ClientCredentialsOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.JwtBearerOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.OAuth2AuthorizationContext; -import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; -import org.springframework.security.oauth2.client.PasswordOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.endpoint.AbstractOAuth2AuthorizationGrantRequest; -import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest; -import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; -import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; -import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest; -import org.springframework.security.oauth2.client.endpoint.OAuth2PasswordGrantRequest; -import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest; -import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest; -import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; -import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; -import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService; -import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; -import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; -import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager; -import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; -import org.springframework.security.oauth2.core.AuthorizationGrantType; -import org.springframework.security.oauth2.core.OAuth2AccessToken; -import org.springframework.security.oauth2.core.OAuth2AuthorizationException; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.TestOAuth2RefreshTokens; -import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; -import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; -import org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses; -import org.springframework.security.oauth2.core.oidc.user.OidcUser; -import org.springframework.security.oauth2.core.user.OAuth2User; -import org.springframework.security.oauth2.jwt.JoseHeaderNames; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.security.oauth2.jwt.JwtClaimNames; -import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.util.StringUtils; - -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.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link OAuth2ClientConfiguration.OAuth2AuthorizedClientManagerConfiguration}. - * - * @author Joe Grandja - * @author Steve Riesenberg - */ -public class OAuth2AuthorizedClientManagerConfigurationTests { - - private static OAuth2AccessTokenResponseClient MOCK_RESPONSE_CLIENT; - - public final SpringTestContext spring = new SpringTestContext(this); - - @Autowired - private OAuth2AuthorizedClientManager authorizedClientManager; - - @Autowired - private ClientRegistrationRepository clientRegistrationRepository; - - @Autowired - private OAuth2AuthorizedClientRepository authorizedClientRepository; - - @Autowired(required = false) - private AuthorizationCodeOAuth2AuthorizedClientProvider authorizationCodeAuthorizedClientProvider; - - private MockHttpServletRequest request; - - private MockHttpServletResponse response; - - @BeforeEach - @SuppressWarnings("unchecked") - public void setUp() { - MOCK_RESPONSE_CLIENT = mock(OAuth2AccessTokenResponseClient.class); - this.request = new MockHttpServletRequest(); - this.response = new MockHttpServletResponse(); - } - - @Test - public void loadContextWhenOAuth2ClientEnabledThenConfigured() { - this.spring.register(MinimalOAuth2ClientConfig.class).autowire(); - assertThat(this.authorizedClientManager).isNotNull(); - } - - @Test - public void authorizeWhenAuthorizationCodeAuthorizedClientProviderBeanThenUsed() { - this.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire(); - - TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", null); - // @formatter:off - OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest - .withClientRegistrationId("google") - .principal(authentication) - .attribute(HttpServletRequest.class.getName(), this.request) - .attribute(HttpServletResponse.class.getName(), this.response) - .build(); - assertThatExceptionOfType(ClientAuthorizationRequiredException.class) - .isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest)) - .extracting(OAuth2AuthorizationException::getError) - .extracting(OAuth2Error::getErrorCode) - .isEqualTo("client_authorization_required"); - // @formatter:on - - verify(this.authorizationCodeAuthorizedClientProvider).authorize(any(OAuth2AuthorizationContext.class)); - } - - @Test - public void authorizeWhenRefreshTokenAccessTokenResponseClientBeanThenUsed() { - this.spring.register(CustomAccessTokenResponseClientsConfig.class).autowire(); - testRefreshTokenGrant(); - } - - @Test - public void authorizeWhenRefreshTokenAuthorizedClientProviderBeanThenUsed() { - this.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire(); - testRefreshTokenGrant(); - } - - private void testRefreshTokenGrant() { - OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build(); - given(MOCK_RESPONSE_CLIENT.getTokenResponse(any(OAuth2RefreshTokenGrantRequest.class))) - .willReturn(accessTokenResponse); - - TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", null); - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("google"); - OAuth2AuthorizedClient existingAuthorizedClient = new OAuth2AuthorizedClient(clientRegistration, - authentication.getName(), getExpiredAccessToken(), TestOAuth2RefreshTokens.refreshToken()); - this.authorizedClientRepository.saveAuthorizedClient(existingAuthorizedClient, authentication, this.request, - this.response); - // @formatter:off - OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest - .withAuthorizedClient(existingAuthorizedClient) - .principal(authentication) - .attribute(HttpServletRequest.class.getName(), this.request) - .attribute(HttpServletResponse.class.getName(), this.response) - .build(); - // @formatter:on - OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest); - assertThat(authorizedClient).isNotNull(); - - ArgumentCaptor grantRequestCaptor = ArgumentCaptor - .forClass(OAuth2RefreshTokenGrantRequest.class); - verify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture()); - - OAuth2RefreshTokenGrantRequest grantRequest = grantRequestCaptor.getValue(); - assertThat(grantRequest.getClientRegistration().getRegistrationId()) - .isEqualTo(clientRegistration.getRegistrationId()); - assertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.REFRESH_TOKEN); - assertThat(grantRequest.getAccessToken()).isEqualTo(existingAuthorizedClient.getAccessToken()); - assertThat(grantRequest.getRefreshToken()).isEqualTo(existingAuthorizedClient.getRefreshToken()); - } - - @Test - public void authorizeWhenClientCredentialsAccessTokenResponseClientBeanThenUsed() { - this.spring.register(CustomAccessTokenResponseClientsConfig.class).autowire(); - testClientCredentialsGrant(); - } - - @Test - public void authorizeWhenClientCredentialsAuthorizedClientProviderBeanThenUsed() { - this.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire(); - testClientCredentialsGrant(); - } - - private void testClientCredentialsGrant() { - OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build(); - given(MOCK_RESPONSE_CLIENT.getTokenResponse(any(OAuth2ClientCredentialsGrantRequest.class))) - .willReturn(accessTokenResponse); - - TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", null); - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("github"); - // @formatter:off - OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest - .withClientRegistrationId(clientRegistration.getRegistrationId()) - .principal(authentication) - .attribute(HttpServletRequest.class.getName(), this.request) - .attribute(HttpServletResponse.class.getName(), this.response) - .build(); - // @formatter:on - OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest); - assertThat(authorizedClient).isNotNull(); - - ArgumentCaptor grantRequestCaptor = ArgumentCaptor - .forClass(OAuth2ClientCredentialsGrantRequest.class); - verify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture()); - - OAuth2ClientCredentialsGrantRequest grantRequest = grantRequestCaptor.getValue(); - assertThat(grantRequest.getClientRegistration().getRegistrationId()) - .isEqualTo(clientRegistration.getRegistrationId()); - assertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS); - } - - @Test - public void authorizeWhenPasswordAccessTokenResponseClientBeanThenUsed() { - this.spring.register(CustomAccessTokenResponseClientsConfig.class).autowire(); - testPasswordGrant(); - } - - @Test - public void authorizeWhenPasswordAuthorizedClientProviderBeanThenUsed() { - this.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire(); - testPasswordGrant(); - } - - private void testPasswordGrant() { - OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build(); - given(MOCK_RESPONSE_CLIENT.getTokenResponse(any(OAuth2PasswordGrantRequest.class))) - .willReturn(accessTokenResponse); - - TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", "password"); - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("facebook"); - // @formatter:off - OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest - .withClientRegistrationId(clientRegistration.getRegistrationId()) - .principal(authentication) - .attribute(HttpServletRequest.class.getName(), this.request) - .attribute(HttpServletResponse.class.getName(), this.response) - .build(); - // @formatter:on - this.request.setParameter(OAuth2ParameterNames.USERNAME, "user"); - this.request.setParameter(OAuth2ParameterNames.PASSWORD, "password"); - OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest); - assertThat(authorizedClient).isNotNull(); - - ArgumentCaptor grantRequestCaptor = ArgumentCaptor - .forClass(OAuth2PasswordGrantRequest.class); - verify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture()); - - OAuth2PasswordGrantRequest grantRequest = grantRequestCaptor.getValue(); - assertThat(grantRequest.getClientRegistration().getRegistrationId()) - .isEqualTo(clientRegistration.getRegistrationId()); - assertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.PASSWORD); - assertThat(grantRequest.getUsername()).isEqualTo("user"); - assertThat(grantRequest.getPassword()).isEqualTo("password"); - } - - @Test - public void authorizeWhenJwtBearerAccessTokenResponseClientBeanThenUsed() { - this.spring.register(CustomAccessTokenResponseClientsConfig.class).autowire(); - testJwtBearerGrant(); - } - - @Test - public void authorizeWhenJwtBearerAuthorizedClientProviderBeanThenUsed() { - this.spring.register(CustomAuthorizedClientProvidersConfig.class).autowire(); - testJwtBearerGrant(); - } - - private void testJwtBearerGrant() { - OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build(); - given(MOCK_RESPONSE_CLIENT.getTokenResponse(any(JwtBearerGrantRequest.class))).willReturn(accessTokenResponse); - - JwtAuthenticationToken authentication = new JwtAuthenticationToken(getJwt()); - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("okta"); - // @formatter:off - OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest - .withClientRegistrationId(clientRegistration.getRegistrationId()) - .principal(authentication) - .attribute(HttpServletRequest.class.getName(), this.request) - .attribute(HttpServletResponse.class.getName(), this.response) - .build(); - // @formatter:on - OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest); - assertThat(authorizedClient).isNotNull(); - - ArgumentCaptor grantRequestCaptor = ArgumentCaptor.forClass(JwtBearerGrantRequest.class); - verify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture()); - - JwtBearerGrantRequest grantRequest = grantRequestCaptor.getValue(); - assertThat(grantRequest.getClientRegistration().getRegistrationId()) - .isEqualTo(clientRegistration.getRegistrationId()); - assertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.JWT_BEARER); - assertThat(grantRequest.getJwt().getSubject()).isEqualTo("user"); - } - - private static OAuth2AccessToken getExpiredAccessToken() { - Instant expiresAt = Instant.now().minusSeconds(60); - Instant issuedAt = expiresAt.minus(Duration.ofDays(1)); - return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "scopes", issuedAt, expiresAt, - new HashSet<>(Arrays.asList("read", "write"))); - } - - private static Jwt getJwt() { - Instant issuedAt = Instant.now(); - return new Jwt("token", issuedAt, issuedAt.plusSeconds(300), - Collections.singletonMap(JoseHeaderNames.ALG, "RS256"), - Collections.singletonMap(JwtClaimNames.SUB, "user")); - } - - @Configuration - @EnableWebSecurity - static class MinimalOAuth2ClientConfig extends OAuth2ClientBaseConfig { - - } - - @Configuration - @EnableWebSecurity - static class CustomAccessTokenResponseClientsConfig extends OAuth2ClientBaseConfig { - - @Bean - OAuth2AccessTokenResponseClient authorizationCodeTokenResponseClient() { - return new MockAuthorizationCodeClient(); - } - - @Bean - OAuth2AccessTokenResponseClient refreshTokenTokenResponseClient() { - return new MockRefreshTokenClient(); - } - - @Bean - OAuth2AccessTokenResponseClient clientCredentialsTokenResponseClient() { - return new MockClientCredentialsClient(); - } - - @Bean - OAuth2AccessTokenResponseClient passwordTokenResponseClient() { - return new MockPasswordClient(); - } - - @Bean - OAuth2AccessTokenResponseClient jwtBearerTokenResponseClient() { - return new MockJwtBearerClient(); - } - - @Bean - OAuth2UserService oauth2UserService() { - return mock(DefaultOAuth2UserService.class); - } - - @Bean - OAuth2UserService oidcUserService() { - return mock(OidcUserService.class); - } - - } - - @Configuration - @EnableWebSecurity - static class CustomAuthorizedClientProvidersConfig extends OAuth2ClientBaseConfig { - - @Bean - AuthorizationCodeOAuth2AuthorizedClientProvider authorizationCodeProvider() { - return spy(new AuthorizationCodeOAuth2AuthorizedClientProvider()); - } - - @Bean - RefreshTokenOAuth2AuthorizedClientProvider refreshTokenProvider() { - RefreshTokenOAuth2AuthorizedClientProvider authorizedClientProvider = new RefreshTokenOAuth2AuthorizedClientProvider(); - authorizedClientProvider.setAccessTokenResponseClient(new MockRefreshTokenClient()); - return authorizedClientProvider; - } - - @Bean - ClientCredentialsOAuth2AuthorizedClientProvider clientCredentialsProvider() { - ClientCredentialsOAuth2AuthorizedClientProvider authorizedClientProvider = new ClientCredentialsOAuth2AuthorizedClientProvider(); - authorizedClientProvider.setAccessTokenResponseClient(new MockClientCredentialsClient()); - return authorizedClientProvider; - } - - @Bean - PasswordOAuth2AuthorizedClientProvider passwordProvider() { - PasswordOAuth2AuthorizedClientProvider authorizedClientProvider = new PasswordOAuth2AuthorizedClientProvider(); - authorizedClientProvider.setAccessTokenResponseClient(new MockPasswordClient()); - return authorizedClientProvider; - } - - @Bean - JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider() { - JwtBearerOAuth2AuthorizedClientProvider authorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider(); - authorizedClientProvider.setAccessTokenResponseClient(new MockJwtBearerClient()); - return authorizedClientProvider; - } - - } - - abstract static class OAuth2ClientBaseConfig { - - @Bean - SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - // @formatter:off - http - .authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()) - .oauth2Login(Customizer.withDefaults()) - .oauth2Client(Customizer.withDefaults()); - return http.build(); - // @formatter:on - } - - @Bean - ClientRegistrationRepository clientRegistrationRepository() { - // @formatter:off - return new InMemoryClientRegistrationRepository(Arrays.asList( - CommonOAuth2Provider.GOOGLE.getBuilder("google") - .clientId("google-client-id") - .clientSecret("google-client-secret") - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .build(), - CommonOAuth2Provider.GITHUB.getBuilder("github") - .clientId("github-client-id") - .clientSecret("github-client-secret") - .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) - .build(), - CommonOAuth2Provider.FACEBOOK.getBuilder("facebook") - .clientId("facebook-client-id") - .clientSecret("facebook-client-secret") - .authorizationGrantType(AuthorizationGrantType.PASSWORD) - .build(), - CommonOAuth2Provider.OKTA.getBuilder("okta") - .clientId("okta-client-id") - .clientSecret("okta-client-secret") - .authorizationGrantType(AuthorizationGrantType.JWT_BEARER) - .build())); - // @formatter:on - } - - @Bean - OAuth2AuthorizedClientRepository authorizedClientRepository() { - return mock(OAuth2AuthorizedClientRepository.class); - } - - @Bean - Consumer authorizedClientManagerConsumer() { - return (authorizedClientManager) -> authorizedClientManager - .setContextAttributesMapper((authorizeRequest) -> { - HttpServletRequest request = Objects - .requireNonNull(authorizeRequest.getAttribute(HttpServletRequest.class.getName())); - String username = request.getParameter(OAuth2ParameterNames.USERNAME); - String password = request.getParameter(OAuth2ParameterNames.PASSWORD); - - Map attributes = Collections.emptyMap(); - if (StringUtils.hasText(username) && StringUtils.hasText(password)) { - attributes = new HashMap<>(); - attributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username); - attributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password); - } - - return attributes; - }); - } - - } - - private static class MockAuthorizationCodeClient - implements OAuth2AccessTokenResponseClient { - - @Override - public OAuth2AccessTokenResponse getTokenResponse( - OAuth2AuthorizationCodeGrantRequest authorizationGrantRequest) { - return MOCK_RESPONSE_CLIENT.getTokenResponse(authorizationGrantRequest); - } - - } - - private static class MockRefreshTokenClient - implements OAuth2AccessTokenResponseClient { - - @Override - public OAuth2AccessTokenResponse getTokenResponse(OAuth2RefreshTokenGrantRequest authorizationGrantRequest) { - return MOCK_RESPONSE_CLIENT.getTokenResponse(authorizationGrantRequest); - } - - } - - private static class MockClientCredentialsClient - implements OAuth2AccessTokenResponseClient { - - @Override - public OAuth2AccessTokenResponse getTokenResponse( - OAuth2ClientCredentialsGrantRequest authorizationGrantRequest) { - return MOCK_RESPONSE_CLIENT.getTokenResponse(authorizationGrantRequest); - } - - } - - private static class MockPasswordClient implements OAuth2AccessTokenResponseClient { - - @Override - public OAuth2AccessTokenResponse getTokenResponse(OAuth2PasswordGrantRequest authorizationGrantRequest) { - return MOCK_RESPONSE_CLIENT.getTokenResponse(authorizationGrantRequest); - } - - } - - private static class MockJwtBearerClient implements OAuth2AccessTokenResponseClient { - - @Override - public OAuth2AccessTokenResponse getTokenResponse(JwtBearerGrantRequest authorizationGrantRequest) { - return MOCK_RESPONSE_CLIENT.getTokenResponse(authorizationGrantRequest); - } - - } - -} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfigurationTests.java index 83a247673a..aea25c75e0 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configuration/OAuth2ClientConfigurationTests.java @@ -180,10 +180,9 @@ public class OAuth2ClientConfigurationTests { @Test public void loadContextWhenAccessTokenResponseClientRegisteredTwiceThenThrowNoUniqueBeanDefinitionException() { // @formatter:off - assertThatExceptionOfType(BeanCreationException.class) + assertThatExceptionOfType(Exception.class) .isThrownBy(() -> this.spring.register(AccessTokenResponseClientRegisteredTwiceConfig.class).autowire()) - .havingRootCause() - .isInstanceOf(NoUniqueBeanDefinitionException.class) + .withRootCauseInstanceOf(NoUniqueBeanDefinitionException.class) .withMessageContaining( "expected single matching bean but found 2: accessTokenResponseClient1,accessTokenResponseClient2"); // @formatter:on diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/SecurityReactorContextConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configuration/SecurityReactorContextConfigurationTests.java index 7dac892969..5be3bfe071 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/SecurityReactorContextConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configuration/SecurityReactorContextConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,20 +17,14 @@ package org.springframework.security.config.annotation.web.configuration; import java.net.URI; -import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ThreadFactory; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnJre; -import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.extension.ExtendWith; import reactor.core.CoreSubscriber; import reactor.core.publisher.BaseSubscriber; @@ -41,8 +35,6 @@ import reactor.util.context.Context; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.task.SimpleAsyncTaskExecutor; -import org.springframework.core.task.VirtualThreadTaskExecutor; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.mock.web.MockHttpServletRequest; @@ -54,7 +46,6 @@ import org.springframework.security.config.annotation.web.configuration.Security import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.oauth2.client.web.reactive.function.client.MockExchangeFunction; @@ -280,58 +271,6 @@ public class SecurityReactorContextConfigurationTests { verify(strategy, times(2)).getContext(); } - @Test - public void createPublisherWhenThreadFactoryIsPlatformThenSecurityContextAttributesAvailable() throws Exception { - this.spring.register(SecurityConfig.class).autowire(); - - ThreadFactory threadFactory = Executors.defaultThreadFactory(); - assertContextAttributesAvailable(threadFactory); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void createPublisherWhenThreadFactoryIsVirtualThenSecurityContextAttributesAvailable() throws Exception { - this.spring.register(SecurityConfig.class).autowire(); - - ThreadFactory threadFactory = new VirtualThreadTaskExecutor().getVirtualThreadFactory(); - assertContextAttributesAvailable(threadFactory); - } - - private void assertContextAttributesAvailable(ThreadFactory threadFactory) throws Exception { - Map expectedContextAttributes = new HashMap<>(); - expectedContextAttributes.put(HttpServletRequest.class, this.servletRequest); - expectedContextAttributes.put(HttpServletResponse.class, this.servletResponse); - expectedContextAttributes.put(Authentication.class, this.authentication); - - try (SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor(threadFactory)) { - Future> future = taskExecutor.submit(this::propagateRequestAttributes); - assertThat(future.get()).isEqualTo(expectedContextAttributes); - } - } - - private Map propagateRequestAttributes() { - RequestAttributes requestAttributes = new ServletRequestAttributes(this.servletRequest, this.servletResponse); - RequestContextHolder.setRequestAttributes(requestAttributes); - - SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); - securityContext.setAuthentication(this.authentication); - SecurityContextHolder.setContext(securityContext); - - // @formatter:off - return Mono.deferContextual(Mono::just) - .filter((ctx) -> ctx.hasKey(SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES)) - .map((ctx) -> ctx.>get(SecurityReactorContextSubscriber.SECURITY_CONTEXT_ATTRIBUTES)) - .map((attributes) -> { - Map map = new HashMap<>(); - // Copy over items from lazily loaded map - Arrays.asList(HttpServletRequest.class, HttpServletResponse.class, Authentication.class) - .forEach((key) -> map.put(key, attributes.get(key))); - return map; - }) - .block(); - // @formatter:on - } - @Configuration @EnableWebSecurity static class SecurityConfig { diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AbstractRequestMatcherBuilderRegistryTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AbstractRequestMatcherBuilderRegistryTests.java deleted file mode 100644 index ef29d6a86c..0000000000 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AbstractRequestMatcherBuilderRegistryTests.java +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers; - -import java.util.List; -import java.util.function.Consumer; - -import jakarta.servlet.Servlet; -import jakarta.servlet.ServletContext; -import org.assertj.core.api.AbstractObjectAssert; -import org.assertj.core.api.ObjectAssert; -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.context.ApplicationContext; -import org.springframework.http.HttpMethod; -import org.springframework.security.config.MockServletContext; -import org.springframework.security.config.annotation.ObjectPostProcessor; -import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; -import org.springframework.security.web.util.matcher.AndRequestMatcher; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.web.context.support.GenericWebApplicationContext; -import org.springframework.web.servlet.DispatcherServlet; -import org.springframework.web.servlet.handler.HandlerMappingIntrospector; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link AbstractRequestMatcherBuilderRegistry} - */ -class AbstractRequestMatcherBuilderRegistryTests { - - @Test - void defaultServletMatchersWhenDefaultDispatcherServletThenMvc() { - MockServletContext servletContext = MockServletContext.mvc(); - List matchers = defaultServlet(servletContext).requestMatchers("/mvc").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(MvcRequestMatcher.class); - assertThatMvc(matchers).servletPath().isNull(); - assertThatMvc(matchers).pattern().isEqualTo("/mvc"); - assertThatMvc(matchers).method().isNull(); - } - - @Test - void defaultServletHttpMethodMatchersWhenDefaultDispatcherServletThenMvc() { - MockServletContext servletContext = MockServletContext.mvc(); - List matchers = defaultServlet(servletContext).requestMatchers(HttpMethod.GET, "/mvc").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(MvcRequestMatcher.class); - assertThatMvc(matchers).servletPath().isNull(); - assertThatMvc(matchers).pattern().isEqualTo("/mvc"); - assertThatMvc(matchers).method().isEqualTo(HttpMethod.GET); - } - - @Test - void servletMatchersWhenPathDispatcherServletThenMvc() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*"); - List matchers = servletPattern(servletContext, "/mvc/*") - .requestMatchers("/controller").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(MvcRequestMatcher.class); - assertThatMvc(matchers).servletPath().isEqualTo("/mvc"); - assertThatMvc(matchers).pattern().isEqualTo("/controller"); - } - - @Test - void servletMatchersWhenAlsoExtraServletContainerMappingsThenMvc() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("default", Servlet.class); - servletContext.addServlet("jspServlet", Servlet.class).addMapping("*.jsp", "*.jspx"); - servletContext.addServlet("facesServlet", Servlet.class).addMapping("/faces/", "*.jsf", "*.faces", "*.xhtml"); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*"); - List matchers = servletPattern(servletContext, "/mvc/*") - .requestMatchers("/controller").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(MvcRequestMatcher.class); - assertThatMvc(matchers).servletPath().isEqualTo("/mvc"); - assertThatMvc(matchers).pattern().isEqualTo("/controller"); - } - - @Test - void defaultServletMatchersWhenOnlyDefaultServletThenAnt() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("default", Servlet.class).addMapping("/"); - List matchers = defaultServlet(servletContext).requestMatchers("/controller").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(AntPathRequestMatcher.class); - assertThatAnt(matchers).pattern().isEqualTo("/controller"); - } - - @Test - void defaultDispatcherServletMatchersWhenNoHandlerMappingIntrospectorThenException() { - MockServletContext servletContext = MockServletContext.mvc(); - assertThatExceptionOfType(NoSuchBeanDefinitionException.class) - .isThrownBy(() -> defaultServlet(servletContext, (context) -> { - })); - } - - @Test - void dispatcherServletMatchersWhenNoHandlerMappingIntrospectorThenException() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*"); - assertThatExceptionOfType(NoSuchBeanDefinitionException.class) - .isThrownBy(() -> servletPattern(servletContext, (context) -> { - }, "/mvc/*")); - } - - @Test - void matchersWhenNoDispatchServletThenAnt() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("default", Servlet.class).addMapping("/"); - servletContext.addServlet("messageDispatcherServlet", Servlet.class).addMapping("/services/*"); - List matchers = defaultServlet(servletContext).requestMatchers("/services/endpoint").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(AntPathRequestMatcher.class); - assertThatAnt(matchers).pattern().isEqualTo("/services/endpoint"); - } - - @Test - void servletMatchersWhenMixedServletsThenDeterminesByServletPath() { - MockServletContext servletContext = MockServletContext.mvc(); - servletContext.addServlet("messageDispatcherServlet", Servlet.class).addMapping("/services/*"); - List matchers = servletPattern(servletContext, "/services/*") - .requestMatchers("/endpoint").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(AntPathRequestMatcher.class); - assertThatAnt(matchers).pattern().isEqualTo("/services/endpoint"); - matchers = defaultServlet(servletContext).requestMatchers("/controller").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(MvcRequestMatcher.class); - assertThatMvc(matchers).servletPath().isNull(); - assertThatMvc(matchers).pattern().isEqualTo("/controller"); - } - - @Test - void servletMatchersWhenDispatcherServletNotDefaultThenDeterminesByServletPath() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("default", Servlet.class).addMapping("/"); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*"); - List matchers = servletPattern(servletContext, "/mvc/*") - .requestMatchers("/controller").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(MvcRequestMatcher.class); - assertThatMvc(matchers).servletPath().isEqualTo("/mvc"); - assertThatMvc(matchers).pattern().isEqualTo("/controller"); - matchers = defaultServlet(servletContext).requestMatchers("/endpoint").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(AntPathRequestMatcher.class); - assertThatAnt(matchers).pattern().isEqualTo("/endpoint"); - } - - @Test - void servletHttpMatchersWhenDispatcherServletNotDefaultThenDeterminesByServletPath() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("default", Servlet.class).addMapping("/"); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*"); - List matchers = servletPattern(servletContext, "/mvc/*").requestMatchers(HttpMethod.GET, - "/controller").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(MvcRequestMatcher.class); - assertThatMvc(matchers).method().isEqualTo(HttpMethod.GET); - assertThatMvc(matchers).servletPath().isEqualTo("/mvc"); - assertThatMvc(matchers).pattern().isEqualTo("/controller"); - matchers = defaultServlet(servletContext).requestMatchers(HttpMethod.GET, "/endpoint").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(AntPathRequestMatcher.class); - assertThatAnt(matchers).method().isEqualTo(HttpMethod.GET); - assertThatAnt(matchers).pattern().isEqualTo("/endpoint"); - } - - @Test - void servletMatchersWhenTwoDispatcherServletsThenDeterminesByServletPath() { - MockServletContext servletContext = MockServletContext.mvc(); - servletContext.addServlet("two", DispatcherServlet.class).addMapping("/other/*"); - List matchers = defaultServlet(servletContext).requestMatchers("/controller").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(MvcRequestMatcher.class); - assertThatMvc(matchers).servletPath().isNull(); - assertThatMvc(matchers).pattern().isEqualTo("/controller"); - matchers = servletPattern(servletContext, "/other/*").requestMatchers("/endpoint").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(MvcRequestMatcher.class); - assertThatMvc(matchers).servletPath().isEqualTo("/other"); - assertThatMvc(matchers).pattern().isEqualTo("/endpoint"); - } - - @Test - void servletMatchersWhenMoreThanOneMappingThenDeterminesByServletPath() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/", "/two/*"); - List matchers = defaultServlet(servletContext).requestMatchers("/controller").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(MvcRequestMatcher.class); - assertThatMvc(matchers).servletPath().isNull(); - assertThatMvc(matchers).pattern().isEqualTo("/controller"); - matchers = servletPattern(servletContext, "/two/*").requestMatchers("/endpoint").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(MvcRequestMatcher.class); - assertThatMvc(matchers).servletPath().isEqualTo("/two"); - assertThatMvc(matchers).pattern().isEqualTo("/endpoint"); - } - - @Test - void servletMatchersWhenMoreThanOneMappingAndDefaultServletsThenDeterminesByServletPath() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/", "/two/*"); - servletContext.addServlet("jspServlet", Servlet.class).addMapping("*.jsp", "*.jspx"); - List matchers = defaultServlet(servletContext).requestMatchers("/controller").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(MvcRequestMatcher.class); - assertThatMvc(matchers).servletPath().isNull(); - assertThatMvc(matchers).pattern().isEqualTo("/controller"); - matchers = servletPattern(servletContext, "/two/*").requestMatchers("/endpoint").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(MvcRequestMatcher.class); - assertThatMvc(matchers).servletPath().isEqualTo("/two"); - assertThatMvc(matchers).pattern().isEqualTo("/endpoint"); - } - - @Test - void defaultServletWhenDispatcherServletThenMvc() { - MockServletContext servletContext = MockServletContext.mvc(); - servletContext.addServlet("messageDispatcherServlet", Servlet.class).addMapping("/services/*"); - List matchers = defaultServlet(servletContext).requestMatchers("/controller").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(MvcRequestMatcher.class); - assertThatMvc(matchers).servletPath().isNull(); - assertThatMvc(matchers).pattern().isEqualTo("/controller"); - matchers = servletPattern(servletContext, "/services/*").requestMatchers("/endpoint").matchers; - assertThat(matchers).hasSize(1).hasOnlyElementsOfType(AntPathRequestMatcher.class); - assertThatAnt(matchers).pattern().isEqualTo("/services/endpoint"); - } - - @Test - void defaultServletWhenNoDefaultServletThenException() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("messageDispatcherServlet", Servlet.class).addMapping("/services/*"); - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> defaultServlet(servletContext)); - } - - @Test - void servletPathWhenNoMatchingServletThenException() { - MockServletContext servletContext = MockServletContext.mvc(); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> servletPattern(servletContext, "/wrong/*")); - } - - TestServletRequestMatcherRegistry defaultServlet(ServletContext servletContext) { - return servletPattern(servletContext, "/"); - } - - TestServletRequestMatcherRegistry defaultServlet(ServletContext servletContext, - Consumer consumer) { - return servletPattern(servletContext, consumer, "/"); - } - - TestServletRequestMatcherRegistry servletPattern(ServletContext servletContext, String pattern) { - return servletPattern(servletContext, (context) -> { - context.registerBean("mvcHandlerMappingIntrospector", HandlerMappingIntrospector.class); - context.registerBean(ObjectPostProcessor.class, () -> mock(ObjectPostProcessor.class)); - }, pattern); - } - - TestServletRequestMatcherRegistry servletPattern(ServletContext servletContext, - Consumer consumer, String pattern) { - GenericWebApplicationContext context = new GenericWebApplicationContext(servletContext); - consumer.accept(context); - context.refresh(); - return new TestServletRequestMatcherRegistry(context, pattern); - } - - static MvcRequestMatcherAssert assertThatMvc(List matchers) { - RequestMatcher matcher = matchers.get(0); - if (matcher instanceof AndRequestMatcher matching) { - List and = (List) ReflectionTestUtils.getField(matching, "requestMatchers"); - assertThat(and).hasSize(2); - assertThat(and.get(1)).isInstanceOf(MvcRequestMatcher.class); - return new MvcRequestMatcherAssert((MvcRequestMatcher) and.get(1)); - } - assertThat(matcher).isInstanceOf(MvcRequestMatcher.class); - return new MvcRequestMatcherAssert((MvcRequestMatcher) matcher); - } - - static AntPathRequestMatcherAssert assertThatAnt(List matchers) { - RequestMatcher matcher = matchers.get(0); - if (matcher instanceof AndRequestMatcher matching) { - List and = (List) ReflectionTestUtils.getField(matching, "requestMatchers"); - assertThat(and).hasSize(2); - assertThat(and.get(1)).isInstanceOf(AntPathRequestMatcher.class); - return new AntPathRequestMatcherAssert((AntPathRequestMatcher) and.get(1)); - } - assertThat(matcher).isInstanceOf(AntPathRequestMatcher.class); - return new AntPathRequestMatcherAssert((AntPathRequestMatcher) matcher); - } - - static final class TestServletRequestMatcherRegistry - extends AbstractRequestMatcherBuilderRegistry { - - List matchers; - - TestServletRequestMatcherRegistry(ApplicationContext context, String pattern) { - super(context, RequestMatcherBuilders.createForServletPattern(context, pattern)); - } - - @Override - protected TestServletRequestMatcherRegistry chainRequestMatchers(List requestMatchers) { - this.matchers = requestMatchers; - return this; - } - - } - - static final class MvcRequestMatcherAssert extends ObjectAssert { - - private MvcRequestMatcherAssert(MvcRequestMatcher matcher) { - super(matcher); - } - - AbstractObjectAssert servletPath() { - return extracting("servletPath"); - } - - AbstractObjectAssert pattern() { - return extracting("pattern"); - } - - AbstractObjectAssert method() { - return extracting("method"); - } - - } - - static final class AntPathRequestMatcherAssert extends ObjectAssert { - - private AntPathRequestMatcherAssert(AntPathRequestMatcher matcher) { - super(matcher); - } - - AbstractObjectAssert pattern() { - return extracting("pattern"); - } - - AbstractObjectAssert method() { - return extracting("httpMethod"); - } - - } - -} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java index 19d5de6f24..252117063b 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java @@ -18,7 +18,6 @@ package org.springframework.security.config.annotation.web.configurers; import java.util.function.Supplier; -import jakarta.servlet.Servlet; import jakarta.servlet.http.HttpServletRequest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -27,7 +26,6 @@ import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpMethod; import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; import org.springframework.security.authentication.RememberMeAuthenticationToken; @@ -35,13 +33,10 @@ import org.springframework.security.authentication.TestAuthentication; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationEventPublisher; import org.springframework.security.authorization.AuthorizationManager; -import org.springframework.security.config.MockServletContext; -import org.springframework.security.config.TestMockHttpServletMappings; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.core.authority.AuthorityUtils; @@ -62,7 +57,6 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.handler.HandlerMappingIntrospector; @@ -76,7 +70,6 @@ import static org.springframework.security.test.web.servlet.request.SecurityMock import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.head; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -127,7 +120,7 @@ public class AuthorizeHttpRequestsConfigurerTests { public void configureWhenMvcMatcherAfterAnyRequestThenException() { assertThatExceptionOfType(BeanCreationException.class) .isThrownBy(() -> this.spring.register(AfterAnyRequestConfig.class).autowire()) - .withMessageContaining("Can't configure requestMatchers after anyRequest"); + .withMessageContaining("Can't configure mvcMatchers after anyRequest"); } @Test @@ -368,7 +361,7 @@ public class AuthorizeHttpRequestsConfigurerTests { @Test public void getWhenServletPathRoleAdminConfiguredAndRoleIsUserThenRespondsWithForbidden() throws Exception { - this.spring.register(MvcServletPathConfig.class, BasicController.class).autowire(); + this.spring.register(ServletPathConfig.class, BasicController.class).autowire(); // @formatter:off MockHttpServletRequestBuilder requestWithUser = get("/spring/") .servletPath("/spring") @@ -381,7 +374,7 @@ public class AuthorizeHttpRequestsConfigurerTests { @Test public void getWhenServletPathRoleAdminConfiguredAndRoleIsUserAndWithoutServletPathThenRespondsWithForbidden() throws Exception { - this.spring.register(MvcServletPathConfig.class, BasicController.class).autowire(); + this.spring.register(ServletPathConfig.class, BasicController.class).autowire(); // @formatter:off MockHttpServletRequestBuilder requestWithUser = get("/") .with(user("user") @@ -392,7 +385,7 @@ public class AuthorizeHttpRequestsConfigurerTests { @Test public void getWhenServletPathRoleAdminConfiguredAndRoleIsAdminThenRespondsWithOk() throws Exception { - this.spring.register(MvcServletPathConfig.class, BasicController.class).autowire(); + this.spring.register(ServletPathConfig.class, BasicController.class).autowire(); // @formatter:off MockHttpServletRequestBuilder requestWithAdmin = get("/spring/") .servletPath("/spring") @@ -483,43 +476,6 @@ public class AuthorizeHttpRequestsConfigurerTests { this.mvc.perform(requestWithRoleOther).andExpect(status().isForbidden()); } - @Test - public void getWhenCustomRolePrefixAndRoleHasDifferentPrefixThenRespondsWithForbidden() throws Exception { - this.spring.register(GrantedAuthorityDefaultHasRoleConfig.class, BasicController.class).autowire(); - // @formatter:off - MockHttpServletRequestBuilder requestWithUser = get("/") - .with(user("user") - .authorities(new SimpleGrantedAuthority("ROLE_USER"))); - // @formatter:on - this.mvc.perform(requestWithUser).andExpect(status().isForbidden()); - } - - @Test - public void getWhenCustomRolePrefixAndHasRoleThenRespondsWithOk() throws Exception { - this.spring.register(GrantedAuthorityDefaultHasRoleConfig.class, BasicController.class).autowire(); - // @formatter:off - MockHttpServletRequestBuilder requestWithUser = get("/") - .with(user("user") - .authorities(new SimpleGrantedAuthority("CUSTOM_PREFIX_USER"))); - // @formatter:on - this.mvc.perform(requestWithUser).andExpect(status().isOk()); - } - - @Test - public void getWhenCustomRolePrefixAndHasAnyRoleThenRespondsWithOk() throws Exception { - this.spring.register(GrantedAuthorityDefaultHasAnyRoleConfig.class, BasicController.class).autowire(); - // @formatter:off - MockHttpServletRequestBuilder requestWithUser = get("/") - .with(user("user") - .authorities(new SimpleGrantedAuthority("CUSTOM_PREFIX_USER"))); - MockHttpServletRequestBuilder requestWithAdmin = get("/") - .with(user("user") - .authorities(new SimpleGrantedAuthority("CUSTOM_PREFIX_ADMIN"))); - // @formatter:on - this.mvc.perform(requestWithUser).andExpect(status().isOk()); - this.mvc.perform(requestWithAdmin).andExpect(status().isOk()); - } - @Test public void getWhenExpressionHasIpAddressLocalhostConfiguredIpAddressIsLocalhostThenRespondsWithOk() throws Exception { @@ -602,232 +558,6 @@ public class AuthorizeHttpRequestsConfigurerTests { this.mvc.perform(requestWithUser).andExpect(status().isForbidden()); } - @Test - public void configureWhenNoDispatcherServletThenSucceeds() throws Exception { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("default", Servlet.class).addMapping("/"); - this.spring.register(AuthorizeHttpRequestsConfig.class) - .postProcessor((context) -> context.setServletContext(servletContext)) - .autowire(); - this.mvc.perform(get("/path")).andExpect(status().isNotFound()); - } - - @Test - public void configureWhenOnlyDispatcherServletThenSucceeds() throws Exception { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*"); - this.spring.register(AuthorizeHttpRequestsConfig.class) - .postProcessor((context) -> context.setServletContext(servletContext)) - .autowire(); - this.mvc.perform(get("/mvc/path").servletPath("/mvc")).andExpect(status().isNotFound()); - this.mvc.perform(get("/mvc")).andExpect(status().isUnauthorized()); - } - - @Test - public void configureWhenMultipleServletsThenSucceeds() throws Exception { - MockServletContext servletContext = MockServletContext.mvc(); - servletContext.addServlet("path", Servlet.class).addMapping("/path/*"); - this.spring.register(AuthorizeHttpRequestsConfig.class) - .postProcessor((context) -> context.setServletContext(servletContext)) - .autowire(); - this.mvc.perform(get("/path").with(servletPath("/path"))).andExpect(status().isNotFound()); - } - - @Test - public void configureWhenAmbiguousServletsThenWiringException() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*"); - servletContext.addServlet("path", Servlet.class).addMapping("/path/*"); - assertThatExceptionOfType(BeanCreationException.class) - .isThrownBy(() -> this.spring.register(AuthorizeHttpRequestsConfig.class) - .postProcessor((context) -> context.setServletContext(servletContext)) - .autowire()); - } - - @Test - void defaultServletMatchersWhenDefaultServletThenPermits() throws Exception { - this.spring.register(DefaultServletConfig.class) - .postProcessor((context) -> context.setServletContext(MockServletContext.mvc())) - .autowire(); - this.mvc.perform(get("/path/path").with(defaultServlet())).andExpect(status().isNotFound()); - this.mvc.perform(get("/path/path").with(servletPath("/path"))).andExpect(status().isUnauthorized()); - } - - @Test - void defaultServletHttpMethodMatchersWhenDefaultServletThenPermits() throws Exception { - this.spring.register(DefaultServletConfig.class) - .postProcessor((context) -> context.setServletContext(MockServletContext.mvc())) - .autowire(); - this.mvc.perform(get("/path/method").with(defaultServlet())).andExpect(status().isNotFound()); - this.mvc.perform(head("/path/method").with(defaultServlet())).andExpect(status().isUnauthorized()); - this.mvc.perform(get("/path/method").with(servletPath("/path"))).andExpect(status().isUnauthorized()); - } - - @Test - void defaultServletWhenNoDefaultServletThenWiringException() { - assertThatExceptionOfType(BeanCreationException.class) - .isThrownBy(() -> this.spring.register(DefaultServletConfig.class) - .postProcessor((context) -> context.setServletContext(new MockServletContext())) - .autowire()); - } - - @Test - void servletPathMatchersWhenMatchingServletThenPermits() throws Exception { - MockServletContext servletContext = MockServletContext.mvc(); - servletContext.addServlet("path", Servlet.class).addMapping("/path/*"); - this.spring.register(ServletPathConfig.class) - .postProcessor((context) -> context.setServletContext(servletContext)) - .autowire(); - this.mvc.perform(get("/path/path").with(servletPath("/path"))).andExpect(status().isNotFound()); - this.mvc.perform(get("/path/path").with(defaultServlet())).andExpect(status().isUnauthorized()); - } - - @Test - void servletPathHttpMethodMatchersWhenMatchingServletThenPermits() throws Exception { - MockServletContext servletContext = MockServletContext.mvc(); - servletContext.addServlet("path", Servlet.class).addMapping("/path/*"); - this.spring.register(ServletPathConfig.class) - .postProcessor((context) -> context.setServletContext(servletContext)) - .autowire(); - this.mvc.perform(get("/path/method").with(servletPath("/path"))).andExpect(status().isNotFound()); - this.mvc.perform(head("/path/method").with(servletPath("/path"))).andExpect(status().isUnauthorized()); - this.mvc.perform(get("/path/method").with(defaultServlet())).andExpect(status().isUnauthorized()); - } - - @Test - void servletPathWhenNoMatchingPathThenWiringException() { - MockServletContext servletContext = MockServletContext.mvc(); - assertThatExceptionOfType(BeanCreationException.class) - .isThrownBy(() -> this.spring.register(ServletPathConfig.class) - .postProcessor((context) -> context.setServletContext(servletContext)) - .autowire()); - } - - @Test - void servletMappingMatchersWhenMatchingServletThenPermits() throws Exception { - MockServletContext servletContext = MockServletContext.mvc(); - servletContext.addServlet("jsp", Servlet.class).addMapping("*.jsp"); - this.spring.register(ServletMappingConfig.class) - .postProcessor((context) -> context.setServletContext(servletContext)) - .autowire(); - this.mvc.perform(get("/path/file.jsp").with(servletExtension(".jsp"))).andExpect(status().isNotFound()); - this.mvc.perform(get("/path/file.jsp").with(defaultServlet())).andExpect(status().isUnauthorized()); - } - - @Test - void servletMappingHttpMethodMatchersWhenMatchingServletThenPermits() throws Exception { - MockServletContext servletContext = MockServletContext.mvc(); - servletContext.addServlet("jsp", Servlet.class).addMapping("*.jsp"); - this.spring.register(ServletMappingConfig.class) - .postProcessor((context) -> context.setServletContext(servletContext)) - .autowire(); - this.mvc.perform(get("/method/file.jsp").with(servletExtension(".jsp"))).andExpect(status().isNotFound()); - this.mvc.perform(head("/method/file.jsp").with(servletExtension(".jsp"))).andExpect(status().isUnauthorized()); - this.mvc.perform(get("/method/file.jsp").with(defaultServlet())).andExpect(status().isUnauthorized()); - } - - @Test - void servletMappingWhenNoMatchingExtensionThenWiringException() { - MockServletContext servletContext = MockServletContext.mvc(); - assertThatExceptionOfType(BeanCreationException.class) - .isThrownBy(() -> this.spring.register(ServletMappingConfig.class) - .postProcessor((context) -> context.setServletContext(servletContext)) - .autowire()); - } - - @Test - void anyRequestWhenUsedWithDefaultServletThenDoesNotWire() { - assertThatExceptionOfType(BeanCreationException.class) - .isThrownBy(() -> this.spring.register(MixedServletEndpointConfig.class).autowire()) - .withMessageContaining("forServletPattern"); - } - - @Test - void servletWhenNoMatchingPathThenDenies() throws Exception { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("default", Servlet.class).addMapping("/"); - servletContext.addServlet("jspServlet", Servlet.class).addMapping("*.jsp"); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*"); - this.spring.register(DefaultServletAndServletPathConfig.class) - .postProcessor((context) -> context.setServletContext(servletContext)) - .autowire(); - this.mvc.perform(get("/js/color.js").with(servletPath("/js"))).andExpect(status().isUnauthorized()); - this.mvc.perform(get("/mvc/controller").with(defaultServlet())).andExpect(status().isUnauthorized()); - this.mvc.perform(get("/js/color.js").with(defaultServlet())).andExpect(status().isNotFound()); - this.mvc.perform(get("/mvc/controller").with(servletPath("/mvc"))).andExpect(status().isUnauthorized()); - this.mvc.perform(get("/mvc/controller").with(user("user")).with(servletPath("/mvc"))) - .andExpect(status().isNotFound()); - } - - @Test - void permitAllWhenDefaultServletThenDoesNotWire() { - assertThatExceptionOfType(BeanCreationException.class) - .isThrownBy(() -> this.spring.register(MixedServletPermitAllConfig.class).autowire()) - .withMessageContaining("forServletPattern"); - } - - static RequestPostProcessor defaultServlet() { - return (request) -> { - String uri = request.getRequestURI(); - request.setHttpServletMapping(TestMockHttpServletMappings.defaultMapping()); - request.setServletPath(uri); - request.setPathInfo(""); - return request; - }; - } - - static RequestPostProcessor servletPath(String path) { - return (request) -> { - String uri = request.getRequestURI(); - request.setHttpServletMapping(TestMockHttpServletMappings.path(request, path)); - request.setServletPath(path); - request.setPathInfo(uri.substring(path.length())); - return request; - }; - } - - static RequestPostProcessor servletExtension(String extension) { - return (request) -> { - String uri = request.getRequestURI(); - request.setHttpServletMapping(TestMockHttpServletMappings.extension(request, extension)); - request.setServletPath(uri); - request.setPathInfo(""); - return request; - }; - } - - @Configuration - @EnableWebSecurity - static class GrantedAuthorityDefaultHasRoleConfig { - - @Bean - GrantedAuthorityDefaults grantedAuthorityDefaults() { - return new GrantedAuthorityDefaults("CUSTOM_PREFIX_"); - } - - @Bean - SecurityFilterChain myFilterChain(HttpSecurity http) throws Exception { - return http.authorizeHttpRequests((c) -> c.anyRequest().hasRole("USER")).build(); - } - - } - - @Configuration - @EnableWebSecurity - static class GrantedAuthorityDefaultHasAnyRoleConfig { - - @Bean - GrantedAuthorityDefaults grantedAuthorityDefaults() { - return new GrantedAuthorityDefaults("CUSTOM_PREFIX_"); - } - - @Bean - SecurityFilterChain myFilterChain(HttpSecurity http) throws Exception { - return http.authorizeHttpRequests((c) -> c.anyRequest().hasAnyRole("USER", "ADMIN")).build(); - } - - } - @Configuration @EnableWebSecurity static class NoRequestsConfig { @@ -893,7 +623,6 @@ public class AuthorizeHttpRequestsConfigurerTests { @Configuration @EnableWebSecurity - @EnableWebMvc static class AfterAnyRequestConfig { @Bean @@ -1155,7 +884,7 @@ public class AuthorizeHttpRequestsConfigurerTests { @Configuration @EnableWebMvc @EnableWebSecurity - static class MvcServletPathConfig { + static class ServletPathConfig { @Bean SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospector introspector) throws Exception { @@ -1337,163 +1066,6 @@ public class AuthorizeHttpRequestsConfigurerTests { } - @Configuration - @EnableWebSecurity - @EnableWebMvc - static class AuthorizeHttpRequestsConfig { - - @Bean - SecurityFilterChain chain(HttpSecurity http) throws Exception { - // @formatter:off - http - .httpBasic(withDefaults()) - .authorizeHttpRequests((requests) -> requests - .requestMatchers("/path/**").permitAll() - .anyRequest().authenticated() - ); - // @formatter:on - return http.build(); - } - - } - - @Configuration - @EnableWebSecurity - @EnableWebMvc - static class DefaultServletConfig { - - @Bean - SecurityFilterChain chain(HttpSecurity http) throws Exception { - // @formatter:off - http - .httpBasic(withDefaults()) - .authorizeHttpRequests((requests) -> requests - .forServletPattern("/", (root) -> root - .requestMatchers(HttpMethod.GET, "/path/method/**").permitAll() - .requestMatchers("/path/path/**").permitAll() - .anyRequest().authenticated() - ) - ); - // @formatter:on - return http.build(); - } - - } - - @Configuration - @EnableWebSecurity - @EnableWebMvc - static class ServletPathConfig { - - @Bean - SecurityFilterChain chain(HttpSecurity http) throws Exception { - // @formatter:off - http - .httpBasic(withDefaults()) - .authorizeHttpRequests((requests) -> requests - .forServletPattern("/path/*", (root) -> root - .requestMatchers(HttpMethod.GET, "/method/**").permitAll() - .requestMatchers("/path/**").permitAll() - .anyRequest().authenticated() - ) - ); - // @formatter:on - return http.build(); - } - - } - - @Configuration - @EnableWebSecurity - @EnableWebMvc - static class ServletMappingConfig { - - @Bean - SecurityFilterChain chain(HttpSecurity http) throws Exception { - // @formatter:off - http - .httpBasic(withDefaults()) - .authorizeHttpRequests((requests) -> requests - .forServletPattern("*.jsp", (jsp) -> jsp - .requestMatchers(HttpMethod.GET, "/method/**").permitAll() - .requestMatchers("/path/**").permitAll() - .anyRequest().authenticated() - ) - ); - // @formatter:on - return http.build(); - } - - } - - @Configuration - @EnableWebSecurity - @EnableWebMvc - static class MixedServletEndpointConfig { - - @Bean - SecurityFilterChain chain(HttpSecurity http) throws Exception { - // @formatter:off - http - .httpBasic(withDefaults()) - .authorizeHttpRequests((requests) -> requests - .forServletPattern("/", (root) -> root.anyRequest().permitAll()) - .anyRequest().authenticated() - ); - // @formatter:on - return http.build(); - } - - } - - @Configuration - @EnableWebSecurity - @EnableWebMvc - static class MixedServletPermitAllConfig { - - @Bean - SecurityFilterChain chain(HttpSecurity http) throws Exception { - // @formatter:off - http - .formLogin((form) -> form.loginPage("/page").permitAll()) - .authorizeHttpRequests((requests) -> requests - .forServletPattern("/", (root) -> root - .anyRequest().authenticated() - ) - ); - // @formatter:on - return http.build(); - } - - } - - @Configuration - @EnableWebSecurity - @EnableWebMvc - static class DefaultServletAndServletPathConfig { - - @Bean - SecurityFilterChain chain(HttpSecurity http) throws Exception { - // @formatter:off - http - .httpBasic(withDefaults()) - .authorizeHttpRequests((requests) -> requests - .forServletPattern("/", (root) -> root - .requestMatchers("/js/**", "/css/**").permitAll() - ) - .forServletPattern("/mvc/*", (mvc) -> mvc - .requestMatchers("/controller/**").authenticated() - ) - .forServletPattern("*.jsp", (jsp) -> jsp - .anyRequest().authenticated() - ) - ); - // @formatter:on - return http.build(); - } - - } - @Configuration static class AuthorizationEventPublisherConfig { diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultFiltersTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultFiltersTests.java index d551a2e305..6bf4158278 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultFiltersTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultFiltersTests.java @@ -85,10 +85,10 @@ public class DefaultFiltersTests { List filterChains = this.spring.getContext() .getBean(FilterChainProxy.class) .getFilterChains(); - assertThat(filterChains).hasSize(1); + assertThat(filterChains.size()).isEqualTo(1); DefaultSecurityFilterChain filterChain = (DefaultSecurityFilterChain) filterChains.get(0); assertThat(filterChain.getRequestMatcher()).isInstanceOf(AnyRequestMatcher.class); - assertThat(filterChain.getFilters()).hasSize(1); + assertThat(filterChain.getFilters().size()).isEqualTo(1); long filter = filterChain.getFilters() .stream() .filter((it) -> it instanceof UsernamePasswordAuthenticationFilter) @@ -102,25 +102,25 @@ public class DefaultFiltersTests { List filterChains = this.spring.getContext() .getBean(FilterChainProxy.class) .getFilterChains(); - assertThat(filterChains).hasSize(2); + assertThat(filterChains.size()).isEqualTo(2); DefaultSecurityFilterChain firstFilter = (DefaultSecurityFilterChain) filterChains.get(0); DefaultSecurityFilterChain secondFilter = (DefaultSecurityFilterChain) filterChains.get(1); assertThat(firstFilter.getFilters().isEmpty()).isEqualTo(true); assertThat(secondFilter.getRequestMatcher()).isInstanceOf(AnyRequestMatcher.class); - List> classes = secondFilter.getFilters() + List> classes = secondFilter.getFilters() .stream() .map(Filter::getClass) .collect(Collectors.toList()); - assertThat(classes).contains(WebAsyncManagerIntegrationFilter.class); - assertThat(classes).contains(SecurityContextHolderFilter.class); - assertThat(classes).contains(HeaderWriterFilter.class); - assertThat(classes).contains(LogoutFilter.class); - assertThat(classes).contains(CsrfFilter.class); - assertThat(classes).contains(RequestCacheAwareFilter.class); - assertThat(classes).contains(SecurityContextHolderAwareRequestFilter.class); - assertThat(classes).contains(AnonymousAuthenticationFilter.class); - assertThat(classes).contains(ExceptionTranslationFilter.class); - assertThat(classes).contains(FilterSecurityInterceptor.class); + assertThat(classes.contains(WebAsyncManagerIntegrationFilter.class)).isTrue(); + assertThat(classes.contains(SecurityContextHolderFilter.class)).isTrue(); + assertThat(classes.contains(HeaderWriterFilter.class)).isTrue(); + assertThat(classes.contains(LogoutFilter.class)).isTrue(); + assertThat(classes.contains(CsrfFilter.class)).isTrue(); + assertThat(classes.contains(RequestCacheAwareFilter.class)).isTrue(); + assertThat(classes.contains(SecurityContextHolderAwareRequestFilter.class)).isTrue(); + assertThat(classes.contains(AnonymousAuthenticationFilter.class)).isTrue(); + assertThat(classes.contains(ExceptionTranslationFilter.class)).isTrue(); + assertThat(classes.contains(FilterSecurityInterceptor.class)).isTrue(); } @Test diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.java index 3b44f86ed8..4fbc0bc045 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/DefaultLoginPageConfigurerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -96,7 +96,7 @@ public class DefaultLoginPageConfigurerTests { + " \n" + " Please sign in\n" + " \n" - + " \n" + + " \n" + " \n" + " \n" + "
    \n" @@ -145,7 +145,7 @@ public class DefaultLoginPageConfigurerTests { + " \n" + " Please sign in\n" + " \n" - + " \n" + + " \n" + " \n" + " \n" + "
    \n" @@ -197,7 +197,7 @@ public class DefaultLoginPageConfigurerTests { + " \n" + " Please sign in\n" + " \n" - + " \n" + + " \n" + " \n" + " \n" + "
    \n" @@ -250,7 +250,7 @@ public class DefaultLoginPageConfigurerTests { + " \n" + " Please sign in\n" + " \n" - + " \n" + + " \n" + " \n" + " \n" + "
    \n" diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurerEagerHeadersTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurerEagerHeadersTests.java index 31468c5711..c805f16aa3 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurerEagerHeadersTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/HeadersConfigurerEagerHeadersTests.java @@ -31,8 +31,6 @@ import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.header.HeaderWriterFilter; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RestController; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; @@ -52,7 +50,7 @@ public class HeadersConfigurerEagerHeadersTests { @Test public void requestWhenHeadersEagerlyConfiguredThenHeadersAreWritten() throws Exception { - this.spring.register(HeadersAtTheBeginningOfRequestConfig.class, HomeController.class).autowire(); + this.spring.register(HeadersAtTheBeginningOfRequestConfig.class).autowire(); this.mvc.perform(get("/").secure(true)) .andExpect(header().string("X-Content-Type-Options", "nosniff")) .andExpect(header().string("X-Frame-Options", "DENY")) @@ -85,14 +83,4 @@ public class HeadersConfigurerEagerHeadersTests { } - @RestController - private static class HomeController { - - @GetMapping("/") - String ok() { - return "ok"; - } - - } - } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurerTests.java index 25cfac15e6..828707ab5a 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpBasicConfigurerTests.java @@ -107,7 +107,6 @@ public class HttpBasicConfigurerTests { @Test public void httpBasicWhenUsingCustomAuthenticationEntryPointThenResponseIncludesBasicChallenge() throws Exception { - CustomAuthenticationEntryPointConfig.ENTRY_POINT = mock(AuthenticationEntryPoint.class); this.spring.register(CustomAuthenticationEntryPointConfig.class).autowire(); this.mvc.perform(get("/")); verify(CustomAuthenticationEntryPointConfig.ENTRY_POINT).commence(any(HttpServletRequest.class), @@ -125,7 +124,7 @@ public class HttpBasicConfigurerTests { // SEC-3019 @Test public void httpBasicWhenRememberMeConfiguredThenSetsRememberMeCookie() throws Exception { - this.spring.register(BasicUsesRememberMeConfig.class, Home.class).autowire(); + this.spring.register(BasicUsesRememberMeConfig.class).autowire(); MockHttpServletRequestBuilder rememberMeRequest = get("/").with(httpBasic("user", "password")) .param("remember-me", "true"); this.mvc.perform(rememberMeRequest).andExpect(cookie().exists("remember-me")); @@ -243,7 +242,7 @@ public class HttpBasicConfigurerTests { @EnableWebSecurity static class CustomAuthenticationEntryPointConfig { - static AuthenticationEntryPoint ENTRY_POINT; + static AuthenticationEntryPoint ENTRY_POINT = mock(AuthenticationEntryPoint.class); @Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.java index 1b14a87b33..5bde6992a6 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestCacheConfigurerTests.java @@ -382,7 +382,7 @@ public class RequestCacheConfigurerTests { .anyRequest().authenticated() ) .formLogin(Customizer.withDefaults()) - .requestCache(RequestCacheConfigurer::disable); + .requestCache((cache) -> cache.disable()); // @formatter:on return http.build(); } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestMatcherBuildersTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestMatcherBuildersTests.java deleted file mode 100644 index 61fdf6b2be..0000000000 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/RequestMatcherBuildersTests.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers; - -import java.util.List; -import java.util.function.Consumer; - -import jakarta.servlet.Servlet; -import jakarta.servlet.ServletContext; -import org.junit.jupiter.api.Test; - -import org.springframework.http.HttpMethod; -import org.springframework.security.config.MockServletContext; -import org.springframework.security.config.annotation.ObjectPostProcessor; -import org.springframework.security.config.annotation.web.configurers.DispatcherServletDelegatingRequestMatcherBuilder.DispatcherServletDelegatingRequestMatcher; -import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.web.context.support.GenericWebApplicationContext; -import org.springframework.web.servlet.DispatcherServlet; -import org.springframework.web.servlet.handler.HandlerMappingIntrospector; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.Mockito.mock; - -public class RequestMatcherBuildersTests { - - @Test - void matchersWhenDefaultDispatcherServletThenMvc() { - MockServletContext servletContext = MockServletContext.mvc(); - RequestMatcherBuilder builder = requestMatchersBuilder(servletContext); - List matchers = builder.matchers("/mvc"); - assertThat(matchers.get(0)).isInstanceOf(MvcRequestMatcher.class); - MvcRequestMatcher matcher = (MvcRequestMatcher) matchers.get(0); - assertThat(ReflectionTestUtils.getField(matcher, "servletPath")).isNull(); - assertThat(ReflectionTestUtils.getField(matcher, "pattern")).isEqualTo("/mvc"); - } - - @Test - void httpMethodMatchersWhenDefaultDispatcherServletThenMvc() { - MockServletContext servletContext = MockServletContext.mvc(); - RequestMatcherBuilder builder = requestMatchersBuilder(servletContext); - List matchers = builder.matchers(HttpMethod.GET, "/mvc"); - assertThat(matchers.get(0)).isInstanceOf(MvcRequestMatcher.class); - MvcRequestMatcher matcher = (MvcRequestMatcher) matchers.get(0); - assertThat(ReflectionTestUtils.getField(matcher, "servletPath")).isNull(); - assertThat(ReflectionTestUtils.getField(matcher, "pattern")).isEqualTo("/mvc"); - assertThat(ReflectionTestUtils.getField(matcher, "method")).isEqualTo(HttpMethod.GET); - } - - @Test - void matchersWhenPathDispatcherServletThenMvc() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*"); - RequestMatcherBuilder builder = requestMatchersBuilder(servletContext); - List matchers = builder.matchers("/controller"); - assertThat(matchers.get(0)).isInstanceOf(MvcRequestMatcher.class); - MvcRequestMatcher matcher = (MvcRequestMatcher) matchers.get(0); - assertThat(ReflectionTestUtils.getField(matcher, "servletPath")).isEqualTo("/mvc"); - assertThat(ReflectionTestUtils.getField(matcher, "pattern")).isEqualTo("/controller"); - } - - @Test - void matchersWhenAlsoExtraServletContainerMappingsThenRequiresServletPath() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("default", Servlet.class).addMapping("/"); - servletContext.addServlet("jspServlet", Servlet.class).addMapping("*.jsp", "*.jspx"); - servletContext.addServlet("facesServlet", Servlet.class).addMapping("/faces/", "*.jsf", "*.faces", "*.xhtml"); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*"); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> requestMatchersBuilder(servletContext).matcher("/path")) - .withMessageContaining(".forServletPattern"); - } - - @Test - void matchersWhenOnlyDefaultServletThenAnt() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("default", Servlet.class).addMapping("/"); - RequestMatcherBuilder builder = requestMatchersBuilder(servletContext); - List matchers = builder.matchers("/controller"); - assertThat(matchers.get(0)).isInstanceOf(AntPathRequestMatcher.class); - AntPathRequestMatcher matcher = (AntPathRequestMatcher) matchers.get(0); - assertThat(ReflectionTestUtils.getField(matcher, "pattern")).isEqualTo("/controller"); - } - - @Test - void matchersWhenNoHandlerMappingIntrospectorThenAnt() { - MockServletContext servletContext = MockServletContext.mvc(); - RequestMatcherBuilder builder = requestMatchersBuilder(servletContext, (context) -> { - }); - List matchers = builder.matchers("/controller"); - assertThat(matchers.get(0)).isInstanceOf(AntPathRequestMatcher.class); - AntPathRequestMatcher matcher = (AntPathRequestMatcher) matchers.get(0); - assertThat(ReflectionTestUtils.getField(matcher, "pattern")).isEqualTo("/controller"); - } - - @Test - void matchersWhenNoDispatchServletThenAnt() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("default", Servlet.class).addMapping("/"); - servletContext.addServlet("messageDispatcherServlet", Servlet.class).addMapping("/services/*"); - RequestMatcherBuilder builder = requestMatchersBuilder(servletContext); - List matchers = builder.matchers("/services/endpoint"); - assertThat(matchers.get(0)).isInstanceOf(AntPathRequestMatcher.class); - AntPathRequestMatcher matcher = (AntPathRequestMatcher) matchers.get(0); - assertThat(ReflectionTestUtils.getField(matcher, "pattern")).isEqualTo("/services/endpoint"); - } - - @Test - void matchersWhenMixedServletsThenServletPathDelegating() { - MockServletContext servletContext = MockServletContext.mvc(); - servletContext.addServlet("messageDispatcherServlet", Servlet.class).addMapping("/services/*"); - RequestMatcherBuilder builder = requestMatchersBuilder(servletContext); - assertThat(builder.matchers("/services/endpoint").get(0)) - .isInstanceOf(DispatcherServletDelegatingRequestMatcher.class); - } - - @Test - void matchersWhenDispatcherServletNotDefaultAndOtherServletsThenRequiresServletPath() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("default", Servlet.class).addMapping("/"); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*"); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> requestMatchersBuilder(servletContext).matcher("/path/**")) - .withMessageContaining(".forServletPattern"); - } - - @Test - void httpMatchersWhenDispatcherServletNotDefaultAndOtherServletsThenRequiresServletPath() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("default", Servlet.class).addMapping("/"); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*"); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> requestMatchersBuilder(servletContext).matcher("/pattern")) - .withMessageContaining(".forServletPattern"); - } - - @Test - void matchersWhenTwoDispatcherServletsThenException() { - MockServletContext servletContext = MockServletContext.mvc(); - servletContext.addServlet("two", DispatcherServlet.class).addMapping("/other/*"); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> requestMatchersBuilder(servletContext).matcher("/path/**")) - .withMessageContaining(".forServletPattern"); - } - - @Test - void matchersWhenMoreThanOneMappingThenException() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/", "/two/*"); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> requestMatchersBuilder(servletContext).matcher("/path/**")) - .withMessageContaining(".forServletPattern"); - } - - @Test - void matchersWhenMoreThanOneMappingAndDefaultServletsThenRequiresServletPath() { - MockServletContext servletContext = new MockServletContext(); - servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/", "/two/*"); - servletContext.addServlet("jspServlet", Servlet.class).addMapping("*.jsp", "*.jspx"); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> requestMatchersBuilder(servletContext).matcher("/path/**")) - .withMessageContaining(".forServletPattern"); - } - - RequestMatcherBuilder requestMatchersBuilder(ServletContext servletContext) { - return requestMatchersBuilder(servletContext, (context) -> { - context.registerBean("mvcHandlerMappingIntrospector", HandlerMappingIntrospector.class, - () -> mock(HandlerMappingIntrospector.class)); - context.registerBean(ObjectPostProcessor.class, () -> mock(ObjectPostProcessor.class)); - }); - } - - RequestMatcherBuilder requestMatchersBuilder(ServletContext servletContext, - Consumer consumer) { - GenericWebApplicationContext context = new GenericWebApplicationContext(servletContext); - consumer.accept(context); - context.refresh(); - return RequestMatcherBuilders.createDefault(context); - } - -} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ServletPatternRequestMatcherTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ServletPatternRequestMatcherTests.java deleted file mode 100644 index 98a371bbc7..0000000000 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/ServletPatternRequestMatcherTests.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers; - -import org.junit.jupiter.api.Test; - -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.security.config.TestMockHttpServletMappings; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link ServletPatternRequestMatcher} - */ -class ServletPatternRequestMatcherTests { - - ServletPatternRequestMatcher matcher = new ServletPatternRequestMatcher("*.jsp"); - - @Test - void matchesWhenDefaultServletThenTrue() { - MockHttpServletRequest request = new MockHttpServletRequest("GET", "/a/uri.jsp"); - request.setHttpServletMapping(TestMockHttpServletMappings.extension(request, ".jsp")); - assertThat(this.matcher.matches(request)).isTrue(); - } - - @Test - void matchesWhenNotDefaultServletThenFalse() { - MockHttpServletRequest request = new MockHttpServletRequest("GET", "/a/uri.jsp"); - request.setHttpServletMapping(TestMockHttpServletMappings.path(request, "/a")); - request.setServletPath("/a/uri.jsp"); - assertThat(this.matcher.matches(request)).isFalse(); - } - - @Test - void matcherWhenDefaultServletThenTrue() { - MockHttpServletRequest request = new MockHttpServletRequest("GET", "/a/uri.jsp"); - request.setHttpServletMapping(TestMockHttpServletMappings.extension(request, ".jsp")); - request.setServletPath("/a/uri.jsp"); - assertThat(this.matcher.matcher(request).isMatch()).isTrue(); - } - - @Test - void matcherWhenNotDefaultServletThenFalse() { - MockHttpServletRequest request = new MockHttpServletRequest("GET", "/a/uri.jsp"); - request.setHttpServletMapping(TestMockHttpServletMappings.path(request, "/a")); - request.setServletPath("/a/uri.jsp"); - assertThat(this.matcher.matcher(request).isMatch()).isFalse(); - } - -} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurerTests.java index 168564a8ee..7ebd37c74b 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/SessionManagementConfigurerTests.java @@ -556,7 +556,9 @@ public class SessionManagementConfigurerTests { .sessionManagement((sessionManagement) -> sessionManagement .requireExplicitAuthenticationStrategy(false) - .sessionFixation(SessionManagementConfigurer.SessionFixationConfigurer::newSession) + .sessionFixation((sessionFixation) -> + sessionFixation.newSession() + ) ) .httpBasic(withDefaults()); // @formatter:on diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutConfigurerTests.java deleted file mode 100644 index cecc69feee..0000000000 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OidcLogoutConfigurerTests.java +++ /dev/null @@ -1,596 +0,0 @@ -/* - * Copyright 2002-2023 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.annotation.web.configurers.oauth2.client; - -import java.io.IOException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.interfaces.RSAPublicKey; -import java.time.Instant; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import com.gargoylesoftware.htmlunit.util.UrlUtils; -import com.nimbusds.jose.jwk.JWKSet; -import com.nimbusds.jose.jwk.RSAKey; -import com.nimbusds.jose.jwk.source.ImmutableJWKSet; -import com.nimbusds.jose.jwk.source.JWKSource; -import com.nimbusds.jose.proc.SecurityContext; -import com.nimbusds.oauth2.sdk.Scope; -import com.nimbusds.oauth2.sdk.token.BearerAccessToken; -import com.nimbusds.openid.connect.sdk.token.OIDCTokens; -import jakarta.annotation.PreDestroy; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpSession; -import okhttp3.mockwebserver.Dispatcher; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.core.annotation.Order; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.mock.web.MockHttpSession; -import org.springframework.mock.web.MockServletContext; -import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.test.SpringTestContext; -import org.springframework.security.config.test.SpringTestContextExtension; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.oauth2.client.oidc.authentication.logout.LogoutTokenClaimNames; -import org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken; -import org.springframework.security.oauth2.client.oidc.authentication.logout.TestOidcLogoutTokens; -import org.springframework.security.oauth2.client.oidc.session.InMemoryOidcSessionRegistry; -import org.springframework.security.oauth2.client.oidc.session.OidcSessionRegistry; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; -import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; -import org.springframework.security.oauth2.client.registration.TestClientRegistrations; -import org.springframework.security.oauth2.core.oidc.OidcIdToken; -import org.springframework.security.oauth2.core.oidc.TestOidcIdTokens; -import org.springframework.security.oauth2.core.oidc.user.OidcUser; -import org.springframework.security.oauth2.jwt.JwtClaimsSet; -import org.springframework.security.oauth2.jwt.JwtEncoder; -import org.springframework.security.oauth2.jwt.JwtEncoderParameters; -import org.springframework.security.oauth2.jwt.NimbusJwtEncoder; -import org.springframework.security.provisioning.InMemoryUserDetailsManager; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.logout.LogoutHandler; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; - -import static org.hamcrest.Matchers.containsString; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.willThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; -import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -/** - * Tests for {@link OidcLogoutConfigurer} - */ -@ExtendWith(SpringTestContextExtension.class) -public class OidcLogoutConfigurerTests { - - @Autowired - private MockMvc mvc; - - @Autowired(required = false) - private MockWebServer web; - - @Autowired - private ClientRegistration clientRegistration; - - public final SpringTestContext spring = new SpringTestContext(this); - - @Test - void logoutWhenDefaultsThenRemotelyInvalidatesSessions() throws Exception { - this.spring.register(WebServerConfig.class, OidcProviderConfig.class, DefaultConfig.class).autowire(); - String registrationId = this.clientRegistration.getRegistrationId(); - MockHttpSession session = login(); - String logoutToken = this.mvc.perform(get("/token/logout").session(session)) - .andExpect(status().isOk()) - .andReturn() - .getResponse() - .getContentAsString(); - this.mvc - .perform(post(this.web.url("/logout/connect/back-channel/" + registrationId).toString()) - .param("logout_token", logoutToken)) - .andExpect(status().isOk()); - this.mvc.perform(get("/token/logout").session(session)).andExpect(status().isUnauthorized()); - } - - @Test - void logoutWhenInvalidLogoutTokenThenBadRequest() throws Exception { - this.spring.register(WebServerConfig.class, OidcProviderConfig.class, DefaultConfig.class).autowire(); - this.mvc.perform(get("/token/logout")).andExpect(status().isUnauthorized()); - String registrationId = this.clientRegistration.getRegistrationId(); - MvcResult result = this.mvc.perform(get("/oauth2/authorization/" + registrationId)) - .andExpect(status().isFound()) - .andReturn(); - MockHttpSession session = (MockHttpSession) result.getRequest().getSession(); - String redirectUrl = UrlUtils.decode(result.getResponse().getRedirectedUrl()); - String state = this.mvc - .perform(get(redirectUrl) - .with(httpBasic(this.clientRegistration.getClientId(), this.clientRegistration.getClientSecret()))) - .andReturn() - .getResponse() - .getContentAsString(); - result = this.mvc - .perform(get("/login/oauth2/code/" + registrationId).param("code", "code") - .param("state", state) - .session(session)) - .andExpect(status().isFound()) - .andReturn(); - session = (MockHttpSession) result.getRequest().getSession(); - this.mvc - .perform(post(this.web.url("/logout/connect/back-channel/" + registrationId).toString()) - .param("logout_token", "invalid")) - .andExpect(status().isBadRequest()); - this.mvc.perform(get("/token/logout").session(session)).andExpect(status().isOk()); - } - - @Test - void logoutWhenLogoutTokenSpecifiesOneSessionThenRemotelyInvalidatesOnlyThatSession() throws Exception { - this.spring.register(WebServerConfig.class, OidcProviderConfig.class, DefaultConfig.class).autowire(); - String registrationId = this.clientRegistration.getRegistrationId(); - MockHttpSession one = login(); - MockHttpSession two = login(); - MockHttpSession three = login(); - String logoutToken = this.mvc.perform(get("/token/logout").session(one)) - .andExpect(status().isOk()) - .andReturn() - .getResponse() - .getContentAsString(); - this.mvc - .perform(post(this.web.url("/logout/connect/back-channel/" + registrationId).toString()) - .param("logout_token", logoutToken)) - .andExpect(status().isOk()); - this.mvc.perform(get("/token/logout").session(one)).andExpect(status().isUnauthorized()); - this.mvc.perform(get("/token/logout").session(two)).andExpect(status().isOk()); - logoutToken = this.mvc.perform(get("/token/logout/all").session(three)) - .andExpect(status().isOk()) - .andReturn() - .getResponse() - .getContentAsString(); - this.mvc - .perform(post(this.web.url("/logout/connect/back-channel/" + registrationId).toString()) - .param("logout_token", logoutToken)) - .andExpect(status().isOk()); - this.mvc.perform(get("/token/logout").session(two)).andExpect(status().isUnauthorized()); - this.mvc.perform(get("/token/logout").session(three)).andExpect(status().isUnauthorized()); - } - - @Test - void logoutWhenRemoteLogoutFailsThenReportsPartialLogout() throws Exception { - this.spring.register(WebServerConfig.class, OidcProviderConfig.class, WithBrokenLogoutConfig.class).autowire(); - LogoutHandler logoutHandler = this.spring.getContext().getBean(LogoutHandler.class); - willThrow(IllegalStateException.class).given(logoutHandler).logout(any(), any(), any()); - String registrationId = this.clientRegistration.getRegistrationId(); - MockHttpSession one = login(); - String logoutToken = this.mvc.perform(get("/token/logout/all").session(one)) - .andExpect(status().isOk()) - .andReturn() - .getResponse() - .getContentAsString(); - this.mvc - .perform(post(this.web.url("/logout/connect/back-channel/" + registrationId).toString()) - .param("logout_token", logoutToken)) - .andExpect(status().isBadRequest()) - .andExpect(content().string(containsString("partial_logout"))); - this.mvc.perform(get("/token/logout").session(one)).andExpect(status().isOk()); - } - - @Test - void logoutWhenCustomComponentsThenUses() throws Exception { - this.spring.register(WebServerConfig.class, OidcProviderConfig.class, WithCustomComponentsConfig.class) - .autowire(); - String registrationId = this.clientRegistration.getRegistrationId(); - MockHttpSession session = login(); - String logoutToken = this.mvc.perform(get("/token/logout").session(session)) - .andExpect(status().isOk()) - .andReturn() - .getResponse() - .getContentAsString(); - this.mvc - .perform(post(this.web.url("/logout/connect/back-channel/" + registrationId).toString()) - .param("logout_token", logoutToken)) - .andExpect(status().isOk()); - this.mvc.perform(get("/token/logout").session(session)).andExpect(status().isUnauthorized()); - OidcSessionRegistry sessionRegistry = this.spring.getContext().getBean(OidcSessionRegistry.class); - verify(sessionRegistry).saveSessionInformation(any()); - verify(sessionRegistry).removeSessionInformation(any(OidcLogoutToken.class)); - } - - private MockHttpSession login() throws Exception { - MockMvcDispatcher dispatcher = (MockMvcDispatcher) this.web.getDispatcher(); - this.mvc.perform(get("/token/logout")).andExpect(status().isUnauthorized()); - String registrationId = this.clientRegistration.getRegistrationId(); - MvcResult result = this.mvc.perform(get("/oauth2/authorization/" + registrationId)) - .andExpect(status().isFound()) - .andReturn(); - MockHttpSession session = (MockHttpSession) result.getRequest().getSession(); - String redirectUrl = UrlUtils.decode(result.getResponse().getRedirectedUrl()); - String state = this.mvc - .perform(get(redirectUrl) - .with(httpBasic(this.clientRegistration.getClientId(), this.clientRegistration.getClientSecret()))) - .andReturn() - .getResponse() - .getContentAsString(); - result = this.mvc - .perform(get("/login/oauth2/code/" + registrationId).param("code", "code") - .param("state", state) - .session(session)) - .andExpect(status().isFound()) - .andReturn(); - session = (MockHttpSession) result.getRequest().getSession(); - dispatcher.registerSession(session); - return session; - } - - @Configuration - static class RegistrationConfig { - - @Autowired(required = false) - MockWebServer web; - - @Bean - ClientRegistration clientRegistration() { - if (this.web == null) { - return TestClientRegistrations.clientRegistration().build(); - } - String issuer = this.web.url("/").toString(); - return TestClientRegistrations.clientRegistration() - .issuerUri(issuer) - .jwkSetUri(issuer + "jwks") - .tokenUri(issuer + "token") - .userInfoUri(issuer + "user") - .scope("openid") - .build(); - } - - @Bean - ClientRegistrationRepository clientRegistrationRepository(ClientRegistration clientRegistration) { - return new InMemoryClientRegistrationRepository(clientRegistration); - } - - } - - @Configuration - @EnableWebSecurity - @Import(RegistrationConfig.class) - static class DefaultConfig { - - @Bean - @Order(1) - SecurityFilterChain filters(HttpSecurity http) throws Exception { - // @formatter:off - http - .authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()) - .oauth2Login(Customizer.withDefaults()) - .oidcLogout((oidc) -> oidc.backChannel(Customizer.withDefaults())); - // @formatter:on - - return http.build(); - } - - } - - @Configuration - @EnableWebSecurity - @Import(RegistrationConfig.class) - static class WithCustomComponentsConfig { - - OidcSessionRegistry sessionRegistry = spy(new InMemoryOidcSessionRegistry()); - - @Bean - @Order(1) - SecurityFilterChain filters(HttpSecurity http) throws Exception { - // @formatter:off - http - .authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()) - .oauth2Login((oauth2) -> oauth2.oidcSessionRegistry(this.sessionRegistry)) - .oidcLogout((oidc) -> oidc.backChannel(Customizer.withDefaults())); - // @formatter:on - - return http.build(); - } - - @Bean - OidcSessionRegistry sessionRegistry() { - return this.sessionRegistry; - } - - } - - @Configuration - @EnableWebSecurity - @Import(RegistrationConfig.class) - static class WithBrokenLogoutConfig { - - private final LogoutHandler logoutHandler = mock(LogoutHandler.class); - - @Bean - @Order(1) - SecurityFilterChain filters(HttpSecurity http) throws Exception { - // @formatter:off - http - .authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated()) - .logout((logout) -> logout.addLogoutHandler(this.logoutHandler)) - .oauth2Login(Customizer.withDefaults()) - .oidcLogout((oidc) -> oidc.backChannel(Customizer.withDefaults())); - // @formatter:on - - return http.build(); - } - - @Bean - LogoutHandler logoutHandler() { - return this.logoutHandler; - } - - } - - @Configuration - @EnableWebSecurity - @EnableWebMvc - @RestController - static class OidcProviderConfig { - - private static final RSAKey key = key(); - - private static final JWKSource jwks = jwks(key); - - private static RSAKey key() { - try { - KeyPair pair = KeyPairGenerator.getInstance("RSA").generateKeyPair(); - return new RSAKey.Builder((RSAPublicKey) pair.getPublic()).privateKey(pair.getPrivate()).build(); - } - catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - private static JWKSource jwks(RSAKey key) { - try { - return new ImmutableJWKSet<>(new JWKSet(key)); - } - catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - private final String username = "user"; - - private final JwtEncoder encoder = new NimbusJwtEncoder(jwks); - - private String nonce; - - @Autowired - ClientRegistration registration; - - @Bean - @Order(0) - SecurityFilterChain authorizationServer(HttpSecurity http, ClientRegistration registration) throws Exception { - // @formatter:off - http - .securityMatcher("/jwks", "/login/oauth/authorize", "/nonce", "/token", "/token/logout", "/user") - .authorizeHttpRequests((authorize) -> authorize - .requestMatchers("/jwks").permitAll() - .anyRequest().authenticated() - ) - .httpBasic(Customizer.withDefaults()) - .oauth2ResourceServer((oauth2) -> oauth2 - .jwt((jwt) -> jwt.jwkSetUri(registration.getProviderDetails().getJwkSetUri())) - ); - // @formatter:off - - return http.build(); - } - - @Bean - UserDetailsService users(ClientRegistration registration) { - return new InMemoryUserDetailsManager(User.withUsername(registration.getClientId()) - .password("{noop}" + registration.getClientSecret()).authorities("APP").build()); - } - - @GetMapping("/login/oauth/authorize") - String nonce(@RequestParam("nonce") String nonce, @RequestParam("state") String state) { - this.nonce = nonce; - return state; - } - - @PostMapping("/token") - Map accessToken(HttpServletRequest request) { - HttpSession session = request.getSession(); - JwtEncoderParameters parameters = JwtEncoderParameters - .from(JwtClaimsSet.builder().id("id").subject(this.username) - .issuer(this.registration.getProviderDetails().getIssuerUri()).issuedAt(Instant.now()) - .expiresAt(Instant.now().plusSeconds(86400)).claim("scope", "openid").build()); - String token = this.encoder.encode(parameters).getTokenValue(); - return new OIDCTokens(idToken(session.getId()), new BearerAccessToken(token, 86400, new Scope("openid")), null) - .toJSONObject(); - } - - String idToken(String sessionId) { - OidcIdToken token = TestOidcIdTokens.idToken().issuer(this.registration.getProviderDetails().getIssuerUri()) - .subject(this.username).expiresAt(Instant.now().plusSeconds(86400)) - .audience(List.of(this.registration.getClientId())).nonce(this.nonce) - .claim(LogoutTokenClaimNames.SID, sessionId).build(); - JwtEncoderParameters parameters = JwtEncoderParameters - .from(JwtClaimsSet.builder().claims((claims) -> claims.putAll(token.getClaims())).build()); - return this.encoder.encode(parameters).getTokenValue(); - } - - @GetMapping("/user") - Map userinfo() { - return Map.of("sub", this.username, "id", this.username); - } - - @GetMapping("/jwks") - String jwks() { - return new JWKSet(key).toString(); - } - - @GetMapping("/token/logout") - String logoutToken(@AuthenticationPrincipal OidcUser user) { - OidcLogoutToken token = TestOidcLogoutTokens.withUser(user) - .audience(List.of(this.registration.getClientId())).build(); - JwtEncoderParameters parameters = JwtEncoderParameters - .from(JwtClaimsSet.builder().claims((claims) -> claims.putAll(token.getClaims())).build()); - return this.encoder.encode(parameters).getTokenValue(); - } - - @GetMapping("/token/logout/all") - String logoutTokenAll(@AuthenticationPrincipal OidcUser user) { - OidcLogoutToken token = TestOidcLogoutTokens.withUser(user) - .audience(List.of(this.registration.getClientId())) - .claims((claims) -> claims.remove(LogoutTokenClaimNames.SID)).build(); - JwtEncoderParameters parameters = JwtEncoderParameters - .from(JwtClaimsSet.builder().claims((claims) -> claims.putAll(token.getClaims())).build()); - return this.encoder.encode(parameters).getTokenValue(); - } - } - - @Configuration - static class WebServerConfig { - - private final MockWebServer server = new MockWebServer(); - - @Bean - MockWebServer web(ObjectProvider mvc) { - this.server.setDispatcher(new MockMvcDispatcher(mvc)); - return this.server; - } - - @PreDestroy - void shutdown() throws IOException { - this.server.shutdown(); - } - - } - - private static class MockMvcDispatcher extends Dispatcher { - - private final Map session = new ConcurrentHashMap<>(); - - private final ObjectProvider mvcProvider; - - private MockMvc mvc; - - MockMvcDispatcher(ObjectProvider mvc) { - this.mvcProvider = mvc; - } - - @Override - public MockResponse dispatch(RecordedRequest request) throws InterruptedException { - this.mvc = this.mvcProvider.getObject(); - String method = request.getMethod(); - String path = request.getPath(); - String csrf = request.getHeader("X-CSRF-TOKEN"); - MockHttpSession session = session(request); - MockHttpServletRequestBuilder builder; - if ("GET".equals(method)) { - builder = get(path); - } - else { - builder = post(path).content(request.getBody().readUtf8()); - if (csrf != null) { - builder.header("X-CSRF-TOKEN", csrf); - } - else { - builder.with(csrf()); - } - } - for (Map.Entry> header : request.getHeaders().toMultimap().entrySet()) { - builder.header(header.getKey(), header.getValue().iterator().next()); - } - try { - MockHttpServletResponse mvcResponse = this.mvc.perform(builder.session(session)).andReturn().getResponse(); - return toMockResponse(mvcResponse); - } - catch (Exception ex) { - MockResponse response = new MockResponse(); - response.setResponseCode(500); - return response; - } - } - - void registerSession(MockHttpSession session) { - this.session.put(session.getId(), session); - } - - private MockHttpSession session(RecordedRequest request) { - String cookieHeaderValue = request.getHeader("Cookie"); - if (cookieHeaderValue == null) { - return new MockHttpSession(); - } - String[] cookies = cookieHeaderValue.split(";"); - for (String cookie : cookies) { - String[] parts = cookie.split("="); - if ("JSESSIONID".equals(parts[0])) { - return this.session.computeIfAbsent(parts[1], - (k) -> new MockHttpSession(new MockServletContext(), parts[1])); - } - } - return new MockHttpSession(); - } - - private MockResponse toMockResponse(MockHttpServletResponse mvcResponse) { - MockResponse response = new MockResponse(); - response.setResponseCode(mvcResponse.getStatus()); - for (String name : mvcResponse.getHeaderNames()) { - response.addHeader(name, mvcResponse.getHeaderValue(name)); - } - response.setBody(getContentAsString(mvcResponse)); - return response; - } - - private String getContentAsString(MockHttpServletResponse response) { - try { - return response.getContentAsString(); - } - catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - } - -} diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java index cb2ba0e137..ca418a865c 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/resource/OAuth2ResourceServerConfigurerTests.java @@ -873,7 +873,8 @@ public class OAuth2ResourceServerConfigurerTests { context.registerBean("decoderTwo", JwtDecoder.class, () -> decoder); this.spring.context(context).autowire(); OAuth2ResourceServerConfigurer.JwtConfigurer jwtConfigurer = new OAuth2ResourceServerConfigurer(context).jwt(); - assertThatExceptionOfType(NoUniqueBeanDefinitionException.class).isThrownBy(jwtConfigurer::getJwtDecoder); + assertThatExceptionOfType(NoUniqueBeanDefinitionException.class) + .isThrownBy(() -> jwtConfigurer.getJwtDecoder()); } @Test @@ -1896,7 +1897,9 @@ public class OAuth2ResourceServerConfigurerTests { .anyRequest().authenticated() ) .oauth2Login(withDefaults()) - .oauth2ResourceServer((oauth2) -> oauth2.jwt(withDefaults())); + .oauth2ResourceServer((oauth2) -> oauth2 + .jwt() + ); return http.build(); // @formatter:on } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java index 461f030e9f..fd5557242f 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java @@ -18,25 +18,18 @@ package org.springframework.security.config.annotation.web.configurers.saml2; import java.io.IOException; import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.Collections; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import net.shibboleth.utilities.java.support.xml.SerializeSupport; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; -import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; -import org.opensaml.core.xml.io.Marshaller; -import org.opensaml.saml.saml2.core.Assertion; -import org.opensaml.saml.saml2.core.Response; -import org.w3c.dom.Element; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; @@ -63,7 +56,6 @@ import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextChangedListener; import org.springframework.security.core.context.SecurityContextHolderStrategy; -import org.springframework.security.saml2.core.OpenSamlInitializationService; import org.springframework.security.saml2.core.Saml2ErrorCodes; import org.springframework.security.saml2.core.Saml2Utils; import org.springframework.security.saml2.core.TestSaml2X509Credentials; @@ -73,7 +65,6 @@ import org.springframework.security.saml2.provider.service.authentication.Saml2A import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken; -import org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects; import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; @@ -122,17 +113,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @ExtendWith(SpringTestContextExtension.class) public class Saml2LoginConfigurerTests { - static { - OpenSamlInitializationService.initialize(); - } - - private static final RelyingPartyRegistration registration = TestRelyingPartyRegistrations.noCredentials() - .signingX509Credentials((c) -> c.add(TestSaml2X509Credentials.assertingPartySigningCredential())) - .assertingPartyDetails((party) -> party - .verificationX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential()))) - .build(); - - private static String SIGNED_RESPONSE; + 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); @@ -159,23 +140,6 @@ public class Saml2LoginConfigurerTests { private MockFilterChain filterChain; - @BeforeAll - static void createResponse() throws Exception { - String destination = registration.getAssertionConsumerServiceLocation(); - String assertingPartyEntityId = registration.getAssertingPartyDetails().getEntityId(); - String relyingPartyEntityId = registration.getEntityId(); - Response response = TestOpenSamlObjects.response(destination, assertingPartyEntityId); - Assertion assertion = TestOpenSamlObjects.assertion("test@saml.user", assertingPartyEntityId, - relyingPartyEntityId, destination); - response.getAssertions().add(assertion); - Response signed = TestOpenSamlObjects.signed(response, - registration.getSigningX509Credentials().iterator().next(), relyingPartyEntityId); - Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(signed); - Element element = marshaller.marshall(signed); - String serialized = SerializeSupport.nodeToString(element); - SIGNED_RESPONSE = Saml2Utils.samlEncode(serialized.getBytes(StandardCharsets.UTF_8)); - } - @BeforeEach public void setup() { this.request = new MockHttpServletRequest("POST", ""); @@ -414,7 +378,7 @@ public class Saml2LoginConfigurerTests { Authentication authentication = this.securityContextRepository .loadContext(new HttpRequestResponseHolder(this.request, this.response)) .getAuthentication(); - assertThat(authentication).as("Expected a valid authentication object.").isNotNull(); + Assertions.assertNotNull(authentication, "Expected a valid authentication object."); assertThat(authentication.getAuthorities()).hasSize(1); assertThat(authentication.getAuthorities()).first() .isInstanceOf(SimpleGrantedAuthority.class) @@ -746,6 +710,11 @@ public class Saml2LoginConfigurerTests { @Bean RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() { + RelyingPartyRegistration registration = TestRelyingPartyRegistrations.noCredentials() + .signingX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartySigningCredential())) + .assertingPartyDetails((party) -> party.verificationX509Credentials( + (c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential()))) + .build(); return spy(new InMemoryRelyingPartyRegistrationRepository(registration)); } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/socket/AbstractSecurityWebSocketMessageBrokerConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/socket/AbstractSecurityWebSocketMessageBrokerConfigurerTests.java index 89af68e9da..558362bcdd 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/socket/AbstractSecurityWebSocketMessageBrokerConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/socket/AbstractSecurityWebSocketMessageBrokerConfigurerTests.java @@ -286,8 +286,8 @@ public class AbstractSecurityWebSocketMessageBrokerConfigurerTests { private void assertHandshake(HttpServletRequest request) { TestHandshakeHandler handshakeHandler = this.context.getBean(TestHandshakeHandler.class); assertThatCsrfToken(handshakeHandler.attributes.get(CsrfToken.class.getName())).isEqualTo(this.token); - assertThat(handshakeHandler.attributes).containsEntry(this.sessionAttr, - request.getSession().getAttribute(this.sessionAttr)); + assertThat(handshakeHandler.attributes.get(this.sessionAttr)) + .isEqualTo(request.getSession().getAttribute(this.sessionAttr)); } private HttpRequestHandler handler(HttpServletRequest request) throws Exception { diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/socket/SyncExecutorSubscribableChannelPostProcessor.java b/config/src/test/java/org/springframework/security/config/annotation/web/socket/SyncExecutorSubscribableChannelPostProcessor.java index 36790510b9..fa1bf08d26 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/socket/SyncExecutorSubscribableChannelPostProcessor.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/socket/SyncExecutorSubscribableChannelPostProcessor.java @@ -27,7 +27,8 @@ public class SyncExecutorSubscribableChannelPostProcessor implements BeanPostPro @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - if (bean instanceof ExecutorSubscribableChannel original) { + if (bean instanceof ExecutorSubscribableChannel) { + ExecutorSubscribableChannel original = (ExecutorSubscribableChannel) bean; ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel(); channel.setInterceptors(original.getInterceptors()); return channel; diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/socket/WebSocketMessageBrokerSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/socket/WebSocketMessageBrokerSecurityConfigurationTests.java index a14986b1ee..de58f72801 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/socket/WebSocketMessageBrokerSecurityConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/socket/WebSocketMessageBrokerSecurityConfigurationTests.java @@ -377,8 +377,8 @@ public class WebSocketMessageBrokerSecurityConfigurationTests { private void assertHandshake(HttpServletRequest request) { TestHandshakeHandler handshakeHandler = this.context.getBean(TestHandshakeHandler.class); assertThatCsrfToken(handshakeHandler.attributes.get(CsrfToken.class.getName())).isEqualTo(this.token); - assertThat(handshakeHandler.attributes).containsEntry(this.sessionAttr, - request.getSession().getAttribute(this.sessionAttr)); + assertThat(handshakeHandler.attributes.get(this.sessionAttr)) + .isEqualTo(request.getSession().getAttribute(this.sessionAttr)); } private HttpRequestHandler handler(HttpServletRequest request) throws Exception { @@ -633,8 +633,9 @@ public class WebSocketMessageBrokerSecurityConfigurationTests { public boolean doHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws HandshakeFailureException { this.attributes = attributes; - if (wsHandler instanceof SockJsWebSocketHandler sockJs) { + if (wsHandler instanceof SockJsWebSocketHandler) { // work around SPR-12716 + SockJsWebSocketHandler sockJs = (SockJsWebSocketHandler) wsHandler; WebSocketServerSockJsSession session = (WebSocketServerSockJsSession) ReflectionTestUtils .getField(sockJs, "sockJsSession"); this.attributes = session.getAttributes(); diff --git a/config/src/test/java/org/springframework/security/config/doc/SpringSecurityXsdParser.java b/config/src/test/java/org/springframework/security/config/doc/SpringSecurityXsdParser.java index 333b617c67..4cd696f468 100644 --- a/config/src/test/java/org/springframework/security/config/doc/SpringSecurityXsdParser.java +++ b/config/src/test/java/org/springframework/security/config/doc/SpringSecurityXsdParser.java @@ -181,7 +181,7 @@ public class SpringSecurityXsdParser { */ private Element elmt(XmlNode n) { String name = n.attribute("ref"); - if (!StringUtils.hasLength(name)) { + if (StringUtils.isEmpty(name)) { name = n.attribute("name"); } else { @@ -201,7 +201,7 @@ public class SpringSecurityXsdParser { e.getAttrs().forEach((attr) -> attr.setElmt(e)); e.getChildElmts().values().forEach((element) -> element.getParentElmts().put(e.getName(), e)); String subGrpName = n.attribute("substitutionGroup"); - if (StringUtils.hasLength(subGrpName)) { + if (!StringUtils.isEmpty(subGrpName)) { Element subGrp = elmt(findNode(n, subGrpName.split(":")[1])); subGrp.getSubGrps().add(e); } diff --git a/config/src/test/java/org/springframework/security/config/doc/XmlNode.java b/config/src/test/java/org/springframework/security/config/doc/XmlNode.java index 02e9096083..5ed6c38c0e 100644 --- a/config/src/test/java/org/springframework/security/config/doc/XmlNode.java +++ b/config/src/test/java/org/springframework/security/config/doc/XmlNode.java @@ -58,7 +58,8 @@ public class XmlNode { public Optional parent() { // @formatter:off - return Optional.ofNullable(this.node.getParentNode()).map(XmlNode::new); + return Optional.ofNullable(this.node.getParentNode()) + .map((parent) -> new XmlNode(parent)); // @formatter:on } @@ -66,7 +67,7 @@ public class XmlNode { // @formatter:off return Optional.ofNullable(this.node.getAttributes()) .map((attrs) -> attrs.getNamedItem(name)) - .map(Node::getTextContent) + .map((attr) -> attr.getTextContent()) .orElse(null); // @formatter:on } diff --git a/config/src/test/java/org/springframework/security/config/doc/XsdDocumentedTests.java b/config/src/test/java/org/springframework/security/config/doc/XsdDocumentedTests.java index 935c4e0cad..e575f16e38 100644 --- a/config/src/test/java/org/springframework/security/config/doc/XsdDocumentedTests.java +++ b/config/src/test/java/org/springframework/security/config/doc/XsdDocumentedTests.java @@ -65,7 +65,7 @@ public class XsdDocumentedTests { String schema31xDocumentLocation = "org/springframework/security/config/spring-security-3.1.xsd"; - String schemaDocumentLocation = "org/springframework/security/config/spring-security-6.2.xsd"; + String schemaDocumentLocation = "org/springframework/security/config/spring-security-6.1.xsd"; XmlSupport xml = new XmlSupport(); @@ -151,8 +151,8 @@ public class XsdDocumentedTests { .list((dir, name) -> name.endsWith(".xsd")); // @formatter:on assertThat(schemas.length) - .withFailMessage("the count is equal to 24, if not then schemaDocument needs updating") - .isEqualTo(24); + .withFailMessage("the count is equal to 23, if not then schemaDocument needs updating") + .isEqualTo(23); } /** diff --git a/config/src/test/java/org/springframework/security/config/http/FormLoginBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/http/FormLoginBeanDefinitionParserTests.java index 86a1c351ac..d501ee82bd 100644 --- a/config/src/test/java/org/springframework/security/config/http/FormLoginBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/http/FormLoginBeanDefinitionParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2018 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. @@ -63,7 +63,7 @@ public class FormLoginBeanDefinitionParserTests { + " \n" + " Please sign in\n" + " \n" - + " \n" + + " \n" + " \n" + " \n" + "
    \n" @@ -104,7 +104,7 @@ public class FormLoginBeanDefinitionParserTests { + " \n" + " Please sign in\n" + " \n" - + " \n" + + " \n" + " \n" + " \n" + "
    \n" diff --git a/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java b/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java index 57741cea04..7a87a31037 100644 --- a/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/MiscHttpConfigTests.java @@ -417,7 +417,7 @@ public class MiscHttpConfigTests { this.spring.configLocations(xml("DeleteCookies")).autowire(); MvcResult result = this.mvc.perform(post("/logout").with(csrf())).andReturn(); List values = result.getResponse().getHeaders("Set-Cookie"); - assertThat(values).hasSize(2); + assertThat(values.size()).isEqualTo(2); assertThat(values).extracting((value) -> value.split("=")[0]).contains("JSESSIONID", "mycookie"); } diff --git a/config/src/test/java/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests.java b/config/src/test/java/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests.java deleted file mode 100644 index 09c1c2dacd..0000000000 --- a/config/src/test/java/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests.java +++ /dev/null @@ -1,475 +0,0 @@ -/* - * Copyright 2002-2023 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.http; - -import java.time.Duration; -import java.time.Instant; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.Consumer; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.config.oauth2.client.CommonOAuth2Provider; -import org.springframework.security.config.test.SpringTestContext; -import org.springframework.security.oauth2.client.AuthorizationCodeOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.ClientAuthorizationRequiredException; -import org.springframework.security.oauth2.client.ClientCredentialsOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.JwtBearerOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.OAuth2AuthorizationContext; -import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; -import org.springframework.security.oauth2.client.PasswordOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider; -import org.springframework.security.oauth2.client.endpoint.AbstractOAuth2AuthorizationGrantRequest; -import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest; -import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient; -import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest; -import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest; -import org.springframework.security.oauth2.client.endpoint.OAuth2PasswordGrantRequest; -import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; -import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager; -import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository; -import org.springframework.security.oauth2.core.AuthorizationGrantType; -import org.springframework.security.oauth2.core.OAuth2AccessToken; -import org.springframework.security.oauth2.core.OAuth2AuthorizationException; -import org.springframework.security.oauth2.core.OAuth2Error; -import org.springframework.security.oauth2.core.TestOAuth2RefreshTokens; -import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse; -import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; -import org.springframework.security.oauth2.core.endpoint.TestOAuth2AccessTokenResponses; -import org.springframework.security.oauth2.jwt.JoseHeaderNames; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.security.oauth2.jwt.JwtClaimNames; -import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; -import org.springframework.util.StringUtils; - -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.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link OAuth2AuthorizedClientManagerRegistrar}. - * - * @author Steve Riesenberg - */ -public class OAuth2AuthorizedClientManagerRegistrarTests { - - private static final String CONFIG_LOCATION_PREFIX = "classpath:org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests"; - - private static OAuth2AccessTokenResponseClient MOCK_RESPONSE_CLIENT; - - public final SpringTestContext spring = new SpringTestContext(this); - - @Autowired - private OAuth2AuthorizedClientManager authorizedClientManager; - - @Autowired - private ClientRegistrationRepository clientRegistrationRepository; - - @Autowired - private OAuth2AuthorizedClientRepository authorizedClientRepository; - - @Autowired(required = false) - private AuthorizationCodeOAuth2AuthorizedClientProvider authorizationCodeAuthorizedClientProvider; - - private MockHttpServletRequest request; - - private MockHttpServletResponse response; - - @BeforeEach - @SuppressWarnings("unchecked") - public void setUp() { - MOCK_RESPONSE_CLIENT = mock(OAuth2AccessTokenResponseClient.class); - this.request = new MockHttpServletRequest(); - this.response = new MockHttpServletResponse(); - } - - @Test - public void loadContextWhenOAuth2ClientEnabledThenConfigured() { - this.spring.configLocations(xml("minimal")).autowire(); - assertThat(this.authorizedClientManager).isNotNull(); - } - - @Test - public void authorizeWhenAuthorizationCodeAuthorizedClientProviderBeanThenUsed() { - this.spring.configLocations(xml("providers")).autowire(); - - TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", null); - // @formatter:off - OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest - .withClientRegistrationId("google") - .principal(authentication) - .attribute(HttpServletRequest.class.getName(), this.request) - .attribute(HttpServletResponse.class.getName(), this.response) - .build(); - assertThatExceptionOfType(ClientAuthorizationRequiredException.class) - .isThrownBy(() -> this.authorizedClientManager.authorize(authorizeRequest)) - .extracting(OAuth2AuthorizationException::getError) - .extracting(OAuth2Error::getErrorCode) - .isEqualTo("client_authorization_required"); - // @formatter:on - - verify(this.authorizationCodeAuthorizedClientProvider).authorize(any(OAuth2AuthorizationContext.class)); - } - - @Test - public void authorizeWhenRefreshTokenAccessTokenResponseClientBeanThenUsed() { - this.spring.configLocations(xml("clients")).autowire(); - testRefreshTokenGrant(); - } - - @Test - public void authorizeWhenRefreshTokenAuthorizedClientProviderBeanThenUsed() { - this.spring.configLocations(xml("providers")).autowire(); - testRefreshTokenGrant(); - } - - private void testRefreshTokenGrant() { - OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build(); - given(MOCK_RESPONSE_CLIENT.getTokenResponse(any(OAuth2RefreshTokenGrantRequest.class))) - .willReturn(accessTokenResponse); - - TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", null); - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("google"); - OAuth2AuthorizedClient existingAuthorizedClient = new OAuth2AuthorizedClient(clientRegistration, - authentication.getName(), getExpiredAccessToken(), TestOAuth2RefreshTokens.refreshToken()); - this.authorizedClientRepository.saveAuthorizedClient(existingAuthorizedClient, authentication, this.request, - this.response); - // @formatter:off - OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest - .withAuthorizedClient(existingAuthorizedClient) - .principal(authentication) - .attribute(HttpServletRequest.class.getName(), this.request) - .attribute(HttpServletResponse.class.getName(), this.response) - .build(); - // @formatter:on - OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest); - assertThat(authorizedClient).isNotNull(); - - ArgumentCaptor grantRequestCaptor = ArgumentCaptor - .forClass(OAuth2RefreshTokenGrantRequest.class); - verify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture()); - - OAuth2RefreshTokenGrantRequest grantRequest = grantRequestCaptor.getValue(); - assertThat(grantRequest.getClientRegistration().getRegistrationId()) - .isEqualTo(clientRegistration.getRegistrationId()); - assertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.REFRESH_TOKEN); - assertThat(grantRequest.getAccessToken()).isEqualTo(existingAuthorizedClient.getAccessToken()); - assertThat(grantRequest.getRefreshToken()).isEqualTo(existingAuthorizedClient.getRefreshToken()); - } - - @Test - public void authorizeWhenClientCredentialsAccessTokenResponseClientBeanThenUsed() { - this.spring.configLocations(xml("clients")).autowire(); - testClientCredentialsGrant(); - } - - @Test - public void authorizeWhenClientCredentialsAuthorizedClientProviderBeanThenUsed() { - this.spring.configLocations(xml("providers")).autowire(); - testClientCredentialsGrant(); - } - - private void testClientCredentialsGrant() { - OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build(); - given(MOCK_RESPONSE_CLIENT.getTokenResponse(any(OAuth2ClientCredentialsGrantRequest.class))) - .willReturn(accessTokenResponse); - - TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", null); - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("github"); - // @formatter:off - OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest - .withClientRegistrationId(clientRegistration.getRegistrationId()) - .principal(authentication) - .attribute(HttpServletRequest.class.getName(), this.request) - .attribute(HttpServletResponse.class.getName(), this.response) - .build(); - // @formatter:on - OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest); - assertThat(authorizedClient).isNotNull(); - - ArgumentCaptor grantRequestCaptor = ArgumentCaptor - .forClass(OAuth2ClientCredentialsGrantRequest.class); - verify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture()); - - OAuth2ClientCredentialsGrantRequest grantRequest = grantRequestCaptor.getValue(); - assertThat(grantRequest.getClientRegistration().getRegistrationId()) - .isEqualTo(clientRegistration.getRegistrationId()); - assertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS); - } - - @Test - public void authorizeWhenPasswordAccessTokenResponseClientBeanThenUsed() { - this.spring.configLocations(xml("clients")).autowire(); - testPasswordGrant(); - } - - @Test - public void authorizeWhenPasswordAuthorizedClientProviderBeanThenUsed() { - this.spring.configLocations(xml("providers")).autowire(); - testPasswordGrant(); - } - - private void testPasswordGrant() { - OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build(); - given(MOCK_RESPONSE_CLIENT.getTokenResponse(any(OAuth2PasswordGrantRequest.class))) - .willReturn(accessTokenResponse); - - TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", "password"); - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("facebook"); - // @formatter:off - OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest - .withClientRegistrationId(clientRegistration.getRegistrationId()) - .principal(authentication) - .attribute(HttpServletRequest.class.getName(), this.request) - .attribute(HttpServletResponse.class.getName(), this.response) - .build(); - // @formatter:on - this.request.setParameter(OAuth2ParameterNames.USERNAME, "user"); - this.request.setParameter(OAuth2ParameterNames.PASSWORD, "password"); - OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest); - assertThat(authorizedClient).isNotNull(); - - ArgumentCaptor grantRequestCaptor = ArgumentCaptor - .forClass(OAuth2PasswordGrantRequest.class); - verify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture()); - - OAuth2PasswordGrantRequest grantRequest = grantRequestCaptor.getValue(); - assertThat(grantRequest.getClientRegistration().getRegistrationId()) - .isEqualTo(clientRegistration.getRegistrationId()); - assertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.PASSWORD); - assertThat(grantRequest.getUsername()).isEqualTo("user"); - assertThat(grantRequest.getPassword()).isEqualTo("password"); - } - - @Test - public void authorizeWhenJwtBearerAccessTokenResponseClientBeanThenUsed() { - this.spring.configLocations(xml("clients")).autowire(); - testJwtBearerGrant(); - } - - @Test - public void authorizeWhenJwtBearerAuthorizedClientProviderBeanThenUsed() { - this.spring.configLocations(xml("providers")).autowire(); - testJwtBearerGrant(); - } - - private void testJwtBearerGrant() { - OAuth2AccessTokenResponse accessTokenResponse = TestOAuth2AccessTokenResponses.accessTokenResponse().build(); - given(MOCK_RESPONSE_CLIENT.getTokenResponse(any(JwtBearerGrantRequest.class))).willReturn(accessTokenResponse); - - JwtAuthenticationToken authentication = new JwtAuthenticationToken(getJwt()); - ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("okta"); - // @formatter:off - OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest - .withClientRegistrationId(clientRegistration.getRegistrationId()) - .principal(authentication) - .attribute(HttpServletRequest.class.getName(), this.request) - .attribute(HttpServletResponse.class.getName(), this.response) - .build(); - // @formatter:on - OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest); - assertThat(authorizedClient).isNotNull(); - - ArgumentCaptor grantRequestCaptor = ArgumentCaptor.forClass(JwtBearerGrantRequest.class); - verify(MOCK_RESPONSE_CLIENT).getTokenResponse(grantRequestCaptor.capture()); - - JwtBearerGrantRequest grantRequest = grantRequestCaptor.getValue(); - assertThat(grantRequest.getClientRegistration().getRegistrationId()) - .isEqualTo(clientRegistration.getRegistrationId()); - assertThat(grantRequest.getGrantType()).isEqualTo(AuthorizationGrantType.JWT_BEARER); - assertThat(grantRequest.getJwt().getSubject()).isEqualTo("user"); - } - - private static OAuth2AccessToken getExpiredAccessToken() { - Instant expiresAt = Instant.now().minusSeconds(60); - Instant issuedAt = expiresAt.minus(Duration.ofDays(1)); - return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "scopes", issuedAt, expiresAt, - new HashSet<>(Arrays.asList("read", "write"))); - } - - private static Jwt getJwt() { - Instant issuedAt = Instant.now(); - return new Jwt("token", issuedAt, issuedAt.plusSeconds(300), - Collections.singletonMap(JoseHeaderNames.ALG, "RS256"), - Collections.singletonMap(JwtClaimNames.SUB, "user")); - } - - private static String xml(String configName) { - return CONFIG_LOCATION_PREFIX + "-" + configName + ".xml"; - } - - public static List getClientRegistrations() { - // @formatter:off - return Arrays.asList( - CommonOAuth2Provider.GOOGLE.getBuilder("google") - .clientId("google-client-id") - .clientSecret("google-client-secret") - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .build(), - CommonOAuth2Provider.GITHUB.getBuilder("github") - .clientId("github-client-id") - .clientSecret("github-client-secret") - .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) - .build(), - CommonOAuth2Provider.FACEBOOK.getBuilder("facebook") - .clientId("facebook-client-id") - .clientSecret("facebook-client-secret") - .authorizationGrantType(AuthorizationGrantType.PASSWORD) - .build(), - CommonOAuth2Provider.OKTA.getBuilder("okta") - .clientId("okta-client-id") - .clientSecret("okta-client-secret") - .authorizationGrantType(AuthorizationGrantType.JWT_BEARER) - .build()); - // @formatter:on - } - - public static Consumer authorizedClientManagerConsumer() { - return (authorizedClientManager) -> authorizedClientManager.setContextAttributesMapper((authorizeRequest) -> { - HttpServletRequest request = Objects - .requireNonNull(authorizeRequest.getAttribute(HttpServletRequest.class.getName())); - String username = request.getParameter(OAuth2ParameterNames.USERNAME); - String password = request.getParameter(OAuth2ParameterNames.PASSWORD); - - Map attributes = Collections.emptyMap(); - if (StringUtils.hasText(username) && StringUtils.hasText(password)) { - attributes = new HashMap<>(); - attributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username); - attributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password); - } - - return attributes; - }); - } - - public static AuthorizationCodeOAuth2AuthorizedClientProvider authorizationCodeAuthorizedClientProvider() { - return spy(new AuthorizationCodeOAuth2AuthorizedClientProvider()); - } - - public static RefreshTokenOAuth2AuthorizedClientProvider refreshTokenAuthorizedClientProvider() { - RefreshTokenOAuth2AuthorizedClientProvider authorizedClientProvider = new RefreshTokenOAuth2AuthorizedClientProvider(); - authorizedClientProvider.setAccessTokenResponseClient(refreshTokenAccessTokenResponseClient()); - return authorizedClientProvider; - } - - public static MockRefreshTokenClient refreshTokenAccessTokenResponseClient() { - return new MockRefreshTokenClient(); - } - - public static ClientCredentialsOAuth2AuthorizedClientProvider clientCredentialsAuthorizedClientProvider() { - ClientCredentialsOAuth2AuthorizedClientProvider authorizedClientProvider = new ClientCredentialsOAuth2AuthorizedClientProvider(); - authorizedClientProvider.setAccessTokenResponseClient(clientCredentialsAccessTokenResponseClient()); - return authorizedClientProvider; - } - - public static OAuth2AccessTokenResponseClient clientCredentialsAccessTokenResponseClient() { - return new MockClientCredentialsClient(); - } - - public static PasswordOAuth2AuthorizedClientProvider passwordAuthorizedClientProvider() { - PasswordOAuth2AuthorizedClientProvider authorizedClientProvider = new PasswordOAuth2AuthorizedClientProvider(); - authorizedClientProvider.setAccessTokenResponseClient(passwordAccessTokenResponseClient()); - return authorizedClientProvider; - } - - public static OAuth2AccessTokenResponseClient passwordAccessTokenResponseClient() { - return new MockPasswordClient(); - } - - public static JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider() { - JwtBearerOAuth2AuthorizedClientProvider authorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider(); - authorizedClientProvider.setAccessTokenResponseClient(jwtBearerAccessTokenResponseClient()); - return authorizedClientProvider; - } - - public static OAuth2AccessTokenResponseClient jwtBearerAccessTokenResponseClient() { - return new MockJwtBearerClient(); - } - - private static class MockAuthorizationCodeClient - implements OAuth2AccessTokenResponseClient { - - @Override - public OAuth2AccessTokenResponse getTokenResponse( - OAuth2AuthorizationCodeGrantRequest authorizationGrantRequest) { - return MOCK_RESPONSE_CLIENT.getTokenResponse(authorizationGrantRequest); - } - - } - - private static class MockRefreshTokenClient - implements OAuth2AccessTokenResponseClient { - - @Override - public OAuth2AccessTokenResponse getTokenResponse(OAuth2RefreshTokenGrantRequest authorizationGrantRequest) { - return MOCK_RESPONSE_CLIENT.getTokenResponse(authorizationGrantRequest); - } - - } - - private static class MockClientCredentialsClient - implements OAuth2AccessTokenResponseClient { - - @Override - public OAuth2AccessTokenResponse getTokenResponse( - OAuth2ClientCredentialsGrantRequest authorizationGrantRequest) { - return MOCK_RESPONSE_CLIENT.getTokenResponse(authorizationGrantRequest); - } - - } - - private static class MockPasswordClient implements OAuth2AccessTokenResponseClient { - - @Override - public OAuth2AccessTokenResponse getTokenResponse(OAuth2PasswordGrantRequest authorizationGrantRequest) { - return MOCK_RESPONSE_CLIENT.getTokenResponse(authorizationGrantRequest); - } - - } - - private static class MockJwtBearerClient implements OAuth2AccessTokenResponseClient { - - @Override - public OAuth2AccessTokenResponse getTokenResponse(JwtBearerGrantRequest authorizationGrantRequest) { - return MOCK_RESPONSE_CLIENT.getTokenResponse(authorizationGrantRequest); - } - - } - -} diff --git a/config/src/test/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests.java index 3a84f768e6..022e5c684c 100644 --- a/config/src/test/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests.java @@ -24,9 +24,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.mock.web.MockHttpSession; import org.springframework.security.config.oauth2.client.CommonOAuth2Provider; import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; @@ -222,12 +219,8 @@ public class OAuth2ClientBeanDefinitionParserTests { ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("google"); OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, "user", TestOAuth2AccessTokens.noScopes()); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, request, response); - this.mvc.perform(get("/authorized-client").session((MockHttpSession) request.getSession())) - .andExpect(status().isOk()) - .andExpect(content().string("resolved")); + given(this.authorizedClientRepository.loadAuthorizedClient(any(), any(), any())).willReturn(authorizedClient); + this.mvc.perform(get("/authorized-client")).andExpect(status().isOk()).andExpect(content().string("resolved")); } private static OAuth2AuthorizationRequest createAuthorizationRequest(ClientRegistration clientRegistration) { diff --git a/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java index b632f834de..5ffd87f868 100644 --- a/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests.java @@ -27,9 +27,6 @@ import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.http.MediaType; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.mock.web.MockHttpSession; import org.springframework.security.authentication.event.AuthenticationSuccessEvent; import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; @@ -536,11 +533,9 @@ public class OAuth2LoginBeanDefinitionParserTests { ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId("google-login"); OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, "user", TestOAuth2AccessTokens.noScopes()); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, null, request, response); + given(this.authorizedClientRepository.loadAuthorizedClient(any(), any(), any())).willReturn(authorizedClient); // @formatter:off - this.mvc.perform(get("/authorized-client").session((MockHttpSession) request.getSession())) + this.mvc.perform(get("/authorized-client")) .andExpect(status().isOk()) .andExpect(content().string("resolved")); // @formatter:on @@ -555,7 +550,7 @@ public class OAuth2LoginBeanDefinitionParserTests { @GetMapping("/authorized-client") String authorizedClient(Model model, - @RegisteredOAuth2AuthorizedClient("google-login") OAuth2AuthorizedClient authorizedClient) { + @RegisteredOAuth2AuthorizedClient("google") OAuth2AuthorizedClient authorizedClient) { return (authorizedClient != null) ? "resolved" : "not-resolved"; } diff --git a/config/src/test/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests.java b/config/src/test/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests.java index 9eb168233a..a01345e56b 100644 --- a/config/src/test/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests.java +++ b/config/src/test/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests.java @@ -16,20 +16,11 @@ package org.springframework.security.config.http; -import java.nio.charset.StandardCharsets; - import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import net.shibboleth.utilities.java.support.xml.SerializeSupport; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; -import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; -import org.opensaml.core.xml.io.Marshaller; -import org.opensaml.saml.saml2.core.Assertion; -import org.opensaml.saml.saml2.core.Response; -import org.w3c.dom.Element; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; @@ -42,7 +33,6 @@ import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolderStrategy; -import org.springframework.security.saml2.core.OpenSamlInitializationService; import org.springframework.security.saml2.core.Saml2ParameterNames; import org.springframework.security.saml2.core.Saml2Utils; import org.springframework.security.saml2.core.TestSaml2X509Credentials; @@ -51,7 +41,6 @@ import org.springframework.security.saml2.provider.service.authentication.Saml2A import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationToken; import org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest; -import org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations; @@ -90,19 +79,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @SecurityTestExecutionListeners public class Saml2LoginBeanDefinitionParserTests { - static { - OpenSamlInitializationService.initialize(); - } - private static final String CONFIG_LOCATION_PREFIX = "classpath:org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests"; - private static final RelyingPartyRegistration registration = TestRelyingPartyRegistrations.noCredentials() - .signingX509Credentials((c) -> c.add(TestSaml2X509Credentials.assertingPartySigningCredential())) - .assertingPartyDetails((party) -> party - .verificationX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential()))) - .build(); - - private static String SIGNED_RESPONSE; + private static final String SIGNED_RESPONSE = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9ycC5leGFtcGxlLm9yZy9hY3MiIElEPSJfYzE3MzM2YTAtNTM1My00MTQ5LWI3MmMtMDNkOWY5YWYzMDdlIiBJc3N1ZUluc3RhbnQ9IjIwMjAtMDgtMDRUMjI6MDQ6NDUuMDE2WiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5hcC1lbnRpdHktaWQ8L3NhbWwyOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KPGRzOlNpZ25lZEluZm8+CjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CjxkczpSZWZlcmVuY2UgVVJJPSIjX2MxNzMzNmEwLTUzNTMtNDE0OS1iNzJjLTAzZDlmOWFmMzA3ZSI+CjxkczpUcmFuc2Zvcm1zPgo8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz4KPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgo8L2RzOlRyYW5zZm9ybXM+CjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz4KPGRzOkRpZ2VzdFZhbHVlPjYzTmlyenFzaDVVa0h1a3NuRWUrM0hWWU5aYWFsQW1OQXFMc1lGMlRuRDA9PC9kczpEaWdlc3RWYWx1ZT4KPC9kczpSZWZlcmVuY2U+CjwvZHM6U2lnbmVkSW5mbz4KPGRzOlNpZ25hdHVyZVZhbHVlPgpLMVlvWWJVUjBTclY4RTdVMkhxTTIvZUNTOTNoV25mOExnNnozeGZWMUlyalgzSXhWYkNvMVlYcnRBSGRwRVdvYTJKKzVOMmFNbFBHJiMxMzsKN2VpbDBZRC9xdUVRamRYbTNwQTBjZmEvY25pa2RuKzVhbnM0ZWQwanU1amo2dkpvZ2w2Smt4Q25LWUpwTU9HNzhtampmb0phengrWCYjMTM7CkM2NktQVStBYUdxeGVwUEQ1ZlhRdTFKSy9Jb3lBaitaa3k4Z2Jwc3VyZHFCSEJLRWxjdnVOWS92UGY0OGtBeFZBKzdtRGhNNUMvL1AmIzEzOwp0L084Y3NZYXB2UjZjdjZrdk45QXZ1N3FRdm9qVk1McHVxZWNJZDJwTUVYb0NSSnE2Nkd4MStNTUVPeHVpMWZZQlRoMEhhYjRmK3JyJiMxMzsKOEY2V1NFRC8xZllVeHliRkJqZ1Q4d2lEWHFBRU8wSVY4ZWRQeEE9PQo8L2RzOlNpZ25hdHVyZVZhbHVlPgo8L2RzOlNpZ25hdHVyZT48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iQWUzZjQ5OGI4LTliMTctNDA3OC05ZDM1LTg2YTA4NDA4NDk5NSIgSXNzdWVJbnN0YW50PSIyMDIwLTA4LTA0VDIyOjA0OjQ1LjA3N1oiIFZlcnNpb249IjIuMCI+PHNhbWwyOklzc3Vlcj5hcC1lbnRpdHktaWQ8L3NhbWwyOklzc3Vlcj48c2FtbDI6U3ViamVjdD48c2FtbDI6TmFtZUlEPnRlc3RAc2FtbC51c2VyPC9zYW1sMjpOYW1lSUQ+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90QmVmb3JlPSIyMDIwLTA4LTA0VDIxOjU5OjQ1LjA5MFoiIE5vdE9uT3JBZnRlcj0iMjA0MC0wNy0zMFQyMjowNTowNi4wODhaIiBSZWNpcGllbnQ9Imh0dHBzOi8vcnAuZXhhbXBsZS5vcmcvYWNzIi8+PC9zYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDI6U3ViamVjdD48c2FtbDI6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMjAtMDgtMDRUMjE6NTk6NDUuMDgwWiIgTm90T25PckFmdGVyPSIyMDQwLTA3LTMwVDIyOjA1OjA2LjA4N1oiLz48L3NhbWwyOkFzc2VydGlvbj48L3NhbWwycDpSZXNwb25zZT4="; private static final String IDP_SSO_URL = "https://sso-url.example.com/IDP/SSO"; @@ -138,23 +117,6 @@ public class Saml2LoginBeanDefinitionParserTests { @Autowired private MockMvc mvc; - @BeforeAll - static void createResponse() throws Exception { - String destination = registration.getAssertionConsumerServiceLocation(); - String assertingPartyEntityId = registration.getAssertingPartyDetails().getEntityId(); - String relyingPartyEntityId = registration.getEntityId(); - Response response = TestOpenSamlObjects.response(destination, assertingPartyEntityId); - Assertion assertion = TestOpenSamlObjects.assertion("test@saml.user", assertingPartyEntityId, - relyingPartyEntityId, destination); - response.getAssertions().add(assertion); - Response signed = TestOpenSamlObjects.signed(response, - registration.getSigningX509Credentials().iterator().next(), relyingPartyEntityId); - Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(signed); - Element element = marshaller.marshall(signed); - String serialized = SerializeSupport.nodeToString(element); - SIGNED_RESPONSE = Saml2Utils.samlEncode(serialized.getBytes(StandardCharsets.UTF_8)); - } - @Test public void requestWhenSingleRelyingPartyRegistrationThenAutoRedirect() throws Exception { this.spring.configLocations(this.xml("SingleRelyingPartyRegistration")).autowire(); @@ -336,9 +298,13 @@ public class Saml2LoginBeanDefinitionParserTests { throws Exception { this.spring.configLocations(this.xml("WithCustomLoginProcessingUrl-WithCustomAuthenticationConverter")) .autowire(); + RelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.noCredentials() + .assertingPartyDetails((party) -> party + .verificationX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential()))) + .build(); String response = new String(Saml2Utils.samlDecode(SIGNED_RESPONSE)); given(this.authenticationConverter.convert(any(HttpServletRequest.class))) - .willReturn(new Saml2AuthenticationToken(registration, response)); + .willReturn(new Saml2AuthenticationToken(relyingPartyRegistration, response)); // @formatter:off MockHttpServletRequestBuilder request = post("/my/custom/url").param("SAMLResponse", SIGNED_RESPONSE); // @formatter:on @@ -347,8 +313,12 @@ public class Saml2LoginBeanDefinitionParserTests { } private RelyingPartyRegistration relyingPartyRegistrationWithVerifyingCredential() { - given(this.repository.findByRegistrationId(anyString())).willReturn(registration); - return registration; + RelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.noCredentials() + .assertingPartyDetails((party) -> party + .verificationX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential()))) + .build(); + given(this.repository.findByRegistrationId(anyString())).willReturn(relyingPartyRegistration); + return relyingPartyRegistration; } private String xml(String configName) { diff --git a/config/src/test/java/org/springframework/security/config/test/SpringTestContextExtension.java b/config/src/test/java/org/springframework/security/config/test/SpringTestContextExtension.java index aeb4d37cc4..1496e52355 100644 --- a/config/src/test/java/org/springframework/security/config/test/SpringTestContextExtension.java +++ b/config/src/test/java/org/springframework/security/config/test/SpringTestContextExtension.java @@ -31,7 +31,7 @@ public class SpringTestContextExtension implements BeforeEachCallback, AfterEach @Override public void afterEach(ExtensionContext context) throws Exception { TestSecurityContextHolder.clearContext(); - getContexts(context.getRequiredTestInstance()).forEach(SpringTestContext::close); + getContexts(context.getRequiredTestInstance()).forEach((springTestContext) -> springTestContext.close()); } @Override diff --git a/config/src/test/java/org/springframework/security/config/web/server/OidcLogoutSpecTests.java b/config/src/test/java/org/springframework/security/config/web/server/OidcLogoutSpecTests.java deleted file mode 100644 index 8117dc6e28..0000000000 --- a/config/src/test/java/org/springframework/security/config/web/server/OidcLogoutSpecTests.java +++ /dev/null @@ -1,685 +0,0 @@ -/* - * 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.web.server; - -import java.io.IOException; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.interfaces.RSAPublicKey; -import java.time.Duration; -import java.time.Instant; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import com.gargoylesoftware.htmlunit.util.UrlUtils; -import com.nimbusds.jose.jwk.JWKSet; -import com.nimbusds.jose.jwk.RSAKey; -import com.nimbusds.jose.jwk.source.ImmutableJWKSet; -import com.nimbusds.jose.jwk.source.JWKSource; -import com.nimbusds.jose.proc.SecurityContext; -import com.nimbusds.oauth2.sdk.Scope; -import com.nimbusds.oauth2.sdk.token.BearerAccessToken; -import com.nimbusds.openid.connect.sdk.token.OIDCTokens; -import jakarta.annotation.PreDestroy; -import okhttp3.mockwebserver.Dispatcher; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import okhttp3.mockwebserver.RecordedRequest; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import reactor.core.publisher.Mono; - -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.core.annotation.Order; -import org.springframework.http.ResponseCookie; -import org.springframework.http.client.reactive.ClientHttpConnector; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; -import org.springframework.security.config.test.SpringTestContext; -import org.springframework.security.config.test.SpringTestContextExtension; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; -import org.springframework.security.core.userdetails.ReactiveUserDetailsService; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.oauth2.client.oidc.authentication.logout.LogoutTokenClaimNames; -import org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken; -import org.springframework.security.oauth2.client.oidc.authentication.logout.TestOidcLogoutTokens; -import org.springframework.security.oauth2.client.oidc.server.session.InMemoryReactiveOidcSessionRegistry; -import org.springframework.security.oauth2.client.oidc.server.session.ReactiveOidcSessionRegistry; -import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository; -import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; -import org.springframework.security.oauth2.client.registration.TestClientRegistrations; -import org.springframework.security.oauth2.core.oidc.OidcIdToken; -import org.springframework.security.oauth2.core.oidc.TestOidcIdTokens; -import org.springframework.security.oauth2.core.oidc.user.OidcUser; -import org.springframework.security.oauth2.jwt.JwtClaimsSet; -import org.springframework.security.oauth2.jwt.JwtEncoder; -import org.springframework.security.oauth2.jwt.JwtEncoderParameters; -import org.springframework.security.oauth2.jwt.NimbusJwtEncoder; -import org.springframework.security.web.server.SecurityWebFilterChain; -import org.springframework.security.web.server.authentication.logout.ServerLogoutHandler; -import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher; -import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher; -import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher; -import org.springframework.test.web.reactive.server.FluxExchangeResult; -import org.springframework.test.web.reactive.server.WebTestClient; -import org.springframework.test.web.reactive.server.WebTestClientConfigurer; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.context.ConfigurableWebApplicationContext; -import org.springframework.web.reactive.config.EnableWebFlux; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.server.WebSession; -import org.springframework.web.server.adapter.WebHttpHandlerBuilder; - -import static org.hamcrest.Matchers.containsString; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockAuthentication; -import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity; - -/** - * Tests for {@link ServerHttpSecurity.OAuth2ResourceServerSpec} - */ -@ExtendWith({ SpringTestContextExtension.class }) -public class OidcLogoutSpecTests { - - private static final String SESSION_COOKIE_NAME = "SESSION"; - - private WebTestClient test; - - @Autowired(required = false) - private MockWebServer web; - - @Autowired - private ClientRegistration clientRegistration; - - public final SpringTestContext spring = new SpringTestContext(this); - - @Autowired - public void setApplicationContext(ApplicationContext context) { - this.test = WebTestClient.bindToApplicationContext(context) - .apply(springSecurity()) - .configureClient() - .responseTimeout(Duration.ofDays(1)) - .build(); - if (context instanceof ConfigurableWebApplicationContext configurable) { - configurable.getBeanFactory().registerResolvableDependency(WebTestClient.class, this.test); - } - } - - @Test - void logoutWhenDefaultsThenRemotelyInvalidatesSessions() { - this.spring.register(WebServerConfig.class, OidcProviderConfig.class, DefaultConfig.class).autowire(); - String registrationId = this.clientRegistration.getRegistrationId(); - String session = login(); - String logoutToken = this.test.mutateWith(session(session)) - .get() - .uri("/token/logout") - .exchange() - .expectStatus() - .isOk() - .returnResult(String.class) - .getResponseBody() - .blockFirst(); - this.test.post() - .uri(this.web.url("/logout/connect/back-channel/" + registrationId).toString()) - .body(BodyInserters.fromFormData("logout_token", logoutToken)) - .exchange() - .expectStatus() - .isOk(); - this.test.mutateWith(session(session)).get().uri("/token/logout").exchange().expectStatus().isUnauthorized(); - } - - @Test - void logoutWhenInvalidLogoutTokenThenBadRequest() { - this.spring.register(WebServerConfig.class, OidcProviderConfig.class, DefaultConfig.class).autowire(); - this.test.get().uri("/token/logout").exchange().expectStatus().isUnauthorized(); - String registrationId = this.clientRegistration.getRegistrationId(); - FluxExchangeResult result = this.test.get() - .uri("/oauth2/authorization/" + registrationId) - .exchange() - .expectStatus() - .isFound() - .returnResult(String.class); - String session = sessionId(result); - String redirectUrl = UrlUtils.decode(result.getResponseHeaders().getLocation().toString()); - String state = this.test - .mutateWith(mockAuthentication(new TestingAuthenticationToken(this.clientRegistration.getClientId(), - this.clientRegistration.getClientSecret(), "APP"))) - .get() - .uri(redirectUrl) - .exchange() - .returnResult(String.class) - .getResponseBody() - .blockFirst(); - result = this.test.get() - .uri("/login/oauth2/code/" + registrationId + "?code=code&state=" + state) - .cookie("SESSION", session) - .exchange() - .expectStatus() - .isFound() - .returnResult(String.class); - session = sessionId(result); - this.test.post() - .uri(this.web.url("/logout/connect/back-channel/" + registrationId).toString()) - .body(BodyInserters.fromFormData("logout_token", "invalid")) - .exchange() - .expectStatus() - .isBadRequest(); - this.test.get().uri("/token/logout").cookie("SESSION", session).exchange().expectStatus().isOk(); - } - - @Test - void logoutWhenLogoutTokenSpecifiesOneSessionThenRemotelyInvalidatesOnlyThatSession() throws Exception { - this.spring.register(WebServerConfig.class, OidcProviderConfig.class, DefaultConfig.class).autowire(); - String registrationId = this.clientRegistration.getRegistrationId(); - String one = login(); - String two = login(); - String three = login(); - String logoutToken = this.test.get() - .uri("/token/logout") - .cookie("SESSION", one) - .exchange() - .expectStatus() - .isOk() - .returnResult(String.class) - .getResponseBody() - .blockFirst(); - this.test.post() - .uri(this.web.url("/logout/connect/back-channel/" + registrationId).toString()) - .body(BodyInserters.fromFormData("logout_token", logoutToken)) - .exchange() - .expectStatus() - .isOk(); - this.test.get().uri("/token/logout").cookie("SESSION", one).exchange().expectStatus().isUnauthorized(); - this.test.get().uri("/token/logout").cookie("SESSION", two).exchange().expectStatus().isOk(); - logoutToken = this.test.get() - .uri("/token/logout/all") - .cookie("SESSION", three) - .exchange() - .expectStatus() - .isOk() - .returnResult(String.class) - .getResponseBody() - .blockFirst(); - this.test.post() - .uri(this.web.url("/logout/connect/back-channel/" + registrationId).toString()) - .body(BodyInserters.fromFormData("logout_token", logoutToken)) - .exchange() - .expectStatus() - .isOk(); - this.test.get().uri("/token/logout").cookie("SESSION", two).exchange().expectStatus().isUnauthorized(); - this.test.get().uri("/token/logout").cookie("SESSION", three).exchange().expectStatus().isUnauthorized(); - } - - @Test - void logoutWhenRemoteLogoutFailsThenReportsPartialLogout() { - this.spring.register(WebServerConfig.class, OidcProviderConfig.class, WithBrokenLogoutConfig.class).autowire(); - ServerLogoutHandler logoutHandler = this.spring.getContext().getBean(ServerLogoutHandler.class); - given(logoutHandler.logout(any(), any())).willReturn(Mono.error(() -> new IllegalStateException("illegal"))); - String registrationId = this.clientRegistration.getRegistrationId(); - String one = login(); - String logoutToken = this.test.get() - .uri("/token/logout/all") - .cookie("SESSION", one) - .exchange() - .expectStatus() - .isOk() - .returnResult(String.class) - .getResponseBody() - .blockFirst(); - this.test.post() - .uri(this.web.url("/logout/connect/back-channel/" + registrationId).toString()) - .body(BodyInserters.fromFormData("logout_token", logoutToken)) - .exchange() - .expectStatus() - .isBadRequest() - .expectBody(String.class) - .value(containsString("partial_logout")); - this.test.get().uri("/token/logout").cookie("SESSION", one).exchange().expectStatus().isOk(); - } - - @Test - void logoutWhenCustomComponentsThenUses() { - this.spring.register(WebServerConfig.class, OidcProviderConfig.class, WithCustomComponentsConfig.class) - .autowire(); - String registrationId = this.clientRegistration.getRegistrationId(); - String sessionId = login(); - String logoutToken = this.test.get() - .uri("/token/logout") - .cookie("SESSION", sessionId) - .exchange() - .expectStatus() - .isOk() - .returnResult(String.class) - .getResponseBody() - .blockFirst(); - this.test.post() - .uri(this.web.url("/logout/connect/back-channel/" + registrationId).toString()) - .body(BodyInserters.fromFormData("logout_token", logoutToken)) - .exchange() - .expectStatus() - .isOk(); - this.test.get().uri("/token/logout").cookie("SESSION", sessionId).exchange().expectStatus().isUnauthorized(); - ReactiveOidcSessionRegistry sessionRegistry = this.spring.getContext() - .getBean(ReactiveOidcSessionRegistry.class); - verify(sessionRegistry, atLeastOnce()).saveSessionInformation(any()); - verify(sessionRegistry, atLeastOnce()).removeSessionInformation(any(OidcLogoutToken.class)); - } - - private String login() { - this.test.get().uri("/token/logout").exchange().expectStatus().isUnauthorized(); - String registrationId = this.clientRegistration.getRegistrationId(); - FluxExchangeResult result = this.test.get() - .uri("/oauth2/authorization/" + registrationId) - .exchange() - .expectStatus() - .isFound() - .returnResult(String.class); - String sessionId = sessionId(result); - String redirectUrl = UrlUtils.decode(result.getResponseHeaders().getLocation().toString()); - result = this.test - .mutateWith(mockAuthentication(new TestingAuthenticationToken(this.clientRegistration.getClientId(), - this.clientRegistration.getClientSecret(), "APP"))) - .get() - .uri(redirectUrl) - .exchange() - .returnResult(String.class); - String state = result.getResponseBody().blockFirst(); - result = this.test.mutateWith(session(sessionId)) - .get() - .uri("/login/oauth2/code/" + registrationId + "?code=code&state=" + state) - .exchange() - .expectStatus() - .isFound() - .returnResult(String.class); - return sessionId(result); - } - - private String sessionId(FluxExchangeResult result) { - List cookies = result.getResponseCookies().get(SESSION_COOKIE_NAME); - if (cookies == null || cookies.isEmpty()) { - return null; - } - return cookies.get(0).getValue(); - } - - static SessionMutator session(String session) { - return new SessionMutator(session); - } - - private record SessionMutator(String session) implements WebTestClientConfigurer { - - @Override - public void afterConfigurerAdded(WebTestClient.Builder builder, WebHttpHandlerBuilder httpHandlerBuilder, - ClientHttpConnector connector) { - builder.defaultCookie(SESSION_COOKIE_NAME, this.session); - } - - } - - @Configuration - static class RegistrationConfig { - - @Autowired(required = false) - MockWebServer web; - - @Bean - ClientRegistration clientRegistration() { - if (this.web == null) { - return TestClientRegistrations.clientRegistration().build(); - } - String issuer = this.web.url("/").toString(); - return TestClientRegistrations.clientRegistration() - .issuerUri(issuer) - .jwkSetUri(issuer + "jwks") - .tokenUri(issuer + "token") - .userInfoUri(issuer + "user") - .scope("openid") - .build(); - } - - @Bean - ReactiveClientRegistrationRepository clientRegistrationRepository(ClientRegistration clientRegistration) { - return new InMemoryReactiveClientRegistrationRepository(clientRegistration); - } - - } - - @Configuration - @EnableWebFluxSecurity - @Import(RegistrationConfig.class) - static class DefaultConfig { - - @Bean - @Order(1) - SecurityWebFilterChain filters(ServerHttpSecurity http) throws Exception { - // @formatter:off - http - .authorizeExchange((authorize) -> authorize.anyExchange().authenticated()) - .oauth2Login(Customizer.withDefaults()) - .oidcLogout((oidc) -> oidc.backChannel(Customizer.withDefaults())); - // @formatter:on - - return http.build(); - } - - } - - @Configuration - @EnableWebFluxSecurity - @Import(RegistrationConfig.class) - static class WithCustomComponentsConfig { - - ReactiveOidcSessionRegistry sessionRegistry = spy(new InMemoryReactiveOidcSessionRegistry()); - - @Bean - @Order(1) - SecurityWebFilterChain filters(ServerHttpSecurity http) throws Exception { - // @formatter:off - http - .authorizeExchange((authorize) -> authorize.anyExchange().authenticated()) - .oauth2Login((oauth2) -> oauth2.oidcSessionRegistry(this.sessionRegistry)) - .oidcLogout((oidc) -> oidc.backChannel(Customizer.withDefaults())); - // @formatter:on - - return http.build(); - } - - @Bean - ReactiveOidcSessionRegistry sessionRegistry() { - return this.sessionRegistry; - } - - } - - @Configuration - @EnableWebFluxSecurity - @Import(RegistrationConfig.class) - static class WithBrokenLogoutConfig { - - private final ServerLogoutHandler logoutHandler = mock(ServerLogoutHandler.class); - - @Bean - @Order(1) - SecurityWebFilterChain filters(ServerHttpSecurity http) throws Exception { - // @formatter:off - http - .authorizeExchange((authorize) -> authorize.anyExchange().authenticated()) - .logout((logout) -> logout.logoutHandler(this.logoutHandler)) - .oauth2Login(Customizer.withDefaults()) - .oidcLogout((oidc) -> oidc.backChannel(Customizer.withDefaults())); - // @formatter:on - - return http.build(); - } - - @Bean - ServerLogoutHandler logoutHandler() { - return this.logoutHandler; - } - - } - - @Configuration - @EnableWebFluxSecurity - @EnableWebFlux - @RestController - static class OidcProviderConfig { - - private static final RSAKey key = key(); - - private static final JWKSource jwks = jwks(key); - - private static RSAKey key() { - try { - KeyPair pair = KeyPairGenerator.getInstance("RSA").generateKeyPair(); - return new RSAKey.Builder((RSAPublicKey) pair.getPublic()).privateKey(pair.getPrivate()).build(); - } - catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - private static JWKSource jwks(RSAKey key) { - try { - return new ImmutableJWKSet<>(new JWKSet(key)); - } - catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - private final String username = "user"; - - private final JwtEncoder encoder = new NimbusJwtEncoder(jwks); - - private String nonce; - - @Autowired - ClientRegistration registration; - - static ServerWebExchangeMatcher or(String... patterns) { - List matchers = new ArrayList<>(); - for (String pattern : patterns) { - matchers.add(new PathPatternParserServerWebExchangeMatcher(pattern)); - } - return new OrServerWebExchangeMatcher(matchers); - } - - @Bean - @Order(0) - SecurityWebFilterChain authorizationServer(ServerHttpSecurity http, ClientRegistration registration) - throws Exception { - // @formatter:off - http - .securityMatcher(or("/jwks", "/login/oauth/authorize", "/nonce", "/token", "/token/logout", "/user")) - .authorizeExchange((authorize) -> authorize - .pathMatchers("/jwks").permitAll() - .anyExchange().authenticated() - ) - .httpBasic(Customizer.withDefaults()) - .oauth2ResourceServer((oauth2) -> oauth2 - .jwt((jwt) -> jwt.jwkSetUri(registration.getProviderDetails().getJwkSetUri())) - ); - // @formatter:off - - return http.build(); - } - - @Bean - ReactiveUserDetailsService users(ClientRegistration registration) { - return new MapReactiveUserDetailsService(User.withUsername(registration.getClientId()) - .password("{noop}" + registration.getClientSecret()).authorities("APP").build()); - } - - @GetMapping("/login/oauth/authorize") - String nonce(@RequestParam("nonce") String nonce, @RequestParam("state") String state) { - this.nonce = nonce; - return state; - } - - @PostMapping("/token") - Map accessToken(WebSession session) { - JwtEncoderParameters parameters = JwtEncoderParameters - .from(JwtClaimsSet.builder().id("id").subject(this.username) - .issuer(this.registration.getProviderDetails().getIssuerUri()).issuedAt(Instant.now()) - .expiresAt(Instant.now().plusSeconds(86400)).claim("scope", "openid").build()); - String token = this.encoder.encode(parameters).getTokenValue(); - return new OIDCTokens(idToken(session.getId()), new BearerAccessToken(token, 86400, new Scope("openid")), null) - .toJSONObject(); - } - - String idToken(String sessionId) { - OidcIdToken token = TestOidcIdTokens.idToken().issuer(this.registration.getProviderDetails().getIssuerUri()) - .subject(this.username).expiresAt(Instant.now().plusSeconds(86400)) - .audience(List.of(this.registration.getClientId())).nonce(this.nonce) - .claim(LogoutTokenClaimNames.SID, sessionId).build(); - JwtEncoderParameters parameters = JwtEncoderParameters - .from(JwtClaimsSet.builder().claims((claims) -> claims.putAll(token.getClaims())).build()); - return this.encoder.encode(parameters).getTokenValue(); - } - - @GetMapping("/user") - Map userinfo() { - return Map.of("sub", this.username, "id", this.username); - } - - @GetMapping("/jwks") - String jwks() { - return new JWKSet(key).toString(); - } - - @GetMapping("/token/logout") - String logoutToken(@AuthenticationPrincipal OidcUser user) { - OidcLogoutToken token = TestOidcLogoutTokens.withUser(user) - .audience(List.of(this.registration.getClientId())).build(); - JwtEncoderParameters parameters = JwtEncoderParameters - .from(JwtClaimsSet.builder().claims((claims) -> claims.putAll(token.getClaims())).build()); - return this.encoder.encode(parameters).getTokenValue(); - } - - @GetMapping("/token/logout/all") - String logoutTokenAll(@AuthenticationPrincipal OidcUser user) { - OidcLogoutToken token = TestOidcLogoutTokens.withUser(user) - .audience(List.of(this.registration.getClientId())) - .claims((claims) -> claims.remove(LogoutTokenClaimNames.SID)).build(); - JwtEncoderParameters parameters = JwtEncoderParameters - .from(JwtClaimsSet.builder().claims((claims) -> claims.putAll(token.getClaims())).build()); - return this.encoder.encode(parameters).getTokenValue(); - } - } - - @Configuration - static class WebServerConfig { - - private final MockWebServer server = new MockWebServer(); - - @Bean - MockWebServer web(ObjectProvider web) { - this.server.setDispatcher(new WebTestClientDispatcher(web)); - return this.server; - } - - @PreDestroy - void shutdown() throws IOException { - this.server.shutdown(); - } - - } - - private static class WebTestClientDispatcher extends Dispatcher { - - private final ObjectProvider webProvider; - - private WebTestClient web; - - WebTestClientDispatcher(ObjectProvider web) { - this.webProvider = web; - } - - @Override - public MockResponse dispatch(RecordedRequest request) throws InterruptedException { - this.web = this.webProvider.getObject(); - String method = request.getMethod(); - String path = request.getPath(); - String csrf = request.getHeader("X-CSRF-TOKEN"); - String sessionId = session(request); - WebTestClient.RequestHeadersSpec r; - if ("GET".equals(method)) { - r = this.web.get().uri(path); - } - else { - WebTestClient.RequestBodySpec body; - if (csrf == null) { - body = this.web.mutateWith(csrf()).post().uri(path); - } - else { - body = this.web.post().uri(path).header("X-CSRF-TOKEN", csrf); - } - body.body(BodyInserters.fromValue(request.getBody().readUtf8())); - r = body; - } - for (Map.Entry> header : request.getHeaders().toMultimap().entrySet()) { - if (header.getKey().equalsIgnoreCase("Cookie")) { - continue; - } - r.header(header.getKey(), header.getValue().iterator().next()); - } - if (sessionId != null) { - r.cookie(SESSION_COOKIE_NAME, sessionId); - } - - try { - FluxExchangeResult result = r.exchange().returnResult(String.class); - return toMockResponse(result); - } - catch (Exception ex) { - MockResponse response = new MockResponse(); - response.setResponseCode(500); - response.setBody(ex.getMessage()); - return response; - } - } - - private String session(RecordedRequest request) { - String cookieHeaderValue = request.getHeader("Cookie"); - if (cookieHeaderValue == null) { - return null; - } - String[] cookies = cookieHeaderValue.split(";"); - for (String cookie : cookies) { - String[] parts = cookie.split("="); - if (SESSION_COOKIE_NAME.equals(parts[0])) { - return parts[1]; - } - } - return null; - } - - private MockResponse toMockResponse(FluxExchangeResult result) { - MockResponse response = new MockResponse(); - response.setResponseCode(result.getStatus().value()); - for (String name : result.getResponseHeaders().keySet()) { - response.addHeader(name, result.getResponseHeaders().getFirst(name)); - } - String body = result.getResponseBody().blockFirst(); - if (body != null) { - response.setBody(body); - } - return response; - } - - } - -} diff --git a/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java b/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java index b8bcb40a67..872b1cf4fb 100644 --- a/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java +++ b/config/src/test/java/org/springframework/security/config/web/server/ServerHttpSecurityTests.java @@ -746,7 +746,7 @@ public class ServerHttpSecurityTests { private Optional getWebFilter(SecurityWebFilterChain filterChain, Class filterClass) { return (Optional) filterChain.getWebFilters() .filter(Objects::nonNull) - .filter((filter) -> filterClass.isAssignableFrom(filter.getClass())) + .filter((filter) -> filter.getClass().isAssignableFrom(filterClass)) .singleOrEmpty() .blockOptional(); } diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/method/configuration/KotlinEnableReactiveMethodSecurityNoAuthorizationManagerTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/method/configuration/KotlinEnableReactiveMethodSecurityNoAuthorizationManagerTests.kt index 32a3379b26..0ea704fe34 100644 --- a/config/src/test/kotlin/org/springframework/security/config/annotation/method/configuration/KotlinEnableReactiveMethodSecurityNoAuthorizationManagerTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/annotation/method/configuration/KotlinEnableReactiveMethodSecurityNoAuthorizationManagerTests.kt @@ -18,17 +18,14 @@ package org.springframework.security.config.annotation.method.configuration import io.mockk.Called import io.mockk.clearAllMocks -import io.mockk.coEvery -import io.mockk.coVerify import io.mockk.mockk import io.mockk.verify import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.toList import kotlinx.coroutines.runBlocking import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThatExceptionOfType -import org.junit.jupiter.api.AfterEach +import org.junit.After import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.springframework.beans.factory.annotation.Autowired @@ -49,7 +46,7 @@ class KotlinEnableReactiveMethodSecurityNoAuthorizationManagerTests { @Autowired var messageService: KotlinReactiveMessageService? = null - @AfterEach + @After fun cleanup() { clearAllMocks() } @@ -128,16 +125,6 @@ class KotlinEnableReactiveMethodSecurityNoAuthorizationManagerTests { verify { delegate wasNot Called } } - @Test - @WithMockUser(authorities = ["ROLE_ADMIN"]) - fun `suspendingPreAuthorizeDelegate when user has role then delegate called`() { - coEvery { delegate.suspendingPreAuthorizeHasRole() } returns "ok" - runBlocking { - messageService!!.suspendingPreAuthorizeDelegate() - } - coVerify(exactly = 1) { delegate.suspendingPreAuthorizeHasRole() } - } - @Test @WithMockUser(authorities = ["ROLE_ADMIN"]) fun `suspendingFlowPreAuthorize when user has role then success`() { @@ -181,16 +168,6 @@ class KotlinEnableReactiveMethodSecurityNoAuthorizationManagerTests { verify { delegate wasNot Called } } - @Test - @WithMockUser(authorities = ["ROLE_ADMIN"]) - fun `suspendingFlowPreAuthorizeDelegate when user has role then delegate called`() { - coEvery { delegate.flowPreAuthorize() } returns flow { } - runBlocking { - messageService!!.suspendingFlowPreAuthorizeDelegate().collect() - } - coVerify(exactly = 1) { delegate.flowPreAuthorize() } - } - @Test @WithMockUser(authorities = ["ROLE_ADMIN"]) fun `flowPreAuthorize when user has role then success`() { @@ -234,16 +211,6 @@ class KotlinEnableReactiveMethodSecurityNoAuthorizationManagerTests { verify { delegate wasNot Called } } - @Test - @WithMockUser(authorities = ["ROLE_ADMIN"]) - fun `flowPreAuthorizeDelegate when user has role then delegate called`() { - coEvery { delegate.flowPreAuthorize() } returns flow { } - runBlocking { - messageService!!.flowPreAuthorizeDelegate().collect() - } - coVerify(exactly = 1) { delegate.flowPreAuthorize() } - } - @Configuration @EnableReactiveMethodSecurity(useAuthorizationManager = false) open class Config { diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/web/AuthorizeHttpRequestsDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/web/AuthorizeHttpRequestsDslTests.kt index e341921325..3dac9f6ed9 100644 --- a/config/src/test/kotlin/org/springframework/security/config/annotation/web/AuthorizeHttpRequestsDslTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/annotation/web/AuthorizeHttpRequestsDslTests.kt @@ -124,13 +124,11 @@ class AuthorizeHttpRequestsDslTests { @RestController internal class PathController { @RequestMapping("/path") - fun path(): String { - return "ok" + fun path() { } @RequestMapping("/onlyPostPermitted") - fun onlyPostPermitted():String { - return "ok" + fun onlyPostPermitted() { } } } @@ -276,8 +274,7 @@ class AuthorizeHttpRequestsDslTests { @RestController internal class PathController { @GetMapping("/") - fun index(): String { - return "ok" + fun index() { } } @@ -343,8 +340,7 @@ class AuthorizeHttpRequestsDslTests { @RestController internal class PathController { @GetMapping("/") - fun index(): String { - return "ok" + fun index() { } } @@ -409,8 +405,7 @@ class AuthorizeHttpRequestsDslTests { @RestController internal class PathController { @GetMapping("/") - fun index(): String { - return "ok" + fun index() { } } @@ -476,8 +471,7 @@ class AuthorizeHttpRequestsDslTests { @RestController internal class PathController { @GetMapping("/") - fun index(): String { - return "ok" + fun index() { } } diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/web/AuthorizeRequestsDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/web/AuthorizeRequestsDslTests.kt index be39dbd89d..c4c3dcc067 100644 --- a/config/src/test/kotlin/org/springframework/security/config/annotation/web/AuthorizeRequestsDslTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/annotation/web/AuthorizeRequestsDslTests.kt @@ -114,13 +114,11 @@ class AuthorizeRequestsDslTests { @RestController internal class PathController { @RequestMapping("/path") - fun path(): String { - return "ok" + fun path() { } @RequestMapping("/onlyPostPermitted") - fun onlyPostPermitted(): String { - return "ok" + fun onlyPostPermitted() { } } } @@ -173,8 +171,7 @@ class AuthorizeRequestsDslTests { @RestController internal class PathController { @RequestMapping("/path") - fun path(): String { - return "ok" + fun path() { } } } @@ -330,8 +327,7 @@ class AuthorizeRequestsDslTests { @RestController internal class PathController { @GetMapping("/") - fun index():String { - return "ok" + fun index() { } } @@ -402,8 +398,7 @@ class AuthorizeRequestsDslTests { @RestController internal class PathController { @GetMapping("/") - fun index():String { - return "ok" + fun index() { } } @@ -466,8 +461,7 @@ class AuthorizeRequestsDslTests { @RestController internal class PathController { @RequestMapping("/path") - fun path():String { - return "ok" + fun path() { } } } @@ -490,8 +484,7 @@ class AuthorizeRequestsDslTests { @RestController internal class PathController { @RequestMapping("/path") - fun path(): String { - return "ok" + fun path() { } } } @@ -529,8 +522,7 @@ class AuthorizeRequestsDslTests { @RestController internal class PathController { @RequestMapping("/path") - fun path(): String { - return "ok" + fun path() { } } } diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/web/CorsDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/web/CorsDslTests.kt index 67574bc8a4..39adb26fa5 100644 --- a/config/src/test/kotlin/org/springframework/security/config/annotation/web/CorsDslTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/annotation/web/CorsDslTests.kt @@ -31,9 +31,7 @@ import org.springframework.security.config.test.SpringTestContextExtension import org.springframework.security.web.SecurityFilterChain import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.get -import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMethod -import org.springframework.web.bind.annotation.RestController import org.springframework.web.cors.CorsConfiguration import org.springframework.web.cors.CorsConfigurationSource import org.springframework.web.cors.UrlBasedCorsConfigurationSource @@ -74,7 +72,7 @@ class CorsDslTests { @Test fun `CORS when CORS configuration source bean then responds with CORS header`() { - this.spring.register(CorsCrossOriginBeanConfig::class.java, HomeController::class.java).autowire() + this.spring.register(CorsCrossOriginBeanConfig::class.java).autowire() this.mockMvc.get("/") { @@ -151,7 +149,7 @@ class CorsDslTests { @Test fun `CORS when CORS configuration source dsl then responds with CORS header`() { - this.spring.register(CorsCrossOriginBeanConfig::class.java, HomeController::class.java).autowire() + this.spring.register(CorsCrossOriginBeanConfig::class.java).autowire() this.mockMvc.get("/") { @@ -182,13 +180,4 @@ class CorsDslTests { return http.build() } } - - @RestController - private class HomeController { - @GetMapping("/") - fun ok(): String { - return "ok" - } - } - } diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/web/CsrfDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/web/CsrfDslTests.kt index ae6568263d..ac2c78553c 100644 --- a/config/src/test/kotlin/org/springframework/security/config/annotation/web/CsrfDslTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/annotation/web/CsrfDslTests.kt @@ -290,13 +290,11 @@ class CsrfDslTests { @RestController internal class BasicController { @PostMapping("/test1") - fun test1():String { - return "ok" + fun test1() { } @PostMapping("/test2") - fun test2():String { - return "ok" + fun test2() { } } diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/web/HttpBasicDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/web/HttpBasicDslTests.kt index da6ac0be36..2a1f1f6ad6 100644 --- a/config/src/test/kotlin/org/springframework/security/config/annotation/web/HttpBasicDslTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/annotation/web/HttpBasicDslTests.kt @@ -217,8 +217,7 @@ class HttpBasicDslTests { @RestController class MainController { @GetMapping("/") - fun main():String { - return "ok" + fun main() { } } } diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/web/HttpSecurityDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/web/HttpSecurityDslTests.kt index ed11cf15a2..bdb849e83a 100644 --- a/config/src/test/kotlin/org/springframework/security/config/annotation/web/HttpSecurityDslTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/annotation/web/HttpSecurityDslTests.kt @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,6 @@ package org.springframework.security.config.annotation.web import io.mockk.every import io.mockk.mockkObject import io.mockk.verify -import jakarta.servlet.Filter import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -56,6 +55,7 @@ import org.springframework.test.web.servlet.get import org.springframework.test.web.servlet.post import org.springframework.test.web.servlet.request.MockMvcRequestBuilders import org.springframework.web.servlet.config.annotation.EnableWebMvc +import jakarta.servlet.Filter /** * Tests for [HttpSecurityDsl] @@ -530,18 +530,6 @@ class HttpSecurityDslTests { ) } - @Test - fun `HTTP security when apply custom security configurer using with then custom filter added to filter chain`() { - this.spring.register(CustomSecurityConfigurerConfig::class.java).autowire() - - val filterChain = spring.context.getBean(FilterChainProxy::class.java) - val filterClasses: List> = filterChain.getFilters("/").map { it.javaClass } - - assertThat(filterClasses).contains( - CustomFilter::class.java - ) - } - @Configuration @EnableWebSecurity @EnableWebMvc @@ -557,21 +545,6 @@ class HttpSecurityDslTests { } } - @Configuration - @EnableWebSecurity - @EnableWebMvc - open class CustomSecurityConfigurerUsingWithConfig { - @Bean - open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - http { - with(CustomSecurityConfigurer()) { - filter = CustomFilter() - } - } - return http.build() - } - } - class CustomSecurityConfigurer> : AbstractHttpConfigurer, H>() { var filter: Filter? = null override fun init(builder: H) { @@ -582,46 +555,4 @@ class HttpSecurityDslTests { builder.addFilterBefore(CustomFilter(), UsernamePasswordAuthenticationFilter::class.java) } } - - @Test - fun `HTTP security when apply form login using with from custom security configurer then filter added to filter chain`() { - this.spring.register(CustomDslUsingWithConfig::class.java).autowire() - - val filterChain = spring.context.getBean(FilterChainProxy::class.java) - val filterClasses: List> = filterChain.getFilters("/").map { it.javaClass } - - assertThat(filterClasses).contains( - UsernamePasswordAuthenticationFilter::class.java - ) - } - - @Configuration - @EnableWebSecurity - @EnableWebMvc - open class CustomDslUsingWithConfig { - @Bean - open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - http { - with(CustomDslFormLogin()) { - formLogin = true - } - httpBasic { } - } - return http.build() - } - } - - class CustomDslFormLogin: AbstractHttpConfigurer() { - - var formLogin = false - - override fun init(builder: HttpSecurity) { - if (formLogin) { - builder.formLogin { } - } - } - - } - - } diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/web/OAuth2LoginDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/web/OAuth2LoginDslTests.kt index cca0178e77..13102b63d9 100644 --- a/config/src/test/kotlin/org/springframework/security/config/annotation/web/OAuth2LoginDslTests.kt +++ b/config/src/test/kotlin/org/springframework/security/config/annotation/web/OAuth2LoginDslTests.kt @@ -128,9 +128,7 @@ class OAuth2LoginDslTests { @RestController class LoginController { @GetMapping("/custom-login") - fun loginPage():String { - return "ok" - } + fun loginPage() { } } } diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/web/OidcLogoutDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/web/OidcLogoutDslTests.kt deleted file mode 100644 index 468be4251b..0000000000 --- a/config/src/test/kotlin/org/springframework/security/config/annotation/web/OidcLogoutDslTests.kt +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.config.annotation.web - -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.security.config.annotation.web.builders.HttpSecurity -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity -import org.springframework.security.config.test.SpringTestContext -import org.springframework.security.config.test.SpringTestContextExtension -import org.springframework.security.oauth2.client.registration.ClientRegistration -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository -import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository -import org.springframework.security.oauth2.client.registration.TestClientRegistrations -import org.springframework.security.web.SecurityFilterChain -import org.springframework.test.web.servlet.MockMvc -import org.springframework.test.web.servlet.post - -/** - * Tests for [OAuth2ClientDsl] - * - * @author Eleftheria Stein - */ -@ExtendWith(SpringTestContextExtension::class) -class OidcLogoutDslTests { - @JvmField - val spring = SpringTestContext(this) - - @Autowired - lateinit var mockMvc: MockMvc - - @Test - fun `oidcLogout when invalid token then errors`() { - this.spring.register(ClientRepositoryConfig::class.java).autowire() - val clientRegistration = this.spring.context.getBean(ClientRegistration::class.java) - this.mockMvc.post("/logout/connect/back-channel/" + clientRegistration.registrationId) { - param("logout_token", "token") - }.andExpect { status { isBadRequest() } } - } - - @Configuration - @EnableWebSecurity - open class ClientRepositoryConfig { - - @Bean - open fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - http { - oauth2Login { } - oidcLogout { - backChannel { } - } - authorizeHttpRequests { - authorize(anyRequest, authenticated) - } - } - return http.build() - } - - @Bean - open fun clientRegistration(): ClientRegistration { - return TestClientRegistrations.clientRegistration().build() - } - - @Bean - open fun clientRegistrationRepository(clientRegistration: ClientRegistration): ClientRegistrationRepository { - return InMemoryClientRegistrationRepository(clientRegistration) - } - } - -} diff --git a/config/src/test/kotlin/org/springframework/security/config/web/server/ServerOidcLogoutDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/web/server/ServerOidcLogoutDslTests.kt deleted file mode 100644 index a4b62b2dc0..0000000000 --- a/config/src/test/kotlin/org/springframework/security/config/web/server/ServerOidcLogoutDslTests.kt +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2002-2023 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.web.server - -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.extension.ExtendWith -import org.springframework.beans.factory.annotation.Autowired -import org.springframework.context.ApplicationContext -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity -import org.springframework.security.config.test.SpringTestContext -import org.springframework.security.config.test.SpringTestContextExtension -import org.springframework.security.oauth2.client.registration.ClientRegistration -import org.springframework.security.oauth2.client.registration.InMemoryReactiveClientRegistrationRepository -import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository -import org.springframework.security.oauth2.client.registration.TestClientRegistrations -import org.springframework.security.web.server.SecurityWebFilterChain -import org.springframework.test.web.reactive.server.WebTestClient -import org.springframework.web.reactive.config.EnableWebFlux -import org.springframework.web.reactive.function.BodyInserters - -/** - * Tests for [ServerOidcLogoutDsl] - * - * @author Josh Cummings - */ -@ExtendWith(SpringTestContextExtension::class) -class ServerOidcLogoutDslTests { - @JvmField - val spring = SpringTestContext(this) - - private lateinit var client: WebTestClient - - @Autowired - fun setup(context: ApplicationContext) { - this.client = WebTestClient - .bindToApplicationContext(context) - .configureClient() - .build() - } - - @Test - fun `oidcLogout when invalid token then errors`() { - this.spring.register(ClientRepositoryConfig::class.java).autowire() - val clientRegistration = this.spring.context.getBean(ClientRegistration::class.java) - this.client.post() - .uri("/logout/connect/back-channel/" + clientRegistration.registrationId) - .body(BodyInserters.fromFormData("logout_token", "token")) - .exchange() - .expectStatus().isBadRequest - } - - @Configuration - @EnableWebFlux - @EnableWebFluxSecurity - open class ClientRepositoryConfig { - - @Bean - open fun securityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { - return http { - oauth2Login { } - oidcLogout { - backChannel { } - } - authorizeExchange { - authorize(anyExchange, authenticated) - } - } - } - - @Bean - open fun clientRegistration(): ClientRegistration { - return TestClientRegistrations.clientRegistration().build() - } - - @Bean - open fun clientRegistrationRepository(clientRegistration: ClientRegistration): ReactiveClientRegistrationRepository { - return InMemoryReactiveClientRegistrationRepository(clientRegistration) - } - } - -} diff --git a/config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-mock-csrf-token-repository.xml b/config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-mock-csrf-token-repository.xml index e8ce3ecef0..572f6248b2 100644 --- a/config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-mock-csrf-token-repository.xml +++ b/config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-mock-csrf-token-repository.xml @@ -21,8 +21,8 @@ http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-mock-request-matcher.xml b/config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-mock-request-matcher.xml index 28ac9677bb..6e2d96b0ae 100644 --- a/config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-mock-request-matcher.xml +++ b/config/src/test/resources/org/springframework/security/config/http/CsrfConfigTests-mock-request-matcher.xml @@ -21,7 +21,7 @@ http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/HttpConfigTests-AuthorizationManager.xml b/config/src/test/resources/org/springframework/security/config/http/HttpConfigTests-AuthorizationManager.xml index 0b606b3a8f..981d7b6a8c 100644 --- a/config/src/test/resources/org/springframework/security/config/http/HttpConfigTests-AuthorizationManager.xml +++ b/config/src/test/resources/org/springframework/security/config/http/HttpConfigTests-AuthorizationManager.xml @@ -29,8 +29,8 @@ - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/HttpConfigTests-WithObservationRegistry.xml b/config/src/test/resources/org/springframework/security/config/http/HttpConfigTests-WithObservationRegistry.xml index 0e3320619f..d1d1839f9b 100644 --- a/config/src/test/resources/org/springframework/security/config/http/HttpConfigTests-WithObservationRegistry.xml +++ b/config/src/test/resources/org/springframework/security/config/http/HttpConfigTests-WithObservationRegistry.xml @@ -29,7 +29,7 @@ - + diff --git a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CollidingFilters.xml b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CollidingFilters.xml index c46c8d47cf..e6a66cd5bf 100644 --- a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CollidingFilters.xml +++ b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CollidingFilters.xml @@ -29,7 +29,7 @@ - + diff --git a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomAuthenticationDetailsSourceRef.xml b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomAuthenticationDetailsSourceRef.xml index 97fa2662c5..2dfdfde842 100644 --- a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomAuthenticationDetailsSourceRef.xml +++ b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomAuthenticationDetailsSourceRef.xml @@ -32,7 +32,7 @@ - + diff --git a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomFilters.xml b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomFilters.xml index f547d25487..ec486f8422 100644 --- a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomFilters.xml +++ b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomFilters.xml @@ -34,7 +34,7 @@ - + diff --git a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomHttpBasicEntryPointRef.xml b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomHttpBasicEntryPointRef.xml index 8168a807e9..47a0a16e01 100644 --- a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomHttpBasicEntryPointRef.xml +++ b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-CustomHttpBasicEntryPointRef.xml @@ -30,7 +30,7 @@ - + diff --git a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-EntryPoint.xml b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-EntryPoint.xml index e9426cac31..d158b914bf 100644 --- a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-EntryPoint.xml +++ b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-EntryPoint.xml @@ -29,7 +29,7 @@ - + diff --git a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-ExplicitSaveAndExplicitRepository.xml b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-ExplicitSaveAndExplicitRepository.xml index 53513aa671..4406065ff6 100644 --- a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-ExplicitSaveAndExplicitRepository.xml +++ b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-ExplicitSaveAndExplicitRepository.xml @@ -30,7 +30,7 @@ - + diff --git a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-ExpressionHandler.xml b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-ExpressionHandler.xml index 7fd5edadb3..b7a8e6d1c0 100644 --- a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-ExpressionHandler.xml +++ b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-ExpressionHandler.xml @@ -35,7 +35,7 @@ - + diff --git a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-HttpFirewall.xml b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-HttpFirewall.xml index 79b520a0bd..6bd7635c71 100644 --- a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-HttpFirewall.xml +++ b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-HttpFirewall.xml @@ -32,7 +32,7 @@ - + diff --git a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-Jaas.xml b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-Jaas.xml index 0cd1a88f06..39f3597efc 100644 --- a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-Jaas.xml +++ b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-Jaas.xml @@ -48,7 +48,7 @@ - + diff --git a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-RequestCache.xml b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-RequestCache.xml index 47ed006b15..f7fb16b64e 100644 --- a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-RequestCache.xml +++ b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-RequestCache.xml @@ -30,7 +30,7 @@ - + diff --git a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-RequestRejectedHandler.xml b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-RequestRejectedHandler.xml index c6edc8d005..be62e9a47c 100644 --- a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-RequestRejectedHandler.xml +++ b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-RequestRejectedHandler.xml @@ -27,6 +27,6 @@ - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-Sec750.xml b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-Sec750.xml index ffd22cd312..fd91fffdab 100644 --- a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-Sec750.xml +++ b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-Sec750.xml @@ -38,7 +38,7 @@ - + diff --git a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-SecurityContextRepository.xml b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-SecurityContextRepository.xml index 0c38bb5943..859cddafd3 100644 --- a/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-SecurityContextRepository.xml +++ b/config/src/test/resources/org/springframework/security/config/http/MiscHttpConfigTests-SecurityContextRepository.xml @@ -30,7 +30,7 @@ - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests-clients.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests-clients.xml deleted file mode 100644 index 416520c6f7..0000000000 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests-clients.xml +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests-minimal.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests-minimal.xml deleted file mode 100644 index 6efa77199a..0000000000 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests-minimal.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests-providers.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests-providers.xml deleted file mode 100644 index 1966d46371..0000000000 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2AuthorizedClientManagerRegistrarTests-providers.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-AuthorizedClientArgumentResolver.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-AuthorizedClientArgumentResolver.xml index 5c9737e9bf..228bdf3b54 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-AuthorizedClientArgumentResolver.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-AuthorizedClientArgumentResolver.xml @@ -43,7 +43,9 @@ provider-id="google"/> - + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomAuthorizationRedirectStrategy.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomAuthorizationRedirectStrategy.xml index 43ffb46ea9..4eadcb94fe 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomAuthorizationRedirectStrategy.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomAuthorizationRedirectStrategy.xml @@ -33,8 +33,8 @@ - - + + - - + + - - + + - - + + - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomClientRegistrationRepository.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomClientRegistrationRepository.xml index d28b3b02d2..a3c5d3b091 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomClientRegistrationRepository.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomClientRegistrationRepository.xml @@ -30,8 +30,8 @@ - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomConfiguration.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomConfiguration.xml index 67ae60ee0c..dc15de679a 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomConfiguration.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2ClientBeanDefinitionParserTests-CustomConfiguration.xml @@ -45,17 +45,17 @@ - - + + - - + + - - + + - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-AuthorizedClientArgumentResolver.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-AuthorizedClientArgumentResolver.xml index d867ecfc46..dd0afc9351 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-AuthorizedClientArgumentResolver.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-AuthorizedClientArgumentResolver.xml @@ -34,7 +34,9 @@ - + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomConfiguration.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomConfiguration.xml index eb6c640004..0b4b8a4a20 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomConfiguration.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomConfiguration.xml @@ -33,20 +33,20 @@ - - + + - - + + - - + + - - + + - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomGrantedAuthorities.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomGrantedAuthorities.xml index ee4d504803..f0f7cecc8d 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomGrantedAuthorities.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomGrantedAuthorities.xml @@ -35,23 +35,23 @@ - - + + - - + + - - + + - - + + - - + + - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomLoginProcessingUrl.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomLoginProcessingUrl.xml index c55d276c2c..8d075bc6fd 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomLoginProcessingUrl.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-MultiClientRegistration-WithCustomLoginProcessingUrl.xml @@ -34,17 +34,17 @@ - - + + - - + + - - + + - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthenticationFailureHandler.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthenticationFailureHandler.xml index 098f71a46e..4b5e241d76 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthenticationFailureHandler.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthenticationFailureHandler.xml @@ -30,8 +30,8 @@ - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthorizationRedirectStrategy.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthorizationRedirectStrategy.xml index cd6620d65c..8454de28f1 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthorizationRedirectStrategy.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthorizationRedirectStrategy.xml @@ -30,8 +30,8 @@ - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthorizationRequestResolver.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthorizationRequestResolver.xml index 6952e6e5db..f40da7c40e 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthorizationRequestResolver.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithCustomAuthorizationRequestResolver.xml @@ -30,8 +30,8 @@ - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactory.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactory.xml index 7a537e6858..e695599875 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactory.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactory.xml @@ -34,20 +34,20 @@ - - + + - - + + - - + + - - + + - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactoryAndDefaultSuccessHandler.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactoryAndDefaultSuccessHandler.xml index bce09f2668..de5b7547ef 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactoryAndDefaultSuccessHandler.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration-WithJwtDecoderFactoryAndDefaultSuccessHandler.xml @@ -33,17 +33,17 @@ - - + + - - + + - - + + - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration.xml index bf2b126d88..09a9b67c2e 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-SingleClientRegistration.xml @@ -50,8 +50,8 @@ - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientRepository.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientRepository.xml index 4cd9409705..cef5a89a80 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientRepository.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientRepository.xml @@ -34,20 +34,20 @@ - - + + - - + + - - + + - - + + - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientService.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientService.xml index 5ab6a590e0..e0c5787219 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientService.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomAuthorizedClientService.xml @@ -34,20 +34,20 @@ - - + + - - + + - - + + - - + + - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomClientRegistrationRepository.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomClientRegistrationRepository.xml index 3f9133dcb9..d2f5947c4d 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomClientRegistrationRepository.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomClientRegistrationRepository.xml @@ -33,17 +33,17 @@ - - + + - - + + - - + + - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomSecurityContextHolderStrategy.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomSecurityContextHolderStrategy.xml index 1b2ea12ffa..6af68c55fa 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomSecurityContextHolderStrategy.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2LoginBeanDefinitionParserTests-WithCustomSecurityContextHolderStrategy.xml @@ -40,20 +40,20 @@ - - + + - - + + - - + + - - + + - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationManagerResolver.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationManagerResolver.xml index 384ecdca84..2facf68726 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationManagerResolver.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationManagerResolver.xml @@ -22,7 +22,7 @@ http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationManagerResolverPlusOtherConfig.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationManagerResolverPlusOtherConfig.xml index 69f70fb41c..dbf45c1bd5 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationManagerResolverPlusOtherConfig.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-AuthenticationManagerResolverPlusOtherConfig.xml @@ -22,10 +22,10 @@ http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - + - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-ExpiredJwtClockSkew.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-ExpiredJwtClockSkew.xml index a9b1b9c0be..c132c7b98e 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-ExpiredJwtClockSkew.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-ExpiredJwtClockSkew.xml @@ -31,7 +31,7 @@ - + - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-JwtRestOperations.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-JwtRestOperations.xml index d085cecd23..934e9809fb 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-JwtRestOperations.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-JwtRestOperations.xml @@ -22,7 +22,7 @@ http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - + - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwtAuthenticationConverter.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwtAuthenticationConverter.xml index 2e912b3f86..ab9785e7ea 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwtAuthenticationConverter.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwtAuthenticationConverter.xml @@ -22,6 +22,6 @@ http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwtDecoder.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwtDecoder.xml index 8cd8587fdc..341e1f1a6a 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwtDecoder.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwtDecoder.xml @@ -22,6 +22,6 @@ http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwtValidator.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwtValidator.xml index 14eeeac50c..764f5aaefe 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwtValidator.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-MockJwtValidator.xml @@ -22,11 +22,11 @@ http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - + - + - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenAndAuthenticationConverter.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenAndAuthenticationConverter.xml index b25d16ee9e..34efbd8f1f 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenAndAuthenticationConverter.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenAndAuthenticationConverter.xml @@ -21,8 +21,9 @@ xsi:schemaLocation="http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security.xsd http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenAndIntrospectionUri.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenAndIntrospectionUri.xml index 495847a5bc..21b470d7cb 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenAndIntrospectionUri.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenAndIntrospectionUri.xml @@ -22,7 +22,7 @@ http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - + diff --git a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenRestOperations.xml b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenRestOperations.xml index 3f6f3aa182..5b855415f7 100644 --- a/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenRestOperations.xml +++ b/config/src/test/resources/org/springframework/security/config/http/OAuth2ResourceServerBeanDefinitionParserTests-OpaqueTokenRestOperations.xml @@ -22,7 +22,7 @@ http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd"> - + - + - + - + diff --git a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-SingleRelyingPartyRegistration-WithCustomAuthenticationFailureHandler.xml b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-SingleRelyingPartyRegistration-WithCustomAuthenticationFailureHandler.xml index 81ae2a0930..fcd568996f 100644 --- a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-SingleRelyingPartyRegistration-WithCustomAuthenticationFailureHandler.xml +++ b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-SingleRelyingPartyRegistration-WithCustomAuthenticationFailureHandler.xml @@ -28,10 +28,10 @@ - + - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-SingleRelyingPartyRegistration.xml b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-SingleRelyingPartyRegistration.xml index a128f4c4e2..d694c40b12 100644 --- a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-SingleRelyingPartyRegistration.xml +++ b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-SingleRelyingPartyRegistration.xml @@ -36,7 +36,7 @@ assertion-consumer-service-location="{baseUrl}/login/saml2/sso/{registrationId}" assertion-consumer-service-binding="REDIRECT" asserting-party-id="google"/> - + - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomLoginProcessingUrl-WithCustomAuthenticationConverter.xml b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomLoginProcessingUrl-WithCustomAuthenticationConverter.xml index 47c98fb126..687f2700b0 100644 --- a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomLoginProcessingUrl-WithCustomAuthenticationConverter.xml +++ b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomLoginProcessingUrl-WithCustomAuthenticationConverter.xml @@ -28,11 +28,11 @@ - + - - - + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthenticationConverter.xml b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthenticationConverter.xml index 4783afa40c..2c8c8d620c 100644 --- a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthenticationConverter.xml +++ b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthenticationConverter.xml @@ -28,13 +28,13 @@ - + - - + + - - - + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthenticationManager.xml b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthenticationManager.xml index dd08cc3eff..af5de241c3 100644 --- a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthenticationManager.xml +++ b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthenticationManager.xml @@ -28,13 +28,13 @@ - + - - + + - - - + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthenticationRequestResolver.xml b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthenticationRequestResolver.xml index 8286c8fc8c..806a6fdb52 100644 --- a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthenticationRequestResolver.xml +++ b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthenticationRequestResolver.xml @@ -29,13 +29,13 @@ - + - - + + - - - + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthnRequestRepository.xml b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthnRequestRepository.xml index 0c0d5f8348..91e270febc 100644 --- a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthnRequestRepository.xml +++ b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository-WithCustomAuthnRequestRepository.xml @@ -28,13 +28,13 @@ - + - - + + - - - + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository.xml b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository.xml index 795faa7c11..5fc49103dc 100644 --- a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository.xml +++ b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomRelyingPartyRepository.xml @@ -28,16 +28,16 @@ - + - - + + - - + + - - - + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomSecurityContextHolderStrategy.xml b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomSecurityContextHolderStrategy.xml index 8f767c6762..b33a7da30e 100644 --- a/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomSecurityContextHolderStrategy.xml +++ b/config/src/test/resources/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserTests-WithCustomSecurityContextHolderStrategy.xml @@ -36,14 +36,14 @@ - - + + - - + + - - + + diff --git a/config/src/test/resources/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests-CsrfDisabled-MockLogoutSuccessHandler.xml b/config/src/test/resources/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests-CsrfDisabled-MockLogoutSuccessHandler.xml index 2150d9bc55..7caa59eb92 100644 --- a/config/src/test/resources/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests-CsrfDisabled-MockLogoutSuccessHandler.xml +++ b/config/src/test/resources/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests-CsrfDisabled-MockLogoutSuccessHandler.xml @@ -31,11 +31,11 @@ - + - - - + + + diff --git a/config/src/test/resources/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests-CustomComponents.xml b/config/src/test/resources/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests-CustomComponents.xml index 9ac53c5618..66068ed58d 100644 --- a/config/src/test/resources/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests-CustomComponents.xml +++ b/config/src/test/resources/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParserTests-CustomComponents.xml @@ -30,27 +30,27 @@ - + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + diff --git a/config/src/test/resources/org/springframework/security/config/method-security.xml b/config/src/test/resources/org/springframework/security/config/method-security.xml index 9551041262..2270ee030e 100644 --- a/config/src/test/resources/org/springframework/security/config/method-security.xml +++ b/config/src/test/resources/org/springframework/security/config/method-security.xml @@ -6,7 +6,7 @@ xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/tx https://www.springframework.org/schema/tx/spring-tx.xsd - http://www.springframework.org/schema/security org/springframework/security/config/spring-security-6.2.xsd"> + http://www.springframework.org/schema/security org/springframework/security/config/spring-security-6.1.xsd"> @@ -64,6 +64,6 @@ - - + + diff --git a/config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomAuthorizationManagerConfig.xml b/config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomAuthorizationManagerConfig.xml index 0e7453cc6b..5827be1b75 100644 --- a/config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomAuthorizationManagerConfig.xml +++ b/config/src/test/resources/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests-CustomAuthorizationManagerConfig.xml @@ -23,7 +23,7 @@ - + diff --git a/core/spring-security-core.gradle b/core/spring-security-core.gradle index 57040811c0..fcedeea906 100644 --- a/core/spring-security-core.gradle +++ b/core/spring-security-core.gradle @@ -27,6 +27,7 @@ dependencies { testImplementation "org.junit.jupiter:junit-jupiter-params" testImplementation "org.junit.jupiter:junit-jupiter-engine" testImplementation "org.mockito:mockito-core" + testImplementation 'org.mockito:mockito-inline' testImplementation "org.mockito:mockito-junit-jupiter" testImplementation "org.springframework:spring-test" testImplementation 'org.skyscreamer:jsonassert' diff --git a/core/src/main/java/org/springframework/security/access/SecurityConfig.java b/core/src/main/java/org/springframework/security/access/SecurityConfig.java index 3079174e52..4be11c2dfc 100644 --- a/core/src/main/java/org/springframework/security/access/SecurityConfig.java +++ b/core/src/main/java/org/springframework/security/access/SecurityConfig.java @@ -38,7 +38,8 @@ public class SecurityConfig implements ConfigAttribute { @Override public boolean equals(Object obj) { - if (obj instanceof ConfigAttribute attr) { + if (obj instanceof ConfigAttribute) { + ConfigAttribute attr = (ConfigAttribute) obj; return this.attrib.equals(attr.getAttribute()); } return false; diff --git a/core/src/main/java/org/springframework/security/access/annotation/Jsr250MethodSecurityMetadataSource.java b/core/src/main/java/org/springframework/security/access/annotation/Jsr250MethodSecurityMetadataSource.java index 27992654d4..45f65bb84c 100644 --- a/core/src/main/java/org/springframework/security/access/annotation/Jsr250MethodSecurityMetadataSource.java +++ b/core/src/main/java/org/springframework/security/access/annotation/Jsr250MethodSecurityMetadataSource.java @@ -89,7 +89,8 @@ public class Jsr250MethodSecurityMetadataSource extends AbstractFallbackMethodSe attributes.add(Jsr250SecurityConfig.PERMIT_ALL_ATTRIBUTE); return attributes; } - if (annotation instanceof RolesAllowed ra) { + if (annotation instanceof RolesAllowed) { + RolesAllowed ra = (RolesAllowed) annotation; for (String allowed : ra.value()) { String defaultedAllowed = getRoleWithDefaultPrefix(allowed); diff --git a/core/src/main/java/org/springframework/security/access/method/AbstractMethodSecurityMetadataSource.java b/core/src/main/java/org/springframework/security/access/method/AbstractMethodSecurityMetadataSource.java index c276ead8aa..219b3f7eb8 100644 --- a/core/src/main/java/org/springframework/security/access/method/AbstractMethodSecurityMetadataSource.java +++ b/core/src/main/java/org/springframework/security/access/method/AbstractMethodSecurityMetadataSource.java @@ -43,7 +43,8 @@ public abstract class AbstractMethodSecurityMetadataSource implements MethodSecu @Override public final Collection getAttributes(Object object) { - if (object instanceof MethodInvocation mi) { + if (object instanceof MethodInvocation) { + MethodInvocation mi = (MethodInvocation) object; Object target = mi.getThis(); Class targetClass = null; if (target != null) { diff --git a/core/src/main/java/org/springframework/security/access/method/MapBasedMethodSecurityMetadataSource.java b/core/src/main/java/org/springframework/security/access/method/MapBasedMethodSecurityMetadataSource.java index b7ebebb540..1c41d40d98 100644 --- a/core/src/main/java/org/springframework/security/access/method/MapBasedMethodSecurityMetadataSource.java +++ b/core/src/main/java/org/springframework/security/access/method/MapBasedMethodSecurityMetadataSource.java @@ -264,7 +264,8 @@ public class MapBasedMethodSecurityMetadataSource extends AbstractFallbackMethod if (this == obj) { return true; } - if (obj instanceof RegisteredMethod rhs) { + if (obj != null && obj instanceof RegisteredMethod) { + RegisteredMethod rhs = (RegisteredMethod) obj; return this.method.equals(rhs.method) && this.registeredJavaType.equals(rhs.registeredJavaType); } return false; diff --git a/core/src/main/java/org/springframework/security/access/prepost/PrePostAdviceReactiveMethodInterceptor.java b/core/src/main/java/org/springframework/security/access/prepost/PrePostAdviceReactiveMethodInterceptor.java index 66e2e02f26..bd47472566 100644 --- a/core/src/main/java/org/springframework/security/access/prepost/PrePostAdviceReactiveMethodInterceptor.java +++ b/core/src/main/java/org/springframework/security/access/prepost/PrePostAdviceReactiveMethodInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 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. @@ -19,6 +19,8 @@ package org.springframework.security.access.prepost; import java.lang.reflect.Method; import java.util.Collection; +import kotlin.coroutines.Continuation; +import kotlinx.coroutines.reactive.AwaitKt; import kotlinx.coroutines.reactive.ReactiveFlowKt; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; @@ -27,6 +29,7 @@ import reactor.core.Exceptions; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import org.springframework.core.CoroutinesUtils; import org.springframework.core.KotlinDetector; import org.springframework.core.MethodParameter; import org.springframework.core.ReactiveAdapter; @@ -123,23 +126,34 @@ public class PrePostAdviceReactiveMethodInterceptor implements MethodInterceptor .map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r)); } if (hasFlowReturnType) { + Flux response; if (isSuspendingFunction) { - return toInvoke - .flatMapMany((auth) -> Flux.from(PrePostAdviceReactiveMethodInterceptor.proceed(invocation)) - .map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r)); + response = toInvoke.flatMapMany((auth) -> Flux + .from(CoroutinesUtils.invokeSuspendingFunction(invocation.getMethod(), invocation.getThis(), + invocation.getArguments())) + .map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r)); } else { ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(returnType); Assert.state(adapter != null, () -> "The returnType " + returnType + " on " + method + " must have a org.springframework.core.ReactiveAdapter registered"); - Flux response = toInvoke.flatMapMany((auth) -> Flux + response = toInvoke.flatMapMany((auth) -> Flux .from(adapter.toPublisher(PrePostAdviceReactiveMethodInterceptor.flowProceed(invocation))) .map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r)); - return KotlinDelegate.asFlow(response); } + return KotlinDelegate.asFlow(response); } - return toInvoke.flatMap((auth) -> Mono.from(PrePostAdviceReactiveMethodInterceptor.proceed(invocation)) - .map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r)); + if (isSuspendingFunction) { + Mono response = toInvoke.flatMap((auth) -> Mono + .from(CoroutinesUtils.invokeSuspendingFunction(invocation.getMethod(), invocation.getThis(), + invocation.getArguments())) + .map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r)); + return KotlinDelegate.awaitSingleOrNull(response, + invocation.getArguments()[invocation.getArguments().length - 1]); + } + return toInvoke + .flatMapMany((auth) -> Flux.from(PrePostAdviceReactiveMethodInterceptor.>proceed(invocation)) + .map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r)); } private static > T proceed(final MethodInvocation invocation) { @@ -187,6 +201,10 @@ public class PrePostAdviceReactiveMethodInterceptor implements MethodInterceptor return ReactiveFlowKt.asFlow(publisher); } + private static Object awaitSingleOrNull(Publisher publisher, Object continuation) { + return AwaitKt.awaitSingleOrNull(publisher, (Continuation) continuation); + } + } } diff --git a/core/src/main/java/org/springframework/security/access/vote/ConsensusBased.java b/core/src/main/java/org/springframework/security/access/vote/ConsensusBased.java index b84cf64ffe..83f344d4ca 100644 --- a/core/src/main/java/org/springframework/security/access/vote/ConsensusBased.java +++ b/core/src/main/java/org/springframework/security/access/vote/ConsensusBased.java @@ -71,10 +71,14 @@ public class ConsensusBased extends AbstractAccessDecisionManager { for (AccessDecisionVoter voter : getDecisionVoters()) { int result = voter.vote(authentication, object, configAttributes); switch (result) { - case AccessDecisionVoter.ACCESS_GRANTED -> grant++; - case AccessDecisionVoter.ACCESS_DENIED -> deny++; - default -> { - } + case AccessDecisionVoter.ACCESS_GRANTED: + grant++; + break; + case AccessDecisionVoter.ACCESS_DENIED: + deny++; + break; + default: + break; } } if (grant > deny) { diff --git a/core/src/main/java/org/springframework/security/authentication/ProviderManager.java b/core/src/main/java/org/springframework/security/authentication/ProviderManager.java index a09283b08c..bc54e4c026 100644 --- a/core/src/main/java/org/springframework/security/authentication/ProviderManager.java +++ b/core/src/main/java/org/springframework/security/authentication/ProviderManager.java @@ -256,7 +256,8 @@ public class ProviderManager implements AuthenticationManager, MessageSourceAwar * @param dest the destination authentication object */ private void copyDetails(Authentication source, Authentication dest) { - if ((dest instanceof AbstractAuthenticationToken token) && (dest.getDetails() == null)) { + if ((dest instanceof AbstractAuthenticationToken) && (dest.getDetails() == null)) { + AbstractAuthenticationToken token = (AbstractAuthenticationToken) dest; token.setDetails(source.getDetails()); } } diff --git a/core/src/main/java/org/springframework/security/authentication/RememberMeAuthenticationToken.java b/core/src/main/java/org/springframework/security/authentication/RememberMeAuthenticationToken.java index 629372bc1a..8a62c9ca2a 100644 --- a/core/src/main/java/org/springframework/security/authentication/RememberMeAuthenticationToken.java +++ b/core/src/main/java/org/springframework/security/authentication/RememberMeAuthenticationToken.java @@ -94,8 +94,12 @@ public class RememberMeAuthenticationToken extends AbstractAuthenticationToken { if (!super.equals(obj)) { return false; } - if (obj instanceof RememberMeAuthenticationToken other) { - return this.getKeyHash() == other.getKeyHash(); + if (obj instanceof RememberMeAuthenticationToken) { + RememberMeAuthenticationToken other = (RememberMeAuthenticationToken) obj; + if (this.getKeyHash() != other.getKeyHash()) { + return false; + } + return true; } return false; } diff --git a/core/src/main/java/org/springframework/security/authentication/TestingAuthenticationToken.java b/core/src/main/java/org/springframework/security/authentication/TestingAuthenticationToken.java index abfc6560f4..8162b45965 100644 --- a/core/src/main/java/org/springframework/security/authentication/TestingAuthenticationToken.java +++ b/core/src/main/java/org/springframework/security/authentication/TestingAuthenticationToken.java @@ -16,7 +16,6 @@ package org.springframework.security.authentication; -import java.util.Collection; import java.util.List; import org.springframework.security.core.GrantedAuthority; @@ -48,13 +47,7 @@ public class TestingAuthenticationToken extends AbstractAuthenticationToken { this(principal, credentials, AuthorityUtils.createAuthorityList(authorities)); } - public TestingAuthenticationToken(Object principal, Object credentials, - List authorities) { - this(principal, credentials, (Collection) authorities); - } - - public TestingAuthenticationToken(Object principal, Object credentials, - Collection authorities) { + public TestingAuthenticationToken(Object principal, Object credentials, List authorities) { super(authorities); this.principal = principal; this.credentials = credentials; diff --git a/core/src/main/java/org/springframework/security/authentication/jaas/AbstractJaasAuthenticationProvider.java b/core/src/main/java/org/springframework/security/authentication/jaas/AbstractJaasAuthenticationProvider.java index b886caf6f1..32e7ea70b7 100644 --- a/core/src/main/java/org/springframework/security/authentication/jaas/AbstractJaasAuthenticationProvider.java +++ b/core/src/main/java/org/springframework/security/authentication/jaas/AbstractJaasAuthenticationProvider.java @@ -160,9 +160,10 @@ public abstract class AbstractJaasAuthenticationProvider implements Authenticati */ @Override public Authentication authenticate(Authentication auth) throws AuthenticationException { - if (!(auth instanceof UsernamePasswordAuthenticationToken request)) { + if (!(auth instanceof UsernamePasswordAuthenticationToken)) { return null; } + UsernamePasswordAuthenticationToken request = (UsernamePasswordAuthenticationToken) auth; Set authorities; try { // Create the LoginContext object, and pass our InternallCallbackHandler @@ -232,7 +233,8 @@ public abstract class AbstractJaasAuthenticationProvider implements Authenticati } for (SecurityContext context : contexts) { Authentication auth = context.getAuthentication(); - if ((auth instanceof JaasAuthenticationToken token)) { + if ((auth != null) && (auth instanceof JaasAuthenticationToken)) { + JaasAuthenticationToken token = (JaasAuthenticationToken) auth; try { LoginContext loginContext = token.getLoginContext(); logout(token, loginContext); diff --git a/core/src/main/java/org/springframework/security/authorization/ObservationAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/ObservationAuthorizationManager.java index 943e46dce3..30392eafc3 100644 --- a/core/src/main/java/org/springframework/security/authorization/ObservationAuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/ObservationAuthorizationManager.java @@ -22,12 +22,8 @@ import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationConvention; import io.micrometer.observation.ObservationRegistry; -import org.springframework.context.MessageSource; -import org.springframework.context.MessageSourceAware; -import org.springframework.context.support.MessageSourceAccessor; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.Authentication; -import org.springframework.security.core.SpringSecurityMessageSource; import org.springframework.util.Assert; /** @@ -36,7 +32,7 @@ import org.springframework.util.Assert; * @author Josh Cummings * @since 6.0 */ -public final class ObservationAuthorizationManager implements AuthorizationManager, MessageSourceAware { +public final class ObservationAuthorizationManager implements AuthorizationManager { private final ObservationRegistry registry; @@ -44,8 +40,6 @@ public final class ObservationAuthorizationManager implements AuthorizationMa private ObservationConvention> convention = new AuthorizationObservationConvention(); - private MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); - public ObservationAuthorizationManager(ObservationRegistry registry, AuthorizationManager delegate) { this.registry = registry; this.delegate = delegate; @@ -63,8 +57,7 @@ public final class ObservationAuthorizationManager implements AuthorizationMa AuthorizationDecision decision = this.delegate.check(wrapped, object); context.setDecision(decision); if (decision != null && !decision.isGranted()) { - observation.error(new AccessDeniedException( - this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access Denied"))); + observation.error(new AccessDeniedException("Access Denied")); } return decision; } @@ -88,14 +81,4 @@ public final class ObservationAuthorizationManager implements AuthorizationMa this.convention = convention; } - /** - * Set the MessageSource that this object runs in. - * @param messageSource The message source to be used by this object - * @since 6.2 - */ - @Override - public void setMessageSource(final MessageSource messageSource) { - this.messages = new MessageSourceAccessor(messageSource); - } - } diff --git a/core/src/main/java/org/springframework/security/authorization/method/Jsr250AuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/method/Jsr250AuthorizationManager.java index beb318ed15..a93c0d336d 100644 --- a/core/src/main/java/org/springframework/security/authorization/method/Jsr250AuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/method/Jsr250AuthorizationManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 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. @@ -18,7 +18,6 @@ package org.springframework.security.authorization.method; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.function.Supplier; @@ -31,7 +30,7 @@ import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.support.AopUtils; import org.springframework.core.annotation.AnnotationConfigurationException; import org.springframework.lang.NonNull; -import org.springframework.security.authorization.AuthoritiesAuthorizationManager; +import org.springframework.security.authorization.AuthorityAuthorizationManager; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.core.Authentication; @@ -58,23 +57,8 @@ public final class Jsr250AuthorizationManager implements AuthorizationManager> authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager(); - private String rolePrefix = "ROLE_"; - /** - * Sets an {@link AuthorizationManager} that accepts a collection of authority - * strings. - * @param authoritiesAuthorizationManager the {@link AuthorizationManager} that - * accepts a collection of authority strings to use - * @since 6.2 - */ - public void setAuthoritiesAuthorizationManager( - AuthorizationManager> authoritiesAuthorizationManager) { - Assert.notNull(authoritiesAuthorizationManager, "authoritiesAuthorizationManager cannot be null"); - this.authoritiesAuthorizationManager = authoritiesAuthorizationManager; - } - /** * Sets the role prefix. Defaults to "ROLE_". * @param rolePrefix the role prefix to use @@ -111,9 +95,10 @@ public final class Jsr250AuthorizationManager implements AuthorizationManager new AuthorizationDecision(true); } - if (annotation instanceof RolesAllowed rolesAllowed) { - return (a, o) -> Jsr250AuthorizationManager.this.authoritiesAuthorizationManager.check(a, - getAllowedRolesWithPrefix(rolesAllowed)); + if (annotation instanceof RolesAllowed) { + RolesAllowed rolesAllowed = (RolesAllowed) annotation; + return AuthorityAuthorizationManager.hasAnyRole(Jsr250AuthorizationManager.this.rolePrefix, + rolesAllowed.value()); } return NULL_MANAGER; } @@ -160,14 +145,6 @@ public final class Jsr250AuthorizationManager implements AuthorizationManager getAllowedRolesWithPrefix(RolesAllowed rolesAllowed) { - Set roles = new HashSet<>(); - for (int i = 0; i < rolesAllowed.value().length; i++) { - roles.add(Jsr250AuthorizationManager.this.rolePrefix + rolesAllowed.value()[i]); - } - return roles; - } - } } diff --git a/core/src/main/java/org/springframework/security/core/ComparableVersion.java b/core/src/main/java/org/springframework/security/core/ComparableVersion.java index a7477a4a5b..58e278754b 100644 --- a/core/src/main/java/org/springframework/security/core/ComparableVersion.java +++ b/core/src/main/java/org/springframework/security/core/ComparableVersion.java @@ -130,13 +130,23 @@ class ComparableVersion implements Comparable { return (value == 0) ? 0 : 1; // 1.0 == 1, 1.1 > 1 } - return switch (item.getType()) { - case INT_ITEM -> Integer.compare(value, ((IntItem) item).value); - case LONG_ITEM, BIGINTEGER_ITEM -> -1; - case STRING_ITEM -> 1; // 1.1 > 1-sp - case LIST_ITEM -> 1; // 1.1 > 1-1 - default -> throw new IllegalStateException("invalid item: " + item.getClass()); - }; + switch (item.getType()) { + case INT_ITEM: + int itemValue = ((IntItem) item).value; + return (value < itemValue) ? -1 : ((value == itemValue) ? 0 : 1); + case LONG_ITEM: + case BIGINTEGER_ITEM: + return -1; + + case STRING_ITEM: + return 1; // 1.1 > 1-sp + + case LIST_ITEM: + return 1; // 1.1 > 1-1 + + default: + throw new IllegalStateException("invalid item: " + item.getClass()); + } } @Override @@ -194,14 +204,24 @@ class ComparableVersion implements Comparable { return (value == 0) ? 0 : 1; // 1.0 == 1, 1.1 > 1 } - return switch (item.getType()) { - case INT_ITEM -> 1; - case LONG_ITEM -> Long.compare(value, ((LongItem) item).value); - case BIGINTEGER_ITEM -> -1; - case STRING_ITEM -> 1; // 1.1 > 1-sp - case LIST_ITEM -> 1; // 1.1 > 1-1 - default -> throw new IllegalStateException("invalid item: " + item.getClass()); - }; + switch (item.getType()) { + case INT_ITEM: + return 1; + case LONG_ITEM: + long itemValue = ((LongItem) item).value; + return (value < itemValue) ? -1 : ((value == itemValue) ? 0 : 1); + case BIGINTEGER_ITEM: + return -1; + + case STRING_ITEM: + return 1; // 1.1 > 1-sp + + case LIST_ITEM: + return 1; // 1.1 > 1-1 + + default: + throw new IllegalStateException("invalid item: " + item.getClass()); + } } @Override @@ -258,13 +278,23 @@ class ComparableVersion implements Comparable { return BigInteger.ZERO.equals(value) ? 0 : 1; // 1.0 == 1, 1.1 > 1 } - return switch (item.getType()) { - case INT_ITEM, LONG_ITEM -> 1; - case BIGINTEGER_ITEM -> value.compareTo(((BigIntegerItem) item).value); - case STRING_ITEM -> 1; // 1.1 > 1-sp - case LIST_ITEM -> 1; // 1.1 > 1-1 - default -> throw new IllegalStateException("invalid item: " + item.getClass()); - }; + switch (item.getType()) { + case INT_ITEM: + case LONG_ITEM: + return 1; + + case BIGINTEGER_ITEM: + return value.compareTo(((BigIntegerItem) item).value); + + case STRING_ITEM: + return 1; // 1.1 > 1-sp + + case LIST_ITEM: + return 1; // 1.1 > 1-1 + + default: + throw new IllegalStateException("invalid item: " + item.getClass()); + } } @Override @@ -321,12 +351,18 @@ class ComparableVersion implements Comparable { StringItem(String value, boolean followedByDigit) { if (followedByDigit && value.length() == 1) { // a1 = alpha-1, b1 = beta-1, m1 = milestone-1 - value = switch (value.charAt(0)) { - case 'a' -> "alpha"; - case 'b' -> "beta"; - case 'm' -> "milestone"; - default -> value; - }; + switch (value.charAt(0)) { + case 'a': + value = "alpha"; + break; + case 'b': + value = "beta"; + break; + case 'm': + value = "milestone"; + break; + default: + } } this.value = ALIASES.getProperty(value, value); } @@ -366,13 +402,21 @@ class ComparableVersion implements Comparable { // 1-rc < 1, 1-ga > 1 return comparableQualifier(value).compareTo(RELEASE_VERSION_INDEX); } - return switch (item.getType()) { - case INT_ITEM, LONG_ITEM, BIGINTEGER_ITEM -> -1; // 1.any < 1.1 ? - case STRING_ITEM -> - comparableQualifier(value).compareTo(comparableQualifier(((StringItem) item).value)); - case LIST_ITEM -> -1; // 1.any < 1-1 - default -> throw new IllegalStateException("invalid item: " + item.getClass()); - }; + switch (item.getType()) { + case INT_ITEM: + case LONG_ITEM: + case BIGINTEGER_ITEM: + return -1; // 1.any < 1.1 ? + + case STRING_ITEM: + return comparableQualifier(value).compareTo(comparableQualifier(((StringItem) item).value)); + + case LIST_ITEM: + return -1; // 1.any < 1-1 + + default: + throw new IllegalStateException("invalid item: " + item.getClass()); + } } @Override @@ -440,12 +484,19 @@ class ComparableVersion implements Comparable { Item first = get(0); return first.compareTo(null); } - return switch (item.getType()) { - case INT_ITEM, LONG_ITEM, BIGINTEGER_ITEM -> -1; // 1-1 < 1.0.x - case STRING_ITEM -> 1; // 1-1 > 1-sp - case LIST_ITEM -> { + switch (item.getType()) { + case INT_ITEM: + case LONG_ITEM: + case BIGINTEGER_ITEM: + return -1; // 1-1 < 1.0.x + + case STRING_ITEM: + return 1; // 1-1 > 1-sp + + case LIST_ITEM: Iterator left = iterator(); Iterator right = ((ListItem) item).iterator(); + while (left.hasNext() || right.hasNext()) { Item l = left.hasNext() ? left.next() : null; Item r = right.hasNext() ? right.next() : null; @@ -454,13 +505,15 @@ class ComparableVersion implements Comparable { int result = l == null ? (r == null ? 0 : -1 * r.compareTo(l)) : l.compareTo(r); if (result != 0) { - yield result; + return result; } } - yield 0; - } - default -> throw new IllegalStateException("invalid item: " + item.getClass()); - }; + + return 0; + + default: + throw new IllegalStateException("invalid item: " + item.getClass()); + } } @Override diff --git a/core/src/main/java/org/springframework/security/core/SpringSecurityCoreVersion.java b/core/src/main/java/org/springframework/security/core/SpringSecurityCoreVersion.java index 3d62aee5ad..37b2dd70b9 100644 --- a/core/src/main/java/org/springframework/security/core/SpringSecurityCoreVersion.java +++ b/core/src/main/java/org/springframework/security/core/SpringSecurityCoreVersion.java @@ -43,7 +43,7 @@ public final class SpringSecurityCoreVersion { * N.B. Classes are not intended to be serializable between different versions. See * SEC-1709 for why we still need a serial version. */ - public static final long SERIAL_VERSION_UID = 620L; + public static final long SERIAL_VERSION_UID = 610L; static final String MIN_SPRING_VERSION = getSpringVersion(); diff --git a/core/src/main/java/org/springframework/security/core/context/SecurityContextHolderStrategy.java b/core/src/main/java/org/springframework/security/core/context/SecurityContextHolderStrategy.java index 6b23733667..aaf7def3c2 100644 --- a/core/src/main/java/org/springframework/security/core/context/SecurityContextHolderStrategy.java +++ b/core/src/main/java/org/springframework/security/core/context/SecurityContextHolderStrategy.java @@ -48,7 +48,7 @@ public interface SecurityContextHolderStrategy { * @since 5.8 */ default Supplier getDeferredContext() { - return this::getContext; + return () -> getContext(); } /** diff --git a/core/src/main/java/org/springframework/security/core/context/SecurityContextImpl.java b/core/src/main/java/org/springframework/security/core/context/SecurityContextImpl.java index c83bba8741..734373df57 100644 --- a/core/src/main/java/org/springframework/security/core/context/SecurityContextImpl.java +++ b/core/src/main/java/org/springframework/security/core/context/SecurityContextImpl.java @@ -42,7 +42,8 @@ public class SecurityContextImpl implements SecurityContext { @Override public boolean equals(Object obj) { - if (obj instanceof SecurityContextImpl other) { + if (obj instanceof SecurityContextImpl) { + SecurityContextImpl other = (SecurityContextImpl) obj; if ((this.getAuthentication() == null) && (other.getAuthentication() == null)) { return true; } diff --git a/core/src/main/java/org/springframework/security/core/parameters/DefaultSecurityParameterNameDiscoverer.java b/core/src/main/java/org/springframework/security/core/parameters/DefaultSecurityParameterNameDiscoverer.java index 8c2792eaa8..7a7b339917 100644 --- a/core/src/main/java/org/springframework/security/core/parameters/DefaultSecurityParameterNameDiscoverer.java +++ b/core/src/main/java/org/springframework/security/core/parameters/DefaultSecurityParameterNameDiscoverer.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.Set; import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.PrioritizedParameterNameDiscoverer; import org.springframework.util.Assert; @@ -35,6 +36,11 @@ import org.springframework.util.ClassUtils; *
  • Will use an instance of {@link AnnotationParameterNameDiscoverer} with {@link P} as * a valid annotation. If, Spring Data is on the classpath will also add Param annotation. *
  • + *
  • If Spring 4 is on the classpath, then DefaultParameterNameDiscoverer is added. This + * attempts to use JDK 8 information first and falls back to + * {@link LocalVariableTableParameterNameDiscoverer}.
  • + *
  • If Spring 4 is not on the classpath, then + * {@link LocalVariableTableParameterNameDiscoverer} is added directly.
  • * * * @author Rob Winch diff --git a/core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java b/core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java index a390931541..4aa6c2af75 100644 --- a/core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java +++ b/core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java @@ -100,11 +100,13 @@ public class SessionRegistryImpl implements SessionRegistry, ApplicationListener @Override public void onApplicationEvent(AbstractSessionEvent event) { - if (event instanceof SessionDestroyedEvent sessionDestroyedEvent) { + if (event instanceof SessionDestroyedEvent) { + SessionDestroyedEvent sessionDestroyedEvent = (SessionDestroyedEvent) event; String sessionId = sessionDestroyedEvent.getId(); removeSessionInformation(sessionId); } - else if (event instanceof SessionIdChangedEvent sessionIdChangedEvent) { + else if (event instanceof SessionIdChangedEvent) { + SessionIdChangedEvent sessionIdChangedEvent = (SessionIdChangedEvent) event; String oldSessionId = sessionIdChangedEvent.getOldSessionId(); if (this.sessionIds.containsKey(oldSessionId)) { Object principal = this.sessionIds.get(oldSessionId).getPrincipal(); diff --git a/core/src/main/java/org/springframework/security/core/token/DefaultToken.java b/core/src/main/java/org/springframework/security/core/token/DefaultToken.java index 852dcc52ed..aaa89ffb6d 100644 --- a/core/src/main/java/org/springframework/security/core/token/DefaultToken.java +++ b/core/src/main/java/org/springframework/security/core/token/DefaultToken.java @@ -59,7 +59,8 @@ public class DefaultToken implements Token { @Override public boolean equals(Object obj) { - if (obj instanceof DefaultToken rhs) { + if (obj != null && obj instanceof DefaultToken) { + DefaultToken rhs = (DefaultToken) obj; return this.key.equals(rhs.key) && this.keyCreationTime == rhs.keyCreationTime && this.extendedInformation.equals(rhs.extendedInformation); } @@ -70,7 +71,7 @@ public class DefaultToken implements Token { public int hashCode() { int code = 979; code = code * this.key.hashCode(); - code = code * Long.valueOf(this.keyCreationTime).hashCode(); + code = code * new Long(this.keyCreationTime).hashCode(); code = code * this.extendedInformation.hashCode(); return code; } diff --git a/core/src/main/java/org/springframework/security/core/token/KeyBasedPersistenceTokenService.java b/core/src/main/java/org/springframework/security/core/token/KeyBasedPersistenceTokenService.java index d2ba33c3b3..8ac12a5348 100644 --- a/core/src/main/java/org/springframework/security/core/token/KeyBasedPersistenceTokenService.java +++ b/core/src/main/java/org/springframework/security/core/token/KeyBasedPersistenceTokenService.java @@ -142,7 +142,7 @@ public class KeyBasedPersistenceTokenService implements TokenService, Initializi } private String computeServerSecretApplicableAt(long time) { - return this.serverSecret + ":" + Long.valueOf(time % this.serverInteger).intValue(); + return this.serverSecret + ":" + new Long(time % this.serverInteger).intValue(); } /** diff --git a/core/src/main/java/org/springframework/security/core/userdetails/User.java b/core/src/main/java/org/springframework/security/core/userdetails/User.java index 461e2b478d..2c518fd7d8 100644 --- a/core/src/main/java/org/springframework/security/core/userdetails/User.java +++ b/core/src/main/java/org/springframework/security/core/userdetails/User.java @@ -201,7 +201,7 @@ public class User implements UserDetails, CredentialsContainer { sb.append("Password=[PROTECTED], "); sb.append("Enabled=").append(this.enabled).append(", "); sb.append("AccountNonExpired=").append(this.accountNonExpired).append(", "); - sb.append("CredentialsNonExpired=").append(this.credentialsNonExpired).append(", "); + sb.append("credentialsNonExpired=").append(this.credentialsNonExpired).append(", "); sb.append("AccountNonLocked=").append(this.accountNonLocked).append(", "); sb.append("Granted Authorities=").append(this.authorities).append("]"); return sb.toString(); diff --git a/core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java b/core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java index 955355a4f9..abccf9ac50 100644 --- a/core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java +++ b/core/src/main/java/org/springframework/security/jackson2/SecurityJackson2Modules.java @@ -127,7 +127,7 @@ public final class SecurityJackson2Modules { Class securityModule = (Class) ClassUtils.forName(className, loader); if (securityModule != null) { logger.debug(LogMessage.format("Loaded module %s, now registering", className)); - return securityModule.getConstructor().newInstance(); + return securityModule.newInstance(); } } catch (Exception ex) { diff --git a/core/src/main/java/org/springframework/security/jackson2/UnmodifiableListDeserializer.java b/core/src/main/java/org/springframework/security/jackson2/UnmodifiableListDeserializer.java index 8dc8ef286a..fd86dadccc 100644 --- a/core/src/main/java/org/springframework/security/jackson2/UnmodifiableListDeserializer.java +++ b/core/src/main/java/org/springframework/security/jackson2/UnmodifiableListDeserializer.java @@ -44,7 +44,8 @@ class UnmodifiableListDeserializer extends JsonDeserializer { JsonNode node = mapper.readTree(jp); List result = new ArrayList<>(); if (node != null) { - if (node instanceof ArrayNode arrayNode) { + if (node instanceof ArrayNode) { + ArrayNode arrayNode = (ArrayNode) node; for (JsonNode elementNode : arrayNode) { result.add(mapper.readValue(elementNode.traverse(mapper), Object.class)); } diff --git a/core/src/main/java/org/springframework/security/jackson2/UnmodifiableSetDeserializer.java b/core/src/main/java/org/springframework/security/jackson2/UnmodifiableSetDeserializer.java index 74e19fa8bb..c26d6921b5 100644 --- a/core/src/main/java/org/springframework/security/jackson2/UnmodifiableSetDeserializer.java +++ b/core/src/main/java/org/springframework/security/jackson2/UnmodifiableSetDeserializer.java @@ -44,7 +44,8 @@ class UnmodifiableSetDeserializer extends JsonDeserializer { JsonNode node = mapper.readTree(jp); Set resultSet = new HashSet<>(); if (node != null) { - if (node instanceof ArrayNode arrayNode) { + if (node instanceof ArrayNode) { + ArrayNode arrayNode = (ArrayNode) node; for (JsonNode elementNode : arrayNode) { resultSet.add(mapper.readValue(elementNode.traverse(mapper), Object.class)); } diff --git a/core/src/main/java/org/springframework/security/util/MethodInvocationUtils.java b/core/src/main/java/org/springframework/security/util/MethodInvocationUtils.java index 9c407bfd40..26ade0d092 100644 --- a/core/src/main/java/org/springframework/security/util/MethodInvocationUtils.java +++ b/core/src/main/java/org/springframework/security/util/MethodInvocationUtils.java @@ -60,7 +60,8 @@ public final class MethodInvocationUtils { // Determine the type that declares the requested method, // taking into account proxies Class target = AopUtils.getTargetClass(object); - if (object instanceof Advised a) { + if (object instanceof Advised) { + Advised a = (Advised) object; if (!a.isProxyTargetClass()) { Class[] possibleInterfaces = a.getProxiedInterfaces(); for (Class possibleInterface : possibleInterfaces) { diff --git a/core/src/test/java/org/springframework/security/DelegatingSecurityContextTestUtils.java b/core/src/test/java/org/springframework/security/DelegatingSecurityContextTestUtils.java deleted file mode 100644 index 0c05cf588a..0000000000 --- a/core/src/test/java/org/springframework/security/DelegatingSecurityContextTestUtils.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2002-2023 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; - -import java.util.concurrent.Callable; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.Function; - -import org.springframework.scheduling.TaskScheduler; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; - -/** - * @author Steve Riesenberg - */ -public final class DelegatingSecurityContextTestUtils { - - private DelegatingSecurityContextTestUtils() { - } - - public static SecurityContext runAndReturn(ThreadFactory threadFactory, - Function factory, BiConsumer fn) throws Exception { - CountDownLatch countDownLatch = new CountDownLatch(1); - AtomicReference result = new AtomicReference<>(); - ScheduledExecutorService delegate = Executors.newSingleThreadScheduledExecutor(threadFactory); - try { - T executor = factory.apply(delegate); - Runnable task = () -> { - result.set(SecurityContextHolder.getContext()); - countDownLatch.countDown(); - }; - fn.accept(executor, task); - countDownLatch.await(); - - return result.get(); - } - finally { - delegate.shutdown(); - } - } - - public static SecurityContext runAndReturn(ThreadFactory threadFactory, - Function factory, BiFunction> fn) - throws Exception { - CountDownLatch countDownLatch = new CountDownLatch(1); - AtomicReference result = new AtomicReference<>(); - ScheduledExecutorService delegate = Executors.newSingleThreadScheduledExecutor(threadFactory); - try { - T taskScheduler = factory.apply(delegate); - Runnable task = () -> { - result.set(SecurityContextHolder.getContext()); - countDownLatch.countDown(); - }; - ScheduledFuture future = fn.apply(taskScheduler, task); - countDownLatch.await(); - future.cancel(false); - - return result.get(); - } - finally { - delegate.shutdown(); - } - } - - public static SecurityContext callAndReturn(ThreadFactory threadFactory, - Function factory, - BiFunction, Future> fn) throws Exception { - ScheduledExecutorService delegate = Executors.newSingleThreadScheduledExecutor(threadFactory); - try { - T executor = factory.apply(delegate); - Callable task = SecurityContextHolder::getContext; - return fn.apply(executor, task).get(); - } - finally { - delegate.shutdown(); - } - } - -} diff --git a/core/src/test/java/org/springframework/security/access/intercept/RunAsImplAuthenticationProviderTests.java b/core/src/test/java/org/springframework/security/access/intercept/RunAsImplAuthenticationProviderTests.java index 91a616415b..18e79ca1eb 100644 --- a/core/src/test/java/org/springframework/security/access/intercept/RunAsImplAuthenticationProviderTests.java +++ b/core/src/test/java/org/springframework/security/access/intercept/RunAsImplAuthenticationProviderTests.java @@ -16,6 +16,7 @@ package org.springframework.security.access.intercept; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.security.authentication.BadCredentialsException; @@ -49,7 +50,7 @@ public class RunAsImplAuthenticationProviderTests { RunAsImplAuthenticationProvider provider = new RunAsImplAuthenticationProvider(); provider.setKey("my_password"); Authentication result = provider.authenticate(token); - assertThat(result instanceof RunAsUserToken).as("Should have returned RunAsUserToken").isTrue(); + Assertions.assertTrue(result instanceof RunAsUserToken, "Should have returned RunAsUserToken"); RunAsUserToken resultCast = (RunAsUserToken) result; assertThat(resultCast.getKeyHash()).isEqualTo("my_password".hashCode()); } diff --git a/core/src/test/java/org/springframework/security/access/intercept/RunAsManagerImplTests.java b/core/src/test/java/org/springframework/security/access/intercept/RunAsManagerImplTests.java index 16dc6a5c77..cd877b09cd 100644 --- a/core/src/test/java/org/springframework/security/access/intercept/RunAsManagerImplTests.java +++ b/core/src/test/java/org/springframework/security/access/intercept/RunAsManagerImplTests.java @@ -66,9 +66,9 @@ public class RunAsManagerImplTests { assertThat(result.getPrincipal()).isEqualTo(inputToken.getPrincipal()); assertThat(result.getCredentials()).isEqualTo(inputToken.getCredentials()); Set authorities = AuthorityUtils.authorityListToSet(result.getAuthorities()); - assertThat(authorities).contains("FOOBAR_RUN_AS_SOMETHING"); - assertThat(authorities).contains("ONE"); - assertThat(authorities).contains("TWO"); + assertThat(authorities.contains("FOOBAR_RUN_AS_SOMETHING")).isTrue(); + assertThat(authorities.contains("ONE")).isTrue(); + assertThat(authorities.contains("TWO")).isTrue(); RunAsUserToken resultCast = (RunAsUserToken) result; assertThat(resultCast.getKeyHash()).isEqualTo("my_password".hashCode()); } @@ -87,9 +87,9 @@ public class RunAsManagerImplTests { assertThat(result.getPrincipal()).isEqualTo(inputToken.getPrincipal()); assertThat(result.getCredentials()).isEqualTo(inputToken.getCredentials()); Set authorities = AuthorityUtils.authorityListToSet(result.getAuthorities()); - assertThat(authorities).contains("ROLE_RUN_AS_SOMETHING"); - assertThat(authorities).contains("ROLE_ONE"); - assertThat(authorities).contains("ROLE_TWO"); + assertThat(authorities.contains("ROLE_RUN_AS_SOMETHING")).isTrue(); + assertThat(authorities.contains("ROLE_ONE")).isTrue(); + assertThat(authorities.contains("ROLE_TWO")).isTrue(); RunAsUserToken resultCast = (RunAsUserToken) result; assertThat(resultCast.getKeyHash()).isEqualTo("my_password".hashCode()); } diff --git a/core/src/test/java/org/springframework/security/access/method/DelegatingMethodSecurityMetadataSourceTests.java b/core/src/test/java/org/springframework/security/access/method/DelegatingMethodSecurityMetadataSourceTests.java index 3989732410..3d388d2f48 100644 --- a/core/src/test/java/org/springframework/security/access/method/DelegatingMethodSecurityMetadataSourceTests.java +++ b/core/src/test/java/org/springframework/security/access/method/DelegatingMethodSecurityMetadataSourceTests.java @@ -50,7 +50,7 @@ public class DelegatingMethodSecurityMetadataSourceTests { sources.add(delegate); this.mds = new DelegatingMethodSecurityMetadataSource(sources); assertThat(this.mds.getMethodSecurityMetadataSources()).isSameAs(sources); - assertThat(this.mds.getAllConfigAttributes()).isEmpty(); + assertThat(this.mds.getAllConfigAttributes().isEmpty()).isTrue(); MethodInvocation mi = new SimpleMethodInvocation(null, String.class.getMethod("toString")); assertThat(this.mds.getAttributes(mi)).isEqualTo(Collections.emptyList()); // Exercise the cached case @@ -68,7 +68,7 @@ public class DelegatingMethodSecurityMetadataSourceTests { sources.add(delegate); this.mds = new DelegatingMethodSecurityMetadataSource(sources); assertThat(this.mds.getMethodSecurityMetadataSources()).isSameAs(sources); - assertThat(this.mds.getAllConfigAttributes()).isEmpty(); + assertThat(this.mds.getAllConfigAttributes().isEmpty()).isTrue(); MethodInvocation mi = new SimpleMethodInvocation("", toString); assertThat(this.mds.getAttributes(mi)).isSameAs(attributes); // Exercise the cached case diff --git a/core/src/test/java/org/springframework/security/access/vote/DenyAgainVoter.java b/core/src/test/java/org/springframework/security/access/vote/DenyAgainVoter.java index 9fcfc7fb3a..4d59a0173b 100644 --- a/core/src/test/java/org/springframework/security/access/vote/DenyAgainVoter.java +++ b/core/src/test/java/org/springframework/security/access/vote/DenyAgainVoter.java @@ -17,6 +17,7 @@ package org.springframework.security.access.vote; import java.util.Collection; +import java.util.Iterator; import org.springframework.security.access.AccessDecisionVoter; import org.springframework.security.access.ConfigAttribute; @@ -46,7 +47,9 @@ public class DenyAgainVoter implements AccessDecisionVoter { @Override public int vote(Authentication authentication, Object object, Collection attributes) { - for (ConfigAttribute attribute : attributes) { + Iterator iter = attributes.iterator(); + while (iter.hasNext()) { + ConfigAttribute attribute = iter.next(); if (this.supports(attribute)) { return ACCESS_DENIED; } diff --git a/core/src/test/java/org/springframework/security/access/vote/DenyVoter.java b/core/src/test/java/org/springframework/security/access/vote/DenyVoter.java index 6408c9a570..b20964b020 100644 --- a/core/src/test/java/org/springframework/security/access/vote/DenyVoter.java +++ b/core/src/test/java/org/springframework/security/access/vote/DenyVoter.java @@ -17,6 +17,7 @@ package org.springframework.security.access.vote; import java.util.Collection; +import java.util.Iterator; import org.springframework.security.access.AccessDecisionVoter; import org.springframework.security.access.ConfigAttribute; @@ -48,7 +49,9 @@ public class DenyVoter implements AccessDecisionVoter { @Override public int vote(Authentication authentication, Object object, Collection attributes) { - for (ConfigAttribute attribute : attributes) { + Iterator iter = attributes.iterator(); + while (iter.hasNext()) { + ConfigAttribute attribute = iter.next(); if (this.supports(attribute)) { return ACCESS_DENIED; } diff --git a/core/src/test/java/org/springframework/security/authentication/TestAuthentication.java b/core/src/test/java/org/springframework/security/authentication/TestAuthentication.java index a0faebee71..cdeb4ba1d8 100644 --- a/core/src/test/java/org/springframework/security/authentication/TestAuthentication.java +++ b/core/src/test/java/org/springframework/security/authentication/TestAuthentication.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,23 +17,15 @@ package org.springframework.security.authentication; import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.PasswordEncodedUser; import org.springframework.security.core.userdetails.UserDetails; /** * @author Rob Winch - * @author Evgeniy Cheban * @since 5.0 */ public class TestAuthentication extends PasswordEncodedUser { - private static final Authentication ANONYMOUS = new AnonymousAuthenticationToken("key", "anonymous", - AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); - - private static final RememberMeAuthenticationToken REMEMBER_ME = new RememberMeAuthenticationToken("key", "user", - AuthorityUtils.createAuthorityList("ROLE_USER")); - public static Authentication authenticatedAdmin() { return autheticated(admin()); } @@ -46,12 +38,4 @@ public class TestAuthentication extends PasswordEncodedUser { return UsernamePasswordAuthenticationToken.authenticated(user, null, user.getAuthorities()); } - public static Authentication anonymousUser() { - return ANONYMOUS; - } - - public static Authentication rememberMeUser() { - return REMEMBER_ME; - } - } diff --git a/core/src/test/java/org/springframework/security/authentication/jaas/DefaultJaasAuthenticationProviderTests.java b/core/src/test/java/org/springframework/security/authentication/jaas/DefaultJaasAuthenticationProviderTests.java index 04cf7b85da..ea9bab5653 100644 --- a/core/src/test/java/org/springframework/security/authentication/jaas/DefaultJaasAuthenticationProviderTests.java +++ b/core/src/test/java/org/springframework/security/authentication/jaas/DefaultJaasAuthenticationProviderTests.java @@ -222,13 +222,16 @@ public class DefaultJaasAuthenticationProviderTests { public void javadocExample() { String resName = "/" + getClass().getName().replace('.', '/') + ".xml"; ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(resName); - try (context) { - context.registerShutdownHook(); + context.registerShutdownHook(); + try { this.provider = context.getBean(DefaultJaasAuthenticationProvider.class); Authentication auth = this.provider.authenticate(this.token); assertThat(auth.isAuthenticated()).isEqualTo(true); assertThat(auth.getPrincipal()).isEqualTo(this.token.getPrincipal()); } + finally { + context.close(); + } } private void verifyFailedLogin() { diff --git a/core/src/test/java/org/springframework/security/authentication/jaas/JaasAuthenticationProviderTests.java b/core/src/test/java/org/springframework/security/authentication/jaas/JaasAuthenticationProviderTests.java index 351612f4ce..57c751f3cb 100644 --- a/core/src/test/java/org/springframework/security/authentication/jaas/JaasAuthenticationProviderTests.java +++ b/core/src/test/java/org/springframework/security/authentication/jaas/JaasAuthenticationProviderTests.java @@ -174,7 +174,8 @@ public class JaasAuthenticationProviderTests { assertThat(set.contains("ROLE_TEST2")).withFailMessage("GrantedAuthorities should contain ROLE_TEST2").isTrue(); boolean foundit = false; for (GrantedAuthority a : list) { - if (a instanceof JaasGrantedAuthority grant) { + if (a instanceof JaasGrantedAuthority) { + JaasGrantedAuthority grant = (JaasGrantedAuthority) a; assertThat(grant.getPrincipal()).withFailMessage("Principal was null on JaasGrantedAuthority") .isNotNull(); foundit = true; diff --git a/core/src/test/java/org/springframework/security/authentication/jaas/TestCallbackHandler.java b/core/src/test/java/org/springframework/security/authentication/jaas/TestCallbackHandler.java index 0084c95c8d..645b684709 100644 --- a/core/src/test/java/org/springframework/security/authentication/jaas/TestCallbackHandler.java +++ b/core/src/test/java/org/springframework/security/authentication/jaas/TestCallbackHandler.java @@ -30,7 +30,8 @@ public class TestCallbackHandler implements JaasAuthenticationCallbackHandler { @Override public void handle(Callback callback, Authentication auth) { - if (callback instanceof TextInputCallback tic) { + if (callback instanceof TextInputCallback) { + TextInputCallback tic = (TextInputCallback) callback; tic.setText(auth.getPrincipal().toString()); } } diff --git a/core/src/test/java/org/springframework/security/authentication/jaas/memory/InMemoryConfigurationTests.java b/core/src/test/java/org/springframework/security/authentication/jaas/memory/InMemoryConfigurationTests.java index 7b7c6d0f35..0bb7b6896a 100644 --- a/core/src/test/java/org/springframework/security/authentication/jaas/memory/InMemoryConfigurationTests.java +++ b/core/src/test/java/org/springframework/security/authentication/jaas/memory/InMemoryConfigurationTests.java @@ -89,7 +89,7 @@ public class InMemoryConfigurationTests { public void mappedNonnullDefault() { InMemoryConfiguration configuration = new InMemoryConfiguration(this.mappedEntries, this.defaultEntries); assertThat(this.defaultEntries).isEqualTo(configuration.getAppConfigurationEntry("missing")); - assertThat(this.mappedEntries).containsEntry("name", configuration.getAppConfigurationEntry("name")); + assertThat(this.mappedEntries.get("name")).isEqualTo(configuration.getAppConfigurationEntry("name")); } @Test diff --git a/core/src/test/java/org/springframework/security/authorization/ObservationAuthorizationManagerTests.java b/core/src/test/java/org/springframework/security/authorization/ObservationAuthorizationManagerTests.java index 1921f7dc1a..5f77d81ab3 100644 --- a/core/src/test/java/org/springframework/security/authorization/ObservationAuthorizationManagerTests.java +++ b/core/src/test/java/org/springframework/security/authorization/ObservationAuthorizationManagerTests.java @@ -16,7 +16,6 @@ package org.springframework.security.authorization; -import java.util.Optional; import java.util.function.Supplier; import io.micrometer.observation.Observation; @@ -26,7 +25,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; -import org.springframework.context.MessageSource; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; @@ -34,7 +32,6 @@ import org.springframework.security.core.Authentication; 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.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -88,20 +85,14 @@ public class ObservationAuthorizationManagerTests { @Test void verifyWhenErrorsThenObserves() { - MessageSource source = mock(MessageSource.class); - this.tested.setMessageSource(source); given(this.handler.supportsContext(any())).willReturn(true); given(this.authorizationManager.check(any(), any())).willReturn(this.deny); - given(source.getMessage(eq("AbstractAccessDecisionManager.accessDenied"), any(), any(), any())) - .willReturn("accessDenied"); assertThatExceptionOfType(AccessDeniedException.class) .isThrownBy(() -> this.tested.verify(this.token, this.object)); ArgumentCaptor captor = ArgumentCaptor.forClass(Observation.Context.class); verify(this.handler).onStart(captor.capture()); assertThat(captor.getValue().getName()).isEqualTo(AuthorizationObservationConvention.OBSERVATION_NAME); assertThat(captor.getValue().getError()).isInstanceOf(AccessDeniedException.class); - assertThat(Optional.ofNullable(captor.getValue().getError()).map(Throwable::getMessage).orElse("")) - .isEqualTo("accessDenied"); assertThat(captor.getValue()).isInstanceOf(AuthorizationObservationContext.class); AuthorizationObservationContext context = (AuthorizationObservationContext) captor.getValue(); assertThat(context.getAuthentication()).isNull(); diff --git a/core/src/test/java/org/springframework/security/authorization/SpringAuthorizationEventPublisherTests.java b/core/src/test/java/org/springframework/security/authorization/SpringAuthorizationEventPublisherTests.java index 466e484c74..cc56d8477e 100644 --- a/core/src/test/java/org/springframework/security/authorization/SpringAuthorizationEventPublisherTests.java +++ b/core/src/test/java/org/springframework/security/authorization/SpringAuthorizationEventPublisherTests.java @@ -38,7 +38,7 @@ import static org.mockito.Mockito.verifyNoInteractions; */ public class SpringAuthorizationEventPublisherTests { - Supplier authentication = TestAuthentication::authenticatedUser; + Supplier authentication = () -> TestAuthentication.authenticatedUser(); ApplicationEventPublisher applicationEventPublisher; diff --git a/core/src/test/java/org/springframework/security/authorization/method/Jsr250AuthorizationManagerTests.java b/core/src/test/java/org/springframework/security/authorization/method/Jsr250AuthorizationManagerTests.java index 5a35b88489..017a56fe02 100644 --- a/core/src/test/java/org/springframework/security/authorization/method/Jsr250AuthorizationManagerTests.java +++ b/core/src/test/java/org/springframework/security/authorization/method/Jsr250AuthorizationManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 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. @@ -18,8 +18,6 @@ package org.springframework.security.authorization.method; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Collection; -import java.util.Set; import java.util.function.Supplier; import jakarta.annotation.security.DenyAll; @@ -32,14 +30,11 @@ import org.springframework.security.access.intercept.method.MockMethodInvocation import org.springframework.security.authentication.TestAuthentication; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authorization.AuthorizationDecision; -import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.core.Authentication; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; /** * Tests for {@link Jsr250AuthorizationManager}. @@ -68,27 +63,6 @@ public class Jsr250AuthorizationManagerTests { assertThat(manager).extracting("rolePrefix").isEqualTo("CUSTOM_"); } - @Test - public void setAuthoritiesAuthorizationManagerWhenNullThenException() { - Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); - assertThatIllegalArgumentException().isThrownBy(() -> manager.setAuthoritiesAuthorizationManager(null)) - .withMessage("authoritiesAuthorizationManager cannot be null"); - } - - @Test - public void setAuthoritiesAuthorizationManagerWhenNotNullThenVerifyUsage() throws Exception { - AuthorizationManager> authoritiesAuthorizationManager = mock(AuthorizationManager.class); - Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); - manager.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager); - MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(), - ClassLevelAnnotations.class, "rolesAllowedAdmin"); - Supplier authentication = () -> new TestingAuthenticationToken("user", "password", - "ROLE_ADMIN"); - AuthorizationDecision decision = manager.check(authentication, methodInvocation); - assertThat(decision).isNull(); - verify(authoritiesAuthorizationManager).check(authentication, Set.of("ROLE_ADMIN")); - } - @Test public void checkDoSomethingWhenNoJsr250AnnotationsThenNullDecision() throws Exception { MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, @@ -149,7 +123,7 @@ public class Jsr250AuthorizationManagerTests { } @Test - public void checkMultipleMethodAnnotationsWhenInvokedThenAnnotationConfigurationException() throws Exception { + public void checkMultipleAnnotationsWhenInvokedThenAnnotationConfigurationException() throws Exception { Supplier authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ANONYMOUS"); MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, @@ -159,16 +133,6 @@ public class Jsr250AuthorizationManagerTests { .isThrownBy(() -> manager.check(authentication, methodInvocation)); } - @Test - public void checkMultipleClassAnnotationsWhenInvokedThenAnnotationConfigurationException() throws Exception { - Supplier authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER"); - MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelIllegalAnnotations(), - ClassLevelIllegalAnnotations.class, "inheritedAnnotations"); - Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); - assertThatExceptionOfType(AnnotationConfigurationException.class) - .isThrownBy(() -> manager.check(authentication, methodInvocation)); - } - @Test public void checkRequiresAdminWhenClassAnnotationsThenMethodAnnotationsTakePrecedence() throws Exception { Supplier authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER"); @@ -283,15 +247,6 @@ public class Jsr250AuthorizationManagerTests { } - @MyIllegalRolesAllowed - public static class ClassLevelIllegalAnnotations { - - public void inheritedAnnotations() { - - } - - } - public interface InterfaceAnnotationsOne { @RolesAllowed("ADMIN") @@ -319,11 +274,4 @@ public class Jsr250AuthorizationManagerTests { } - @DenyAll - @RolesAllowed("USER") - @Retention(RetentionPolicy.RUNTIME) - public @interface MyIllegalRolesAllowed { - - } - } diff --git a/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutorIntegrationTests.java b/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutorIntegrationTests.java deleted file mode 100644 index 92098ed79b..0000000000 --- a/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutorIntegrationTests.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2020-2023 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.concurrent; - -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnJre; -import org.junit.jupiter.api.condition.JRE; - -import org.springframework.core.task.VirtualThreadTaskExecutor; -import org.springframework.security.DelegatingSecurityContextTestUtils; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Steve Riesenberg - */ -public class DelegatingSecurityContextExecutorIntegrationTests { - - @Test - public void executeWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void executeWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext executeAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.runAndReturn( - threadFactory, - this::createExecutor, - Executor::execute - ); - // @formatter:on - } - - private DelegatingSecurityContextExecutor createExecutor(ScheduledExecutorService delegate) { - return new DelegatingSecurityContextExecutor(delegate, securityContext()); - } - - private static SecurityContext securityContext() { - SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); - securityContext.setAuthentication(new TestingAuthenticationToken("user", null)); - - return securityContext; - } - -} diff --git a/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutorServiceIntegrationTests.java b/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutorServiceIntegrationTests.java deleted file mode 100644 index 55df678328..0000000000 --- a/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextExecutorServiceIntegrationTests.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2020-2023 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.concurrent; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnJre; -import org.junit.jupiter.api.condition.JRE; - -import org.springframework.core.task.VirtualThreadTaskExecutor; -import org.springframework.security.DelegatingSecurityContextTestUtils; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Steve Riesenberg - */ -public class DelegatingSecurityContextExecutorServiceIntegrationTests { - - @Test - public void executeWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void executeWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext executeAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.runAndReturn( - threadFactory, - this::createExecutor, - ExecutorService::execute - ); - // @formatter:on - } - - @Test - public void submitWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = submitAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void submitWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = submitAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext submitAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.callAndReturn( - threadFactory, - this::createExecutor, - ExecutorService::submit - ); - // @formatter:on - } - - private DelegatingSecurityContextExecutorService createExecutor(ScheduledExecutorService delegate) { - return new DelegatingSecurityContextExecutorService(delegate, securityContext()); - } - - private static SecurityContext securityContext() { - SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); - securityContext.setAuthentication(new TestingAuthenticationToken("user", null)); - - return securityContext; - } - -} diff --git a/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextScheduledExecutorServiceIntegrationTests.java b/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextScheduledExecutorServiceIntegrationTests.java deleted file mode 100644 index 0a6ddc2dd5..0000000000 --- a/core/src/test/java/org/springframework/security/concurrent/DelegatingSecurityContextScheduledExecutorServiceIntegrationTests.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2020-2023 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.concurrent; - -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnJre; -import org.junit.jupiter.api.condition.JRE; - -import org.springframework.core.task.VirtualThreadTaskExecutor; -import org.springframework.security.DelegatingSecurityContextTestUtils; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Steve Riesenberg - */ -public class DelegatingSecurityContextScheduledExecutorServiceIntegrationTests { - - @Test - public void executeWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void executeWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext executeAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.runAndReturn( - threadFactory, - this::createExecutor, - ScheduledExecutorService::execute - ); - // @formatter:on - } - - @Test - public void submitWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = submitAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void submitWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = submitAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext submitAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.callAndReturn( - threadFactory, - this::createExecutor, - ScheduledExecutorService::submit - ); - // @formatter:on - } - - @Test - public void scheduleWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = scheduleAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void scheduleWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = scheduleAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext scheduleAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.callAndReturn( - threadFactory, - this::createExecutor, - (executor, task) -> executor.schedule(task, 50, TimeUnit.MILLISECONDS) - ); - // @formatter:on - } - - private DelegatingSecurityContextScheduledExecutorService createExecutor(ScheduledExecutorService delegate) { - return new DelegatingSecurityContextScheduledExecutorService(delegate, securityContext()); - } - - private static SecurityContext securityContext() { - SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); - securityContext.setAuthentication(new TestingAuthenticationToken("user", null)); - - return securityContext; - } - -} diff --git a/core/src/test/java/org/springframework/security/core/JavaVersionTests.java b/core/src/test/java/org/springframework/security/core/JavaVersionTests.java index 3aab2520a6..de0621cf12 100644 --- a/core/src/test/java/org/springframework/security/core/JavaVersionTests.java +++ b/core/src/test/java/org/springframework/security/core/JavaVersionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 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. @@ -32,11 +32,11 @@ public class JavaVersionTests { private static final int JDK17_CLASS_VERSION = 61; @Test - public void authenticationWhenJdk17ThenCorrectJdkCompatibility() throws Exception { - assertClassVersion(Authentication.class, JDK17_CLASS_VERSION); + public void authenticationCorrectJdkCompatibility() throws Exception { + assertClassVersion(Authentication.class); } - private void assertClassVersion(Class clazz, int classVersion) throws Exception { + private void assertClassVersion(Class clazz) throws Exception { String classResourceName = clazz.getName().replaceAll("\\.", "/") + ".class"; try (InputStream input = Thread.currentThread() .getContextClassLoader() @@ -45,7 +45,7 @@ public class JavaVersionTests { data.readInt(); data.readShort(); // minor int major = data.readShort(); - assertThat(major).isEqualTo(classVersion); + assertThat(major).isEqualTo(JDK17_CLASS_VERSION); } } diff --git a/core/src/test/java/org/springframework/security/core/StaticFinalReflectionUtils.java b/core/src/test/java/org/springframework/security/core/StaticFinalReflectionUtils.java index 76e8b7eed4..1cff222680 100644 --- a/core/src/test/java/org/springframework/security/core/StaticFinalReflectionUtils.java +++ b/core/src/test/java/org/springframework/security/core/StaticFinalReflectionUtils.java @@ -68,7 +68,13 @@ final class StaticFinalReflectionUtils { field.set(null, newValue); } } - catch (SecurityException | IllegalAccessException | IllegalArgumentException ex) { + catch (SecurityException ex) { + throw new RuntimeException(ex); + } + catch (IllegalAccessException ex) { + throw new RuntimeException(ex); + } + catch (IllegalArgumentException ex) { throw new RuntimeException(ex); } } diff --git a/core/src/test/java/org/springframework/security/core/authority/AuthorityUtilsTests.java b/core/src/test/java/org/springframework/security/core/authority/AuthorityUtilsTests.java index 46afc94346..42ff62827a 100644 --- a/core/src/test/java/org/springframework/security/core/authority/AuthorityUtilsTests.java +++ b/core/src/test/java/org/springframework/security/core/authority/AuthorityUtilsTests.java @@ -37,11 +37,11 @@ public class AuthorityUtilsTests { List authorityArray = AuthorityUtils .commaSeparatedStringToAuthorityList(" ROLE_A, B, C, ROLE_D\n,\n E "); Set authorities = AuthorityUtils.authorityListToSet(authorityArray); - assertThat(authorities).contains("B"); - assertThat(authorities).contains("C"); - assertThat(authorities).contains("E"); - assertThat(authorities).contains("ROLE_A"); - assertThat(authorities).contains("ROLE_D"); + assertThat(authorities.contains("B")).isTrue(); + assertThat(authorities.contains("C")).isTrue(); + assertThat(authorities.contains("E")).isTrue(); + assertThat(authorities.contains("ROLE_A")).isTrue(); + assertThat(authorities.contains("ROLE_D")).isTrue(); } @Test diff --git a/core/src/test/java/org/springframework/security/core/authority/mapping/SimpleAuthoritiesMapperTests.java b/core/src/test/java/org/springframework/security/core/authority/mapping/SimpleAuthoritiesMapperTests.java index 4634c23af2..20dd15bd10 100644 --- a/core/src/test/java/org/springframework/security/core/authority/mapping/SimpleAuthoritiesMapperTests.java +++ b/core/src/test/java/org/springframework/security/core/authority/mapping/SimpleAuthoritiesMapperTests.java @@ -45,8 +45,8 @@ public class SimpleAuthoritiesMapperTests { SimpleAuthorityMapper mapper = new SimpleAuthorityMapper(); Set mapped = AuthorityUtils .authorityListToSet(mapper.mapAuthorities(AuthorityUtils.createAuthorityList("AaA", "ROLE_bbb"))); - assertThat(mapped).contains("ROLE_AaA"); - assertThat(mapped).contains("ROLE_bbb"); + assertThat(mapped.contains("ROLE_AaA")).isTrue(); + assertThat(mapped.contains("ROLE_bbb")).isTrue(); } @Test @@ -56,19 +56,19 @@ public class SimpleAuthoritiesMapperTests { List toMap = AuthorityUtils.createAuthorityList("AaA", "Bbb"); Set mapped = AuthorityUtils.authorityListToSet(mapper.mapAuthorities(toMap)); assertThat(mapped).hasSize(2); - assertThat(mapped).contains("AaA"); - assertThat(mapped).contains("Bbb"); + assertThat(mapped.contains("AaA")).isTrue(); + assertThat(mapped.contains("Bbb")).isTrue(); mapper.setConvertToLowerCase(true); mapped = AuthorityUtils.authorityListToSet(mapper.mapAuthorities(toMap)); assertThat(mapped).hasSize(2); - assertThat(mapped).contains("aaa"); - assertThat(mapped).contains("bbb"); + assertThat(mapped.contains("aaa")).isTrue(); + assertThat(mapped.contains("bbb")).isTrue(); mapper.setConvertToLowerCase(false); mapper.setConvertToUpperCase(true); mapped = AuthorityUtils.authorityListToSet(mapper.mapAuthorities(toMap)); assertThat(mapped).hasSize(2); - assertThat(mapped).contains("AAA"); - assertThat(mapped).contains("BBB"); + assertThat(mapped.contains("AAA")).isTrue(); + assertThat(mapped.contains("BBB")).isTrue(); } @Test @@ -86,7 +86,7 @@ public class SimpleAuthoritiesMapperTests { mapper.setDefaultAuthority("ROLE_USER"); Set mapped = AuthorityUtils.authorityListToSet(mapper.mapAuthorities(AuthorityUtils.NO_AUTHORITIES)); assertThat(mapped).hasSize(1); - assertThat(mapped).contains("ROLE_USER"); + assertThat(mapped.contains("ROLE_USER")).isTrue(); } } diff --git a/core/src/test/java/org/springframework/security/core/context/ReactiveSecurityContextHolderTests.java b/core/src/test/java/org/springframework/security/core/context/ReactiveSecurityContextHolderTests.java index d5c7127dc1..af89ece4c9 100644 --- a/core/src/test/java/org/springframework/security/core/context/ReactiveSecurityContextHolderTests.java +++ b/core/src/test/java/org/springframework/security/core/context/ReactiveSecurityContextHolderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2017 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,17 +16,10 @@ package org.springframework.security.core.context; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; - import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnJre; -import org.junit.jupiter.api.condition.JRE; import reactor.core.publisher.Mono; -import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; -import org.springframework.core.task.VirtualThreadTaskExecutor; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; @@ -106,53 +99,4 @@ public class ReactiveSecurityContextHolderTests { // @formatter:on } - @Test - public void getContextWhenThreadFactoryIsPlatformThenPropagated() { - verifySecurityContextIsPropagated(Executors.defaultThreadFactory()); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void getContextWhenThreadFactoryIsVirtualThenPropagated() { - verifySecurityContextIsPropagated(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - } - - private static void verifySecurityContextIsPropagated(ThreadFactory threadFactory) { - Authentication authentication = new TestingAuthenticationToken("user", null); - - // @formatter:off - Mono publisher = ReactiveSecurityContextHolder.getContext() - .map(SecurityContext::getAuthentication) - .contextWrite((context) -> ReactiveSecurityContextHolder.withAuthentication(authentication)) - .subscribeOn(Schedulers.newSingle(threadFactory)); - // @formatter:on - - StepVerifier.create(publisher).expectNext(authentication).verifyComplete(); - } - - @Test - public void clearContextWhenThreadFactoryIsPlatformThenCleared() { - verifySecurityContextIsCleared(Executors.defaultThreadFactory()); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void clearContextWhenThreadFactoryIsVirtualThenCleared() { - verifySecurityContextIsCleared(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - } - - private static void verifySecurityContextIsCleared(ThreadFactory threadFactory) { - Authentication authentication = new TestingAuthenticationToken("user", null); - - // @formatter:off - Mono publisher = ReactiveSecurityContextHolder.getContext() - .map(SecurityContext::getAuthentication) - .contextWrite(ReactiveSecurityContextHolder.clearContext()) - .contextWrite((context) -> ReactiveSecurityContextHolder.withAuthentication(authentication)) - .subscribeOn(Schedulers.newSingle(threadFactory)); - // @formatter:on - - StepVerifier.create(publisher).verifyComplete(); - } - } diff --git a/core/src/test/java/org/springframework/security/core/context/ThreadLocalSecurityContextHolderStrategyTests.java b/core/src/test/java/org/springframework/security/core/context/ThreadLocalSecurityContextHolderStrategyTests.java index 0b3fbc35cb..9fc4b66401 100644 --- a/core/src/test/java/org/springframework/security/core/context/ThreadLocalSecurityContextHolderStrategyTests.java +++ b/core/src/test/java/org/springframework/security/core/context/ThreadLocalSecurityContextHolderStrategyTests.java @@ -57,7 +57,7 @@ class ThreadLocalSecurityContextHolderStrategyTests { void deferredContextValidates() { this.strategy.setDeferredContext(() -> null); Supplier deferredContext = this.strategy.getDeferredContext(); - assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(deferredContext::get); + assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> deferredContext.get()); } @Test diff --git a/core/src/test/java/org/springframework/security/core/parameters/DefaultSecurityParameterNameDiscovererTests.java b/core/src/test/java/org/springframework/security/core/parameters/DefaultSecurityParameterNameDiscovererTests.java index ebf3094b47..10e28b45b9 100644 --- a/core/src/test/java/org/springframework/security/core/parameters/DefaultSecurityParameterNameDiscovererTests.java +++ b/core/src/test/java/org/springframework/security/core/parameters/DefaultSecurityParameterNameDiscovererTests.java @@ -24,8 +24,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; -import org.springframework.core.StandardReflectionParameterNameDiscoverer; import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -60,11 +60,11 @@ public class DefaultSecurityParameterNameDiscovererTests { @Test public void constructorDiscoverers() { this.discoverer = new DefaultSecurityParameterNameDiscoverer( - Arrays.asList(new StandardReflectionParameterNameDiscoverer())); + Arrays.asList(new LocalVariableTableParameterNameDiscoverer())); List discoverers = (List) ReflectionTestUtils .getField(this.discoverer, "parameterNameDiscoverers"); assertThat(discoverers).hasSize(3); - assertThat(discoverers.get(0)).isInstanceOf(StandardReflectionParameterNameDiscoverer.class); + assertThat(discoverers.get(0)).isInstanceOf(LocalVariableTableParameterNameDiscoverer.class); ParameterNameDiscoverer annotationDisc = discoverers.get(1); assertThat(annotationDisc).isInstanceOf(AnnotationParameterNameDiscoverer.class); Set annotationsToUse = (Set) ReflectionTestUtils.getField(annotationDisc, diff --git a/core/src/test/java/org/springframework/security/core/session/SessionRegistryImplTests.java b/core/src/test/java/org/springframework/security/core/session/SessionRegistryImplTests.java index 9e5e2ecf47..ecd563b946 100644 --- a/core/src/test/java/org/springframework/security/core/session/SessionRegistryImplTests.java +++ b/core/src/test/java/org/springframework/security/core/session/SessionRegistryImplTests.java @@ -97,8 +97,8 @@ public class SessionRegistryImplTests { this.sessionRegistry.registerNewSession(sessionId2, principal1); this.sessionRegistry.registerNewSession(sessionId3, principal2); assertThat(this.sessionRegistry.getAllPrincipals()).hasSize(2); - assertThat(this.sessionRegistry.getAllPrincipals()).contains(principal1); - assertThat(this.sessionRegistry.getAllPrincipals()).contains(principal2); + assertThat(this.sessionRegistry.getAllPrincipals().contains(principal1)).isTrue(); + assertThat(this.sessionRegistry.getAllPrincipals().contains(principal2)).isTrue(); } @Test diff --git a/core/src/test/java/org/springframework/security/provisioning/JdbcUserDetailsManagerTests.java b/core/src/test/java/org/springframework/security/provisioning/JdbcUserDetailsManagerTests.java index 67d0c83fa5..38d835dcdf 100644 --- a/core/src/test/java/org/springframework/security/provisioning/JdbcUserDetailsManagerTests.java +++ b/core/src/test/java/org/springframework/security/provisioning/JdbcUserDetailsManagerTests.java @@ -145,7 +145,7 @@ public class JdbcUserDetailsManagerTests { AuthorityUtils.createAuthorityList("A", "B")); this.manager.createUser(user); UserDetails user2 = this.manager.loadUserByUsername(user.getUsername()); - assertThat(user2).usingRecursiveComparison().isEqualTo(user); + assertThat(user2).isEqualToComparingFieldByField(user); } @Test @@ -176,7 +176,7 @@ public class JdbcUserDetailsManagerTests { AuthorityUtils.createAuthorityList("D", "F", "E")); this.manager.updateUser(newJoe); UserDetails joe = this.manager.loadUserByUsername(newJoe.getUsername()); - assertThat(joe).usingRecursiveComparison().isEqualTo(newJoe); + assertThat(joe).isEqualToComparingFieldByField(newJoe); assertThat(this.cache.getUserMap().containsKey(newJoe.getUsername())).isFalse(); } @@ -189,7 +189,7 @@ public class JdbcUserDetailsManagerTests { public void userExistsReturnsTrueForExistingUsername() { insertJoe(); assertThat(this.manager.userExists("joe")).isTrue(); - assertThat(this.cache.getUserMap()).containsKey("joe"); + assertThat(this.cache.getUserMap().containsKey("joe")).isTrue(); } @Test @@ -251,7 +251,7 @@ public class JdbcUserDetailsManagerTests { UserDetails newJoe = this.manager.loadUserByUsername("joe"); assertThat(newJoe.getPassword()).isEqualTo("password"); assertThat(SecurityContextHolder.getContext().getAuthentication().getCredentials()).isEqualTo("password"); - assertThat(this.cache.getUserMap()).containsKey("joe"); + assertThat(this.cache.getUserMap().containsKey("joe")).isTrue(); } @Test diff --git a/core/src/test/java/org/springframework/security/scheduling/DelegatingSecurityContextSchedulingTaskExecutorIntegrationTests.java b/core/src/test/java/org/springframework/security/scheduling/DelegatingSecurityContextSchedulingTaskExecutorIntegrationTests.java deleted file mode 100644 index e77a7cffa5..0000000000 --- a/core/src/test/java/org/springframework/security/scheduling/DelegatingSecurityContextSchedulingTaskExecutorIntegrationTests.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2020-2023 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.scheduling; - -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnJre; -import org.junit.jupiter.api.condition.JRE; - -import org.springframework.core.task.VirtualThreadTaskExecutor; -import org.springframework.scheduling.SchedulingTaskExecutor; -import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor; -import org.springframework.security.DelegatingSecurityContextTestUtils; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Steve Riesenberg - */ -public class DelegatingSecurityContextSchedulingTaskExecutorIntegrationTests { - - @Test - public void executeWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void executeWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext executeAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.runAndReturn( - threadFactory, - this::createExecutor, - SchedulingTaskExecutor::execute - ); - // @formatter:on - } - - @Test - public void executeCompletableWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeCompletableAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void executeCompletableWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeCompletableAndReturn( - new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext executeCompletableAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.runAndReturn( - threadFactory, - this::createExecutor, - SchedulingTaskExecutor::submitCompletable - ); - // @formatter:on - } - - @Test - public void submitWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = submitAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void submitWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = submitAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext submitAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.callAndReturn( - threadFactory, - this::createExecutor, - SchedulingTaskExecutor::submit - ); - // @formatter:on - } - - @Test - public void submitCompletableWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = submitCompletableAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void submitCompletableWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = submitCompletableAndReturn( - new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext submitCompletableAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.callAndReturn( - threadFactory, - this::createExecutor, - SchedulingTaskExecutor::submitCompletable - ); - // @formatter:on - } - - private DelegatingSecurityContextSchedulingTaskExecutor createExecutor(ScheduledExecutorService delegate) { - return new DelegatingSecurityContextSchedulingTaskExecutor(new ConcurrentTaskExecutor(delegate), - securityContext()); - } - - private static SecurityContext securityContext() { - SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); - securityContext.setAuthentication(new TestingAuthenticationToken("user", null)); - - return securityContext; - } - -} diff --git a/core/src/test/java/org/springframework/security/scheduling/DelegatingSecurityContextTaskSchedulerIntegrationTests.java b/core/src/test/java/org/springframework/security/scheduling/DelegatingSecurityContextTaskSchedulerIntegrationTests.java deleted file mode 100644 index aff48a6d8a..0000000000 --- a/core/src/test/java/org/springframework/security/scheduling/DelegatingSecurityContextTaskSchedulerIntegrationTests.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2020-2023 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.scheduling; - -import java.time.Duration; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnJre; -import org.junit.jupiter.api.condition.JRE; - -import org.springframework.core.task.VirtualThreadTaskExecutor; -import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler; -import org.springframework.scheduling.support.PeriodicTrigger; -import org.springframework.security.DelegatingSecurityContextTestUtils; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Steve Riesenberg - */ -public class DelegatingSecurityContextTaskSchedulerIntegrationTests { - - @Test - public void scheduleWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = scheduleAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void scheduleWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = scheduleAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext scheduleAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.runAndReturn( - threadFactory, - this::createTaskScheduler, - (taskScheduler, task) -> taskScheduler.schedule(task, new PeriodicTrigger(Duration.ofMillis(50))) - ); - // @formatter:on - } - - @Test - public void scheduleAtFixedRateWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = scheduleAtFixedRateAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void scheduleAtFixedRateWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = scheduleAtFixedRateAndReturn( - new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext scheduleAtFixedRateAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.runAndReturn( - threadFactory, - this::createTaskScheduler, - (taskScheduler, task) -> taskScheduler.scheduleAtFixedRate(task, Duration.ofMillis(50)) - ); - // @formatter:on - } - - @Test - public void scheduleWithFixedDelayWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = scheduleWithFixedDelayAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void scheduleWithFixedDelayWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = scheduleWithFixedDelayAndReturn( - new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext scheduleWithFixedDelayAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.runAndReturn( - threadFactory, - this::createTaskScheduler, - (taskScheduler, task) -> taskScheduler.scheduleWithFixedDelay(task, Duration.ofMillis(50)) - ); - // @formatter:on - } - - private DelegatingSecurityContextTaskScheduler createTaskScheduler(ScheduledExecutorService delegate) { - return new DelegatingSecurityContextTaskScheduler(new ConcurrentTaskScheduler(delegate), securityContext()); - } - - private static SecurityContext securityContext() { - SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); - securityContext.setAuthentication(new TestingAuthenticationToken("user", null)); - - return securityContext; - } - -} diff --git a/core/src/test/java/org/springframework/security/task/DelegatingSecurityContextAsyncTaskExecutorIntegrationTests.java b/core/src/test/java/org/springframework/security/task/DelegatingSecurityContextAsyncTaskExecutorIntegrationTests.java deleted file mode 100644 index 1fd44da083..0000000000 --- a/core/src/test/java/org/springframework/security/task/DelegatingSecurityContextAsyncTaskExecutorIntegrationTests.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2020-2023 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.task; - -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnJre; -import org.junit.jupiter.api.condition.JRE; - -import org.springframework.core.task.AsyncTaskExecutor; -import org.springframework.core.task.VirtualThreadTaskExecutor; -import org.springframework.core.task.support.TaskExecutorAdapter; -import org.springframework.security.DelegatingSecurityContextTestUtils; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Steve Riesenberg - */ -public class DelegatingSecurityContextAsyncTaskExecutorIntegrationTests { - - @Test - public void executeWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void executeWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext executeAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.runAndReturn(threadFactory, - this::createExecutor, - AsyncTaskExecutor::execute - ); - // @formatter:on - } - - @Test - public void executeCompletableWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeCompletableAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void executeCompletableWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeCompletableAndReturn( - new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext executeCompletableAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.runAndReturn(threadFactory, - this::createExecutor, - AsyncTaskExecutor::submitCompletable - ); - // @formatter:on - } - - @Test - public void submitWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = submitAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void submitWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = submitAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext submitAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.callAndReturn(threadFactory, - this::createExecutor, - AsyncTaskExecutor::submit - ); - // @formatter:on - } - - @Test - public void submitCompletableWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = submitCompletableAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void submitCompletableWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = submitCompletableAndReturn( - new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext submitCompletableAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.callAndReturn(threadFactory, - this::createExecutor, - AsyncTaskExecutor::submitCompletable - ); - // @formatter:on - } - - private DelegatingSecurityContextAsyncTaskExecutor createExecutor(ScheduledExecutorService delegate) { - return new DelegatingSecurityContextAsyncTaskExecutor(new TaskExecutorAdapter(delegate), securityContext()); - } - - private static SecurityContext securityContext() { - SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); - securityContext.setAuthentication(new TestingAuthenticationToken("user", null)); - - return securityContext; - } - -} diff --git a/core/src/test/java/org/springframework/security/task/DelegatingSecurityContextTaskExecutorIntegrationTests.java b/core/src/test/java/org/springframework/security/task/DelegatingSecurityContextTaskExecutorIntegrationTests.java deleted file mode 100644 index 6d0f1a992c..0000000000 --- a/core/src/test/java/org/springframework/security/task/DelegatingSecurityContextTaskExecutorIntegrationTests.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2020-2023 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.task; - -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ThreadFactory; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnJre; -import org.junit.jupiter.api.condition.JRE; - -import org.springframework.core.task.TaskExecutor; -import org.springframework.core.task.VirtualThreadTaskExecutor; -import org.springframework.core.task.support.TaskExecutorAdapter; -import org.springframework.security.DelegatingSecurityContextTestUtils; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Steve Riesenberg - */ -public class DelegatingSecurityContextTaskExecutorIntegrationTests { - - @Test - public void executeWhenThreadFactoryIsPlatformThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeAndReturn(Executors.defaultThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void executeWhenThreadFactoryIsVirtualThenSecurityContextPropagated() throws Exception { - SecurityContext securityContext = executeAndReturn(new VirtualThreadTaskExecutor().getVirtualThreadFactory()); - assertThat(securityContext.getAuthentication()).isNotNull(); - } - - private SecurityContext executeAndReturn(ThreadFactory threadFactory) throws Exception { - // @formatter:off - return DelegatingSecurityContextTestUtils.runAndReturn(threadFactory, - this::createExecutor, - TaskExecutor::execute - ); - // @formatter:on - } - - private DelegatingSecurityContextTaskExecutor createExecutor(ScheduledExecutorService delegate) { - return new DelegatingSecurityContextTaskExecutor(new TaskExecutorAdapter(delegate), securityContext()); - } - - private static SecurityContext securityContext() { - SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); - securityContext.setAuthentication(new TestingAuthenticationToken("user", null)); - - return securityContext; - } - -} diff --git a/crypto/src/main/java/org/springframework/security/crypto/argon2/Argon2EncodingUtils.java b/crypto/src/main/java/org/springframework/security/crypto/argon2/Argon2EncodingUtils.java index 4b27d90318..883ab530f4 100644 --- a/crypto/src/main/java/org/springframework/security/crypto/argon2/Argon2EncodingUtils.java +++ b/crypto/src/main/java/org/springframework/security/crypto/argon2/Argon2EncodingUtils.java @@ -58,13 +58,19 @@ final class Argon2EncodingUtils { */ static String encode(byte[] hash, Argon2Parameters parameters) throws IllegalArgumentException { StringBuilder stringBuilder = new StringBuilder(); - String type = switch (parameters.getType()) { - case Argon2Parameters.ARGON2_d -> "$argon2d"; - case Argon2Parameters.ARGON2_i -> "$argon2i"; - case Argon2Parameters.ARGON2_id -> "$argon2id"; - default -> throw new IllegalArgumentException("Invalid algorithm type: " + parameters.getType()); - }; - stringBuilder.append(type); + switch (parameters.getType()) { + case Argon2Parameters.ARGON2_d: + stringBuilder.append("$argon2d"); + break; + case Argon2Parameters.ARGON2_i: + stringBuilder.append("$argon2i"); + break; + case Argon2Parameters.ARGON2_id: + stringBuilder.append("$argon2id"); + break; + default: + throw new IllegalArgumentException("Invalid algorithm type: " + parameters.getType()); + } stringBuilder.append("$v=") .append(parameters.getVersion()) .append("$m=") @@ -107,12 +113,19 @@ final class Argon2EncodingUtils { throw new IllegalArgumentException("Invalid encoded Argon2-hash"); } int currentPart = 1; - paramsBuilder = switch (parts[currentPart++]) { - case "argon2d" -> new Argon2Parameters.Builder(Argon2Parameters.ARGON2_d); - case "argon2i" -> new Argon2Parameters.Builder(Argon2Parameters.ARGON2_i); - case "argon2id" -> new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id); - default -> throw new IllegalArgumentException("Invalid algorithm type: " + parts[0]); - }; + switch (parts[currentPart++]) { + case "argon2d": + paramsBuilder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_d); + break; + case "argon2i": + paramsBuilder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_i); + break; + case "argon2id": + paramsBuilder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id); + break; + default: + throw new IllegalArgumentException("Invalid algorithm type: " + parts[0]); + } if (parts[currentPart].startsWith("v=")) { paramsBuilder.withVersion(Integer.parseInt(parts[currentPart].substring(2))); currentPart++; diff --git a/crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptTests.java b/crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptTests.java index 2774cb006d..f0d60b4cba 100644 --- a/crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptTests.java +++ b/crypto/src/test/java/org/springframework/security/crypto/bcrypt/BCryptTests.java @@ -390,7 +390,7 @@ public class BCryptTests { Arrays.fill(ba, (byte) 0); ba[i] = (byte) b; String s = encode_base64(ba, 3); - assertThat(s).hasSize(4); + assertThat(s.length()).isEqualTo(4); byte[] decoded = BCrypt.decode_base64(s, 3); assertThat(decoded).isEqualTo(ba); } diff --git a/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorEquivalencyTests.java b/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorEquivalencyTests.java index a37c83092c..66cc64b30e 100644 --- a/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorEquivalencyTests.java +++ b/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorEquivalencyTests.java @@ -20,6 +20,7 @@ import java.security.SecureRandom; import java.util.Random; import java.util.UUID; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -28,8 +29,6 @@ import org.springframework.security.crypto.encrypt.AesBytesEncryptor.CipherAlgor import org.springframework.security.crypto.keygen.BytesKeyGenerator; import org.springframework.security.crypto.keygen.KeyGenerators; -import static org.assertj.core.api.Assertions.assertThat; - public class BouncyCastleAesBytesEncryptorEquivalencyTests { private byte[] testData; @@ -97,11 +96,11 @@ public class BouncyCastleAesBytesEncryptorEquivalencyTests { // and can decrypt back to the original input byte[] leftEncrypted = left.encrypt(this.testData); byte[] rightEncrypted = right.encrypt(this.testData); - assertThat(rightEncrypted).containsExactly(leftEncrypted); + Assertions.assertArrayEquals(leftEncrypted, rightEncrypted); byte[] leftDecrypted = left.decrypt(leftEncrypted); byte[] rightDecrypted = right.decrypt(rightEncrypted); - assertThat(leftDecrypted).containsExactly(this.testData); - assertThat(rightDecrypted).containsExactly(this.testData); + Assertions.assertArrayEquals(this.testData, leftDecrypted); + Assertions.assertArrayEquals(this.testData, rightDecrypted); } } @@ -115,8 +114,8 @@ public class BouncyCastleAesBytesEncryptorEquivalencyTests { byte[] rightEncrypted = right.encrypt(this.testData); byte[] leftDecrypted = left.decrypt(rightEncrypted); byte[] rightDecrypted = right.decrypt(leftEncrypted); - assertThat(leftDecrypted).containsExactly(this.testData); - assertThat(rightDecrypted).containsExactly(this.testData); + Assertions.assertArrayEquals(this.testData, leftDecrypted); + Assertions.assertArrayEquals(this.testData, rightDecrypted); } } diff --git a/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorTests.java b/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorTests.java index 792c7c89df..2b5ec62348 100644 --- a/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorTests.java +++ b/crypto/src/test/java/org/springframework/security/crypto/encrypt/BouncyCastleAesBytesEncryptorTests.java @@ -20,13 +20,13 @@ import java.security.SecureRandom; import java.util.UUID; import org.bouncycastle.util.Arrays; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.security.crypto.codec.Hex; import org.springframework.security.crypto.keygen.KeyGenerators; -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; public class BouncyCastleAesBytesEncryptorTests { @@ -64,11 +64,11 @@ public class BouncyCastleAesBytesEncryptorTests { private void generatesDifferentCipherTexts(BytesEncryptor bcEncryptor) { byte[] encrypted1 = bcEncryptor.encrypt(this.testData); byte[] encrypted2 = bcEncryptor.encrypt(this.testData); - assertThat(Arrays.areEqual(encrypted1, encrypted2)).isFalse(); + Assertions.assertFalse(Arrays.areEqual(encrypted1, encrypted2)); byte[] decrypted1 = bcEncryptor.decrypt(encrypted1); byte[] decrypted2 = bcEncryptor.decrypt(encrypted2); - assertThat(decrypted1).containsExactly(this.testData); - assertThat(decrypted2).containsExactly(this.testData); + Assertions.assertArrayEquals(this.testData, decrypted1); + Assertions.assertArrayEquals(this.testData, decrypted2); } @Test diff --git a/crypto/src/test/java/org/springframework/security/crypto/keygen/KeyGeneratorsTests.java b/crypto/src/test/java/org/springframework/security/crypto/keygen/KeyGeneratorsTests.java index dcf44e34ca..517a01fb04 100644 --- a/crypto/src/test/java/org/springframework/security/crypto/keygen/KeyGeneratorsTests.java +++ b/crypto/src/test/java/org/springframework/security/crypto/keygen/KeyGeneratorsTests.java @@ -60,7 +60,7 @@ public class KeyGeneratorsTests { public void string() { StringKeyGenerator keyGenerator = KeyGenerators.string(); String hexStringKey = keyGenerator.generateKey(); - assertThat(hexStringKey).hasSize(16); + assertThat(hexStringKey.length()).isEqualTo(16); assertThat(Hex.decode(hexStringKey)).hasSize(8); String hexStringKey2 = keyGenerator.generateKey(); assertThat(hexStringKey.equals(hexStringKey2)).isFalse(); diff --git a/crypto/src/test/java/org/springframework/security/crypto/password/Pbkdf2PasswordEncoderTests.java b/crypto/src/test/java/org/springframework/security/crypto/password/Pbkdf2PasswordEncoderTests.java index 5c62fff4cf..487d312e85 100644 --- a/crypto/src/test/java/org/springframework/security/crypto/password/Pbkdf2PasswordEncoderTests.java +++ b/crypto/src/test/java/org/springframework/security/crypto/password/Pbkdf2PasswordEncoderTests.java @@ -39,8 +39,8 @@ public class Pbkdf2PasswordEncoderTests { // encode output is an hex coded String so with 2 chars per encoding result byte // (ie. 1 char for 4 bits). // The encoding result size is : (saltLength * 8) bits + hashWith bits. - assertThat(this.encoder.encode("password")).hasSize((8 * 8 + 256) / 4); - assertThat(this.encoderSalt16.encode("password")).hasSize((16 * 8 + 256) / 4); + assertThat(this.encoder.encode("password").length()).isEqualTo((8 * 8 + 256) / 4); + assertThat(this.encoderSalt16.encode("password").length()).isEqualTo((16 * 8 + 256) / 4); } @Test diff --git a/dependencies/spring-security-dependencies.gradle b/dependencies/spring-security-dependencies.gradle index 434d805583..5a24cbae39 100644 --- a/dependencies/spring-security-dependencies.gradle +++ b/dependencies/spring-security-dependencies.gradle @@ -83,4 +83,3 @@ dependencies { api libs.org.apache.maven.maven.resolver.provider } } - diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index 07b5dd3bde..78d95b298d 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -4,9 +4,13 @@ * xref:whats-new.adoc[What's New] * xref:migration-7/index.adoc[Preparing for 7.0] ** xref:migration-7/configuration.adoc[Configuration] -** xref:migration-7/ldap.adoc[LDAP] -* xref:migration/index.adoc[Migrating to 6.2] -** xref:migration/authorization.adoc[Authorization Changes] +* xref:migration/index.adoc[Migrating to 6.0] +** xref:migration/servlet/index.adoc[Servlet Migrations] +*** xref:migration/servlet/session-management.adoc[Session Management] +*** xref:migration/servlet/exploits.adoc[Exploit Protection] +*** xref:migration/servlet/authentication.adoc[Authentication] +*** xref:migration/servlet/authorization.adoc[Authorization] +** xref:migration/reactive.adoc[Reactive Migrations] * xref:getting-spring-security.adoc[Getting Spring Security] * xref:features/index.adoc[Features] ** xref:features/authentication/index.adoc[Authentication] diff --git a/docs/modules/ROOT/pages/features/exploits/http.adoc b/docs/modules/ROOT/pages/features/exploits/http.adoc index 1135d07aac..6f20b7f56b 100644 --- a/docs/modules/ROOT/pages/features/exploits/http.adoc +++ b/docs/modules/ROOT/pages/features/exploits/http.adoc @@ -20,13 +20,13 @@ Spring Security provides support for xref:features/exploits/headers.adoc#headers == Proxy Server Configuration When using a proxy server, it is important to ensure that you have configured your application properly. -For example, many applications have a load balancer that responds to request for https://example.com/ by forwarding the request to an application server at https://192.168.0.107 -Without proper configuration, the application server can not know that the load balancer exists and treats the request as though https://192.168.0.107:8080 was requested by the client. +For example, many applications have a load balancer that responds to request for https://example.com/ by forwarding the request to an application server at https://192.168.1:8080 +Without proper configuration, the application server can not know that the load balancer exists and treats the request as though https://192.168.1:8080 was requested by the client. To fix this, you can use https://tools.ietf.org/html/rfc7239[RFC 7239] to specify that a load balancer is being used. To make the application aware of this, you need to configure your application server to be aware of the X-Forwarded headers. For example, Tomcat uses https://tomcat.apache.org/tomcat-10.1-doc/api/org/apache/catalina/valves/RemoteIpValve.html[`RemoteIpValve`] and Jetty uses https://eclipse.dev/jetty/javadoc/jetty-11/org/eclipse/jetty/server/ForwardedRequestCustomizer.html[`ForwardedRequestCustomizer`]. -Alternatively, Spring users can use https://docs.spring.io/spring-framework/reference/web/webmvc/filters.html#filters-forwarded-headers[`ForwardedHeaderFilter`] with the Servlet stack or https://docs.spring.io/spring-framework/reference/web/webflux/reactive-spring.html#webflux-forwarded-headers[`ForwardedHeaderTransformer`] with the Reactive stack. +Alternatively, Spring users can use https://github.com/spring-projects/spring-framework/blob/v4.3.3.RELEASE/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java[`ForwardedHeaderFilter`]. -Spring Boot users can use the `server.forward-headers-strategy` property to configure the application. -See the https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto.webserver.use-behind-a-proxy-server[Spring Boot documentation] for further details. +Spring Boot users can use the `server.use-forward-headers` property to configure the application. +See the https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-use-tomcat-behind-a-proxy-server[Spring Boot documentation] for further details. diff --git a/docs/modules/ROOT/pages/migration-7/configuration.adoc b/docs/modules/ROOT/pages/migration-7/configuration.adoc index 42632bc37b..d6ca381fde 100644 --- a/docs/modules/ROOT/pages/migration-7/configuration.adoc +++ b/docs/modules/ROOT/pages/migration-7/configuration.adoc @@ -116,10 +116,3 @@ The Lambda DSL was created to accomplish to following goals: - Automatic indentation makes the configuration more readable. - The is no need to chain configuration options using `.and()` - The Spring Security DSL has a similar configuration style to other Spring DSLs such as Spring Integration and Spring Cloud Gateway. - -== Use `.with()` instead of `.apply()` for Custom DSLs - -In versions prior to 6.2, if you had a xref:servlet/configuration/java.adoc#jc-custom-dsls[custom DSL], you would apply it to the `HttpSecurity` using the `HttpSecurity#apply(...)` method. -However, starting from version 6.2, this method is deprecated and will be removed in 7.0 because it will no longer be possible to chain configurations using `.and()` once `.and()` is removed (see https://github.com/spring-projects/spring-security/issues/13067). -Instead, it is recommended to use the new `.with(...)` method. -For more information about how to use `.with(...)` please refer to the xref:servlet/configuration/java.adoc#jc-custom-dsls[Custom DSLs section]. diff --git a/docs/modules/ROOT/pages/migration-7/ldap.adoc b/docs/modules/ROOT/pages/migration-7/ldap.adoc deleted file mode 100644 index 3bef91f9ac..0000000000 --- a/docs/modules/ROOT/pages/migration-7/ldap.adoc +++ /dev/null @@ -1,11 +0,0 @@ -= LDAP Migrations - -The following steps relate to changes around how to configure the LDAP components and how to use an embedded LDAP server. - -== Use `UnboundId` instead of `ApacheDS` - -ApacheDS has not had a GA release for a considerable period, and its classes in Spring Security were https://github.com/spring-projects/spring-security/pull/6376[deprecated in version 5.2]. -Consequently, support for ApacheDS will be discontinued in version 7.0. - -If you are currently using ApacheDS as an embedded LDAP server, we recommend migrating to https://ldap.com/unboundid-ldap-sdk-for-java/[UnboundId]. -You can find instructions in xref:servlet/authentication/passwords/ldap.adoc#servlet-authentication-ldap-embedded[this section] that describe how to set up an embedded UnboundId LDAP server. diff --git a/docs/modules/ROOT/pages/migration/authorization.adoc b/docs/modules/ROOT/pages/migration/authorization.adoc deleted file mode 100644 index 031c8a8bfb..0000000000 --- a/docs/modules/ROOT/pages/migration/authorization.adoc +++ /dev/null @@ -1,24 +0,0 @@ -= Authorization Changes - -The following sections relate to how to adapt to changes in the authorization support. - -== Method Security - -[[compile-with-parameters]] -=== Compile With `-parameters` - -Spring Framework 6.1 https://github.com/spring-projects/spring-framework/issues/29559[removes LocalVariableTableParameterNameDiscoverer]. -This affects how `@PreAuthorize` and other xref:servlet/authorization/method-security.adoc[method security] annotations will process parameter names. -If you are using method security annotations with parameter names, for example: - -[source,java] -.Method security annotation using `id` parameter name ----- -@PreAuthorize("@authz.checkPermission(#id, authentication)") -public void doSomething(Long id) { - // ... -} ----- - -You must compile with `-parameters` to ensure that the parameter names are available at runtime. -For more information about this, please visit the https://github.com/spring-projects/spring-framework/wiki/Upgrading-to-Spring-Framework-6.x#core-container[Upgrading to Spring Framework 6.1 page]. diff --git a/docs/modules/ROOT/pages/migration/index.adoc b/docs/modules/ROOT/pages/migration/index.adoc index 3f7954e7d6..e52c9e2a38 100644 --- a/docs/modules/ROOT/pages/migration/index.adoc +++ b/docs/modules/ROOT/pages/migration/index.adoc @@ -1,23 +1,31 @@ [[migration]] -= Migrating to 6.2 += Migrating to 6.0 :spring-security-reference-base-url: https://docs.spring.io/spring-security/reference -This guide provides instructions for migrating from Spring Security 6.1 to Spring Security 6.2. +The Spring Security team has prepared the 5.8 release to simplify upgrading to Spring Security 6.0. +Use 5.8 and +ifdef::spring-security-version[] +{spring-security-reference-base-url}/5.8/migration/index.html[its preparation steps] +endif::[] +ifndef::spring-security-version[] +its preparation steps +endif::[] +to simplify updating to 6.0. -== Update to Spring Security 6.2 +After updating to 5.8, follow this guide to perform any remaining migration or cleanup steps. -When updating to a new minor version, it is important that you are already using the latest patch release of the previous minor version. -For example, if you are upgrading to Spring Security 6.2, you should already be using the latest patch release of Spring Security 6.1. -This makes it easier to identify any changes that may have been introduced in the new minor version. +And recall that if you run into trouble, the preparation guide includes opt-out steps to revert to 5.x behaviors. -Therefore, the first step is to ensure you are on the latest patch release of Spring Boot 3.1. -Next, you should ensure you are on the latest patch release of Spring Security 6.1. -Typically, the latest patch release of Spring Boot uses the latest patch release of Spring Security. +== Update to Spring Security 6.0 -With those two steps complete, you can now update to Spring Security 6.2. +The first step is to ensure you are the latest patch release of Spring Boot 3.0. +Next, you should ensure you are on the latest patch release of Spring Security 6.0. +For directions, on how to update to Spring Security 6.0 visit the xref:getting-spring-security.adoc[] section of the reference guide. -== Quick Reference +== Update Package Names -The following list provide a quick reference for the changes that are described in this guide. +Now that you are updated, you need to change your `javax` imports to `jakarta` imports. -- xref:migration/authorization.adoc#compile-with-parameters[You are using method parameter names in `@PreAuthorize`, `@PostAuthorize`, or any other method security annotations] +== Perform Application-Specific Steps + +Next, there are steps you need to perform based on whether it is a xref:migration/servlet/index.adoc[Servlet] or xref:migration/reactive.adoc[Reactive] application. diff --git a/docs/modules/ROOT/pages/migration/reactive.adoc b/docs/modules/ROOT/pages/migration/reactive.adoc new file mode 100644 index 0000000000..370e52262f --- /dev/null +++ b/docs/modules/ROOT/pages/migration/reactive.adoc @@ -0,0 +1,100 @@ += Reactive + +If you have already performed the xref:migration/index.adoc[initial migration steps] for your Reactive application, you're now ready to perform steps specific to Reactive applications. + +== Use `AuthorizationManager` for Method Security + +In 6.0, `@EnableReactiveMethodSecurity` defaults `useAuthorizationManager` to `true`. +So, to complete migration, {security-api-url}org/springframework/security/config/annotation/method/configuration/EnableReactiveMethodSecurity.html[`@EnableReactiveMethodSecurity`] remove the `useAuthorizationManager` attribute: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@EnableReactiveMethodSecurity(useAuthorizationManager = true) +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@EnableReactiveMethodSecurity(useAuthorizationManager = true) +---- +====== + +changes to: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@EnableReactiveMethodSecurity +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@EnableReactiveMethodSecurity +---- +====== + +== Propagate ``AuthenticationServiceException``s + +{security-api-url}org/springframework/security/web/server/authentication/AuthenticationWebFilter.html[`AuthenticationWebFilter`] propagates {security-api-url}org/springframework/security/authentication/AuthenticationServiceException.html[``AuthenticationServiceException``]s to the {security-api-url}org/springframework/security/web/server/ServerAuthenticationEntryPoint.html[`ServerAuthenticationEntryPoint`]. +Because ``AuthenticationServiceException``s represent a server-side error instead of a client-side error, in 6.0, this changes to propagate them to the container. + +So, if you opted into this behavior by setting `rethrowAuthenticationServiceException` too `true`, you can now remove it like so: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +AuthenticationFailureHandler bearerFailureHandler = new ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint); +bearerFailureHandler.setRethrowAuthenticationServiceException(true); +AuthenticationFailureHandler basicFailureHandler = new ServerAuthenticationEntryPointFailureHandler(basicEntryPoint); +basicFailureHandler.setRethrowAuthenticationServiceException(true); +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +val bearerFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint) +bearerFailureHandler.setRethrowAuthenticationServiceException(true) +val basicFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(basicEntryPoint) +basicFailureHandler.setRethrowAuthenticationServiceException(true) +---- +====== + +changes to: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +AuthenticationFailureHandler bearerFailureHandler = new ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint); +AuthenticationFailureHandler basicFailureHandler = new ServerAuthenticationEntryPointFailureHandler(basicEntryPoint); +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +val bearerFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(bearerEntryPoint) +val basicFailureHandler: AuthenticationFailureHandler = ServerAuthenticationEntryPointFailureHandler(basicEntryPoint) +---- +====== + +[NOTE] +==== +If you configured the `ServerAuthenticationFailureHandler` only for the purpose of updating to 6.0, you can remove it completely. +==== diff --git a/docs/modules/ROOT/pages/migration/servlet/authentication.adoc b/docs/modules/ROOT/pages/migration/servlet/authentication.adoc new file mode 100644 index 0000000000..3db94cf7a6 --- /dev/null +++ b/docs/modules/ROOT/pages/migration/servlet/authentication.adoc @@ -0,0 +1,187 @@ += Authentication Migrations + +The following steps relate to how to finish migrating authentication support. + +== Propagate ``AuthenticationServiceException``s + +{security-api-url}org/springframework/security/web/authentication/AuthenticationFilter.html[`AuthenticationFilter`] propagates {security-api-url}org/springframework/security/authentication/AuthenticationServiceException.html[``AuthenticationServiceException``]s to the {security-api-url}org/springframework/security/web/AuthenticationEntryPoint.html[`AuthenticationEntryPoint`]. +Because ``AuthenticationServiceException``s represent a server-side error instead of a client-side error, in 6.0, this changes to propagate them to the container. + +So, if you opted into this behavior by setting `rethrowAuthenticationServiceException` to `true`, you can now remove it like so: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +AuthenticationFilter authenticationFilter = new AuthenticationFilter(...); +AuthenticationEntryPointFailureHandler handler = new AuthenticationEntryPointFailureHandler(...); +handler.setRethrowAuthenticationServiceException(true); +authenticationFilter.setAuthenticationFailureHandler(handler); +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +val authenticationFilter: AuthenticationFilter = AuthenticationFilter(...) +val handler: AuthenticationEntryPointFailureHandler = AuthenticationEntryPointFailureHandler(...) +handler.setRethrowAuthenticationServiceException(true) +authenticationFilter.setAuthenticationFailureHandler(handler) +---- + +Xml:: ++ +[source,xml,role="secondary"] +---- + + + + + + + + +---- +====== + +changes to: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +AuthenticationFilter authenticationFilter = new AuthenticationFilter(...); +AuthenticationEntryPointFailureHandler handler = new AuthenticationEntryPointFailureHandler(...); +authenticationFilter.setAuthenticationFailureHandler(handler); +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +val authenticationFilter: AuthenticationFilter = AuthenticationFilter(...) +val handler: AuthenticationEntryPointFailureHandler = AuthenticationEntryPointFailureHandler(...) +authenticationFilter.setAuthenticationFailureHandler(handler) +---- + +Xml:: ++ +[source,xml,role="secondary"] +---- + + + + + + + + +---- +====== + +[[servlet-opt-in-sha256-rememberme]] +== Use SHA-256 in Remember Me + +In 6.0, the `TokenBasedRememberMeServices` uses SHA-256 to encode and match the token. +To complete the migration, any default values can be removed. + +For example, if you opted in to the 6.0 default for `encodingAlgorithm` and `matchingAlgorithm` like so: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Configuration +@EnableWebSecurity +public class SecurityConfig { + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception { + http + // ... + .rememberMe((remember) -> remember + .rememberMeServices(rememberMeServices) + ); + return http.build(); + } + @Bean + RememberMeServices rememberMeServices(UserDetailsService userDetailsService) { + RememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.SHA256; + TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(myKey, userDetailsService, encodingAlgorithm); + rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.SHA256); + return rememberMe; + } +} +---- + +XML:: ++ +[source,xml,role="secondary"] +---- + + + + + + + + + +---- +====== + +then the defaults can be removed: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Configuration +@EnableWebSecurity +public class SecurityConfig { + @Bean + SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception { + http + // ... + .rememberMe((remember) -> remember + .rememberMeServices(rememberMeServices) + ); + return http.build(); + } + @Bean + RememberMeServices rememberMeServices(UserDetailsService userDetailsService) { + return new TokenBasedRememberMeServices(myKey, userDetailsService); + } +} +---- + +XML:: ++ +[source,xml,role="secondary"] +---- + + + + + + + +---- +====== + +== Default authorities for oauth2Login() + +In Spring Security 5, the default `GrantedAuthority` given to a user that authenticates with an OAuth2 or OpenID Connect 1.0 provider (via `oauth2Login()`) is `ROLE_USER`. + +In Spring Security 6, the default authority given to a user authenticating with an OAuth2 provider is `OAUTH2_USER`. +The default authority given to a user authenticating with an OpenID Connect 1.0 provider is `OIDC_USER`. +If you configured the `GrantedAuthoritiesMapper` only for the purpose of updating to 6.0, you can remove it completely. diff --git a/docs/modules/ROOT/pages/migration/servlet/authorization.adoc b/docs/modules/ROOT/pages/migration/servlet/authorization.adoc new file mode 100644 index 0000000000..1063693b59 --- /dev/null +++ b/docs/modules/ROOT/pages/migration/servlet/authorization.adoc @@ -0,0 +1,117 @@ += Authorization Migrations + +The following steps relate to how to finish migrating authorization support. + +== Use `AuthorizationManager` for Method Security + +There are no further migration steps for this feature. + +== Use `AuthorizationManager` for Message Security + +In 6.0, `` defaults `use-authorization-manager` to `true`. +So, to complete migration, remove any `websocket-message-broker@use-authorization-manager=true` attribute. + +For example: + +[tabs] +====== +Xml:: ++ +[source,xml,role="primary"] +---- + +---- +====== + +changes to: + +[tabs] +====== +Xml:: ++ +[source,xml,role="primary"] +---- + +---- +====== + +There are no further migrations steps for Java or Kotlin for this feature. + +== Use `AuthorizationManager` for Request Security + +In 6.0, `` defaults `once-per-request` to `false`, `filter-all-dispatcher-types` to `true`, and `use-authorization-manager` to `true`. +Also, {security-api-url}org/springframework/security/config/annotation/web/configurers/AbstractInterceptUrlConfigurer.AbstractInterceptUrlRegistry.html#filterSecurityInterceptorOncePerRequest(boolean)[`authorizeRequests#filterSecurityInterceptorOncePerRequest`] defaults to `false` and xref:servlet/authorization/authorize-http-requests.adoc[`authorizeHttpRequests#filterAllDispatcherTypes`] defaults to `true`. +So, to complete migration, any defaults values can be removed. + +For example, if you opted in to the 6.0 default for `filter-all-dispatcher-types` or `authorizeHttpRequests#filterAllDispatcherTypes` like so: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +http + .authorizeHttpRequests((authorize) -> authorize + .filterAllDispatcherTypes(true) + // ... + ) +---- + +Kotlin:: ++ +[source,java,role="secondary"] +---- +http { + authorizeHttpRequests { + filterAllDispatcherTypes = true + // ... + } +} +---- + +Xml:: ++ +[source,xml,role="secondary"] +---- + +---- +====== + +then the defaults may be removed: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +http + .authorizeHttpRequests((authorize) -> authorize + // ... + ) +---- + +Kotlin:: ++ +[source,java,role="secondary"] +---- +http { + authorizeHttpRequests { + // ... + } +} +---- + +Xml:: ++ +[source,xml,role="secondary"] +---- + +---- +====== + +[NOTE] +==== +`once-per-request` applies only when `use-authorization-manager="false"` and `filter-all-dispatcher-types` only applies when `use-authorization-manager="true"` +==== diff --git a/docs/modules/ROOT/pages/migration/servlet/exploits.adoc b/docs/modules/ROOT/pages/migration/servlet/exploits.adoc new file mode 100644 index 0000000000..bddafec6c2 --- /dev/null +++ b/docs/modules/ROOT/pages/migration/servlet/exploits.adoc @@ -0,0 +1,44 @@ += Exploit Protection Migrations +:spring-security-reference-base-url: https://docs.spring.io/spring-security/reference + +The 5.8 migration guide contains several steps for +ifdef::spring-security-version[] +{spring-security-reference-base-url}/5.8/migration/servlet/exploits.html[exploit protection migrations] when updating to 6.0. +endif::[] +ifndef::spring-security-version[] +exploit protection migrations when updating to 6.0. +endif::[] +You are encouraged to follow those steps first. + +The following steps relate to how to finish migrating exploit protection support. + +== Defer Loading CsrfToken + +In Spring Security 5.8, the default `CsrfTokenRequestHandler` for making the `CsrfToken` available to the application is `CsrfTokenRequestAttributeHandler`. +The default for the field `csrfRequestAttributeName` is `null`, which causes the CSRF token to be loaded on every request. + +In Spring Security 6, `csrfRequestAttributeName` defaults to `_csrf`. +If you configured the following only for the purpose of updating to 6.0, you can now remove it: + + requestHandler.setCsrfRequestAttributeName("_csrf"); + +== Protect against CSRF BREACH + +In Spring Security 5.8, the default `CsrfTokenRequestHandler` for making the `CsrfToken` available to the application is `CsrfTokenRequestAttributeHandler`. +`XorCsrfTokenRequestAttributeHandler` was added to allow opting into CSRF BREACH support. + +In Spring Security 6, `XorCsrfTokenRequestAttributeHandler` is the default `CsrfTokenRequestHandler` for making the `CsrfToken` available. +If you configured the `XorCsrfTokenRequestAttributeHandler` only for the purpose of updating to 6.0, you can remove it completely. + +[NOTE] +==== +If you have set the `csrfRequestAttributeName` to `null` in order to opt out of deferred tokens, or if you have configured a `CsrfTokenRequestHandler` for any other reason, you can leave the configuration in place. +==== + +== CSRF BREACH with WebSocket support + +In Spring Security 5.8, the default `ChannelInterceptor` for making the `CsrfToken` available with xref:servlet/integrations/websocket.adoc[WebSocket Security] is `CsrfChannelInterceptor`. +`XorCsrfChannelInterceptor` was added to allow opting into CSRF BREACH support. + +In Spring Security 6, `XorCsrfChannelInterceptor` is the default `ChannelInterceptor` for making the `CsrfToken` available. +If you configured the `XorCsrfChannelInterceptor` only for the purpose of updating to 6.0, you can remove it completely. diff --git a/docs/modules/ROOT/pages/migration/servlet/index.adoc b/docs/modules/ROOT/pages/migration/servlet/index.adoc new file mode 100644 index 0000000000..adc75685a6 --- /dev/null +++ b/docs/modules/ROOT/pages/migration/servlet/index.adoc @@ -0,0 +1,4 @@ += Servlet Migrations +:page-section-summary-toc: 1 + +If you have already performed the xref:migration/index.adoc[initial migration steps] for your Servlet application, you're now ready to perform steps specific to Servlet applications. diff --git a/docs/modules/ROOT/pages/migration/servlet/session-management.adoc b/docs/modules/ROOT/pages/migration/servlet/session-management.adoc new file mode 100644 index 0000000000..c7409b9e07 --- /dev/null +++ b/docs/modules/ROOT/pages/migration/servlet/session-management.adoc @@ -0,0 +1,49 @@ += Session Management Migrations + +The following steps relate to how to finish migrating session management support. + +== Require Explicit Saving of SecurityContextRepository + +In Spring Security 5, the default behavior is for the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontext[`SecurityContext`] to automatically be saved to the xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] using the xref:servlet/authentication/persistence.adoc#securitycontextpersistencefilter[`SecurityContextPersistenceFilter`]. +Saving must be done just prior to the `HttpServletResponse` being committed and just before `SecurityContextPersistenceFilter`. +Unfortunately, automatic persistence of the `SecurityContext` can surprise users when it is done prior to the request completing (i.e. just prior to committing the `HttpServletResponse`). +It also is complex to keep track of the state to determine if a save is necessary causing unnecessary writes to the `SecurityContextRepository` (i.e. `HttpSession`) at times. + +In Spring Security 6, the default behavior is that the xref:servlet/authentication/persistence.adoc#securitycontextholderfilter[`SecurityContextHolderFilter`] will only read the `SecurityContext` from `SecurityContextRepository` and populate it in the `SecurityContextHolder`. +Users now must explicitly save the `SecurityContext` with the `SecurityContextRepository` if they want the `SecurityContext` to persist between requests. +This removes ambiguity and improves performance by only requiring writing to the `SecurityContextRepository` (i.e. `HttpSession`) when it is necessary. + +[NOTE] +==== +Saving the context is also needed when clearing it out, for example during logout. Refer to this section to xref:servlet/authentication/session-management.adoc#properly-clearing-authentication[know more about that]. +==== + +If you are explicitly opting into Spring Security 6's new defaults, the following configuration can be removed to accept the Spring Security 6 defaults. + + +include::partial$servlet/architecture/security-context-explicit.adoc[] + +== Multiple SecurityContextRepository + +In Spring Security 5, the default xref:servlet/authentication/persistence.adoc#securitycontextrepository[`SecurityContextRepository`] was `HttpSessionSecurityContextRepository`. + +In Spring Security 6, the default `SecurityContextRepository` is `DelegatingSecurityContextRepository`. +If you configured the `SecurityContextRepository` only for the purpose of updating to 6.0, you can remove it completely. + +== Deprecation in SecurityContextRepository + +There are no further migration steps for this deprecation. + +[[requestcache-query-optimization]] +== Optimize Querying of `RequestCache` + +In Spring Security 5, the default behavior is to query the xref:servlet/architecture.adoc#savedrequests[saved request] on every request. +This means that in a typical setup, that in order to use the xref:servlet/architecture.adoc#requestcache[`RequestCache`] the `HttpSession` is queried on every request. + +In Spring Security 6, the default is that `RequestCache` will only be queried for a cached request if the HTTP parameter `continue` is defined. +This allows Spring Security to avoid unnecessarily reading the `HttpSession` with the `RequestCache`. + +In Spring Security 5 the default is to use `HttpSessionRequestCache` which will be queried for a cached request on every request. +If you are not overriding the defaults (i.e. using `NullRequestCache`), then the following configuration can be used to explicitly opt into the Spring Security 6 behavior in Spring Security 5.8: + +include::partial$servlet/architecture/request-cache-continue.adoc[] diff --git a/docs/modules/ROOT/pages/reactive/oauth2/login/advanced.adoc b/docs/modules/ROOT/pages/reactive/oauth2/login/advanced.adoc index 319cba192b..0035fd2ec5 100644 --- a/docs/modules/ROOT/pages/reactive/oauth2/login/advanced.adoc +++ b/docs/modules/ROOT/pages/reactive/oauth2/login/advanced.adoc @@ -700,5 +700,111 @@ For MAC based algorithms such as `HS256`, `HS384` or `HS512`, the `client-secret [TIP] If more than one `ClientRegistration` is configured for OpenID Connect 1.0 Authentication, the JWS algorithm resolver may evaluate the provided `ClientRegistration` to determine which algorithm to return. + [[webflux-oauth2-login-advanced-oidc-logout]] -Then, you can proceed to configure xref:reactive/oauth2/login/logout.adoc[logout]. +== OpenID Connect 1.0 Logout + +OpenID Connect Session Management 1.0 allows the ability to log out the End-User at the Provider using the Client. +One of the strategies available is https://openid.net/specs/openid-connect-rpinitiated-1_0.html[RP-Initiated Logout]. + +If the OpenID Provider supports both Session Management and https://openid.net/specs/openid-connect-discovery-1_0.html[Discovery], the client may obtain the `end_session_endpoint` `URL` from the OpenID Provider's https://openid.net/specs/openid-connect-session-1_0.html#OPMetadata[Discovery Metadata]. +This can be achieved by configuring the `ClientRegistration` with the `issuer-uri`, as in the following example: + +[source,yaml] +---- +spring: + security: + oauth2: + client: + registration: + okta: + client-id: okta-client-id + client-secret: okta-client-secret + ... + provider: + okta: + issuer-uri: https://dev-1234.oktapreview.com +---- + +...and the `OidcClientInitiatedServerLogoutSuccessHandler`, which implements RP-Initiated Logout, may be configured as follows: + +[tabs] +====== +Java:: ++ +[source,java,role="primary",subs="-attributes"] +---- +@Configuration +@EnableWebFluxSecurity +public class OAuth2LoginSecurityConfig { + + @Autowired + private ReactiveClientRegistrationRepository clientRegistrationRepository; + + @Bean + public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { + http + .authorizeExchange(authorize -> authorize + .anyExchange().authenticated() + ) + .oauth2Login(withDefaults()) + .logout(logout -> logout + .logoutSuccessHandler(oidcLogoutSuccessHandler()) + ); + + return http.build(); + } + + private ServerLogoutSuccessHandler oidcLogoutSuccessHandler() { + OidcClientInitiatedServerLogoutSuccessHandler oidcLogoutSuccessHandler = + new OidcClientInitiatedServerLogoutSuccessHandler(this.clientRegistrationRepository); + + // Sets the location that the End-User's User Agent will be redirected to + // after the logout has been performed at the Provider + oidcLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}"); + + return oidcLogoutSuccessHandler; + } +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary",subs="-attributes"] +---- +@Configuration +@EnableWebFluxSecurity +class OAuth2LoginSecurityConfig { + + @Autowired + private lateinit var clientRegistrationRepository: ReactiveClientRegistrationRepository + + @Bean + fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { + http { + authorizeExchange { + authorize(anyExchange, authenticated) + } + oauth2Login { } + logout { + logoutSuccessHandler = oidcLogoutSuccessHandler() + } + } + + return http.build() + } + + private fun oidcLogoutSuccessHandler(): ServerLogoutSuccessHandler { + val oidcLogoutSuccessHandler = OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository) + + // Sets the location that the End-User's User Agent will be redirected to + // after the logout has been performed at the Provider + oidcLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}") + return oidcLogoutSuccessHandler + } +} +---- +====== + +NOTE: `OidcClientInitiatedServerLogoutSuccessHandler` supports the `+{baseUrl}+` placeholder. +If used, the application's base URL, like `https://app.example.org`, will replace it at request time. diff --git a/docs/modules/ROOT/pages/reactive/oauth2/login/logout.adoc b/docs/modules/ROOT/pages/reactive/oauth2/login/logout.adoc deleted file mode 100644 index ac1eb76028..0000000000 --- a/docs/modules/ROOT/pages/reactive/oauth2/login/logout.adoc +++ /dev/null @@ -1,267 +0,0 @@ -= OIDC Logout - -Once an end user is able to login to your application, it's important to consider how they will log out. - -Generally speaking, there are three use cases for you to consider: - -1. I want to perform only a local logout -2. I want to log out both my application and the OIDC Provider, initiated by my application -3. I want to log out both my application and the OIDC Provider, initiated by the OIDC Provider - -[[configure-local-logout]] -== Local Logout - -To perform a local logout, no special OIDC configuration is needed. -Spring Security automatically stands up a local logout endpoint, which you can xref:reactive/authentication/logout.adoc[configure through the `logout()` DSL]. - -[[configure-client-initiated-oidc-logout]] -[[oauth2login-advanced-oidc-logout]] -== OpenID Connect 1.0 Client-Initiated Logout - -OpenID Connect Session Management 1.0 allows the ability to log out the end user at the Provider by using the Client. -One of the strategies available is https://openid.net/specs/openid-connect-rpinitiated-1_0.html[RP-Initiated Logout]. - -If the OpenID Provider supports both Session Management and https://openid.net/specs/openid-connect-discovery-1_0.html[Discovery], the client can obtain the `end_session_endpoint` `URL` from the OpenID Provider's https://openid.net/specs/openid-connect-session-1_0.html#OPMetadata[Discovery Metadata]. -You can do so by configuring the `ClientRegistration` with the `issuer-uri`, as follows: - -[source,yaml] ----- -spring: - security: - oauth2: - client: - registration: - okta: - client-id: okta-client-id - client-secret: okta-client-secret - ... - provider: - okta: - issuer-uri: https://dev-1234.oktapreview.com ----- - -Also, you should configure `OidcClientInitiatedServerLogoutSuccessHandler`, which implements RP-Initiated Logout, as follows: - -[tabs] -====== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -@EnableWebFluxSecurity -public class OAuth2LoginSecurityConfig { - - @Autowired - private ReactiveClientRegistrationRepository clientRegistrationRepository; - - @Bean - public SecurityWebFilterChain filterChain(ServerHttpSecurity http) throws Exception { - http - .authorizeExchange((authorize) -> authorize - .anyExchange().authenticated() - ) - .oauth2Login(withDefaults()) - .logout((logout) -> logout - .logoutSuccessHandler(oidcLogoutSuccessHandler()) - ); - return http.build(); - } - - private ServerLogoutSuccessHandler oidcLogoutSuccessHandler() { - OidcClientInitiatedServerLogoutSuccessHandler oidcLogoutSuccessHandler = - new OidcClientInitiatedServerLogoutSuccessHandler(this.clientRegistrationRepository); - - // Sets the location that the End-User's User Agent will be redirected to - // after the logout has been performed at the Provider - oidcLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}"); - - return oidcLogoutSuccessHandler; - } -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -@Configuration -@EnableWebFluxSecurity -class OAuth2LoginSecurityConfig { - @Autowired - private lateinit var clientRegistrationRepository: ReactiveClientRegistrationRepository - - @Bean - open fun filterChain(http: ServerHttpSecurity): SecurityWebFilterChain { - http { - authorizeExchange { - authorize(anyExchange, authenticated) - } - oauth2Login { } - logout { - logoutSuccessHandler = oidcLogoutSuccessHandler() - } - } - return http.build() - } - - private fun oidcLogoutSuccessHandler(): ServerLogoutSuccessHandler { - val oidcLogoutSuccessHandler = OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository) - - // Sets the location that the End-User's User Agent will be redirected to - // after the logout has been performed at the Provider - oidcLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}") - return oidcLogoutSuccessHandler - } -} ----- -====== - -[NOTE] -==== -`OidcClientInitiatedServerLogoutSuccessHandler` supports the `+{baseUrl}+` placeholder. -If used, the application's base URL, such as `https://app.example.org`, replaces it at request time. -==== - -[[configure-provider-initiated-oidc-logout]] -== OpenID Connect 1.0 Back-Channel Logout - -OpenID Connect Session Management 1.0 allows the ability to log out the end user at the Client by having the Provider make an API call to the Client. -This is referred to as https://openid.net/specs/openid-connect-backchannel-1_0.html[OIDC Back-Channel Logout]. - -To enable this, you can stand up the Back-Channel Logout endpoint in the DSL like so: - -[tabs] -====== -Java:: -+ -[source=java,role="primary"] ----- -@Bean -public SecurityWebFilterChain filterChain(ServerHttpSecurity http) throws Exception { - http - .authorizeExchange((authorize) -> authorize - .anyExchange().authenticated() - ) - .oauth2Login(withDefaults()) - .oidcLogout((logout) -> logout - .backChannel(Customizer.withDefaults()) - ); - return http.build(); -} ----- - -Kotlin:: -+ -[source=kotlin,role="secondary"] ----- -@Bean -open fun filterChain(http: ServerHttpSecurity): SecurityWebFilterChain { - http { - authorizeExchange { - authorize(anyExchange, authenticated) - } - oauth2Login { } - oidcLogout { - backChannel { } - } - } - return http.build() -} ----- -====== - -And that's it! - -This will stand up the endpoint `+/logout/connect/back-channel/{registrationId}+` which the OIDC Provider can request to invalidate a given session of an end user in your application. - -[NOTE] -`oidcLogout` requires that `oauth2Login` also be configured. - -[NOTE] -`oidcLogout` requires that the session cookie be called `JSESSIONID` in order to correctly log out each session through a backchannel. - -=== Back-Channel Logout Architecture - -Consider a `ClientRegistration` whose identifier is `registrationId`. - -The overall flow for a Back-Channel logout is like this: - -1. At login time, Spring Security correlates the ID Token, CSRF Token, and Provider Session ID (if any) to your application's session id in its `ReactiveOidcSessionStrategy` implementation. -2. Then at logout time, your OIDC Provider makes an API call to `/logout/connect/back-channel/registrationId` including a Logout Token that indicates either the `sub` (the End User) or the `sid` (the Provider Session ID) to logout. -3. Spring Security validates the token's signature and claims. -4. If the token contains a `sid` claim, then only the Client's session that correlates to that provider session is terminated. -5. Otherwise, if the token contains a `sub` claim, then all that Client's sessions for that End User are terminated. - -[NOTE] -Remember that Spring Security's OIDC support is multi-tenant. -This means that it will only terminate sessions whose Client matches the `aud` claim in the Logout Token. - -=== Customizing the OIDC Provider Session Strategy - -By default, Spring Security stores in-memory all links between the OIDC Provider session and the Client session. - -There are a number of circumstances, like a clustered application, where it would be nice to store this instead in a separate location, like a database. - -You can achieve this by configuring a custom `ReactiveOidcSessionStrategy`, like so: - -[tabs] -====== -Java:: -+ -[source=java,role="primary"] ----- -@Component -public final class MySpringDataOidcSessionStrategy implements OidcSessionStrategy { - private final OidcProviderSessionRepository sessions; - - // ... - - @Override - public void saveSessionInformation(OidcSessionInformation info) { - this.sessions.save(info); - } - - @Override - public OidcSessionInformation(String clientSessionId) { - return this.sessions.removeByClientSessionId(clientSessionId); - } - - @Override - public Iterable removeSessionInformation(OidcLogoutToken token) { - return token.getSessionId() != null ? - this.sessions.removeBySessionIdAndIssuerAndAudience(...) : - this.sessions.removeBySubjectAndIssuerAndAudience(...); - } -} ----- - -Kotlin:: -+ -[source=kotlin,role="secondary"] ----- -@Component -class MySpringDataOidcSessionStrategy: ReactiveOidcSessionStrategy { - val sessions: OidcProviderSessionRepository - - // ... - - @Override - fun saveSessionInformation(info: OidcSessionInformation): Mono { - return this.sessions.save(info) - } - - @Override - fun removeSessionInformation(clientSessionId: String): Mono { - return this.sessions.removeByClientSessionId(clientSessionId); - } - - @Override - fun removeSessionInformation(token: OidcLogoutToken): Flux { - return token.getSessionId() != null ? - this.sessions.removeBySessionIdAndIssuerAndAudience(...) : - this.sessions.removeBySubjectAndIssuerAndAudience(...); - } -} ----- -====== diff --git a/docs/modules/ROOT/pages/reactive/oauth2/resource-server/jwt.adoc b/docs/modules/ROOT/pages/reactive/oauth2/resource-server/jwt.adoc index 519da49f77..8b704c2627 100644 --- a/docs/modules/ROOT/pages/reactive/oauth2/resource-server/jwt.adoc +++ b/docs/modules/ROOT/pages/reactive/oauth2/resource-server/jwt.adoc @@ -165,13 +165,11 @@ Java:: + [source,java,role="primary"] ---- -import static org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope; - @Bean SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { http .authorizeExchange(exchanges -> exchanges - .pathMatchers("/message/**").access(hasScope("message:read")) + .pathMatchers("/message/**").hasAuthority("SCOPE_message:read") .anyExchange().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 @@ -185,13 +183,11 @@ Kotlin:: + [source,kotlin,role="secondary"] ---- -import org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope - @Bean fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { return http { authorizeExchange { - authorize("/message/**", hasScope("message:read")) + authorize("/message/**", hasAuthority("SCOPE_message:read")) authorize(anyExchange, authenticated) } oauth2ResourceServer { @@ -686,14 +682,12 @@ Java:: + [source,java,role="primary"] ---- -import static org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope; - @Bean SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { http .authorizeExchange(exchanges -> exchanges - .mvcMatchers("/contacts/**").access(hasScope("contacts")) - .mvcMatchers("/messages/**").access(hasScope("messages")) + .mvcMatchers("/contacts/**").hasAuthority("SCOPE_contacts") + .mvcMatchers("/messages/**").hasAuthority("SCOPE_messages") .anyExchange().authenticated() ) .oauth2ResourceServer(OAuth2ResourceServerSpec::jwt); @@ -705,14 +699,12 @@ Kotlin:: + [source,kotlin,role="secondary"] ---- -import org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope - @Bean fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { return http { authorizeExchange { - authorize("/contacts/**", hasScope("contacts")) - authorize("/messages/**", hasScope("messages")) + authorize("/contacts/**", hasAuthority("SCOPE_contacts")) + authorize("/messages/**", hasAuthority("SCOPE_messages")) authorize(anyExchange, authenticated) } oauth2ResourceServer { diff --git a/docs/modules/ROOT/pages/reactive/oauth2/resource-server/multitenancy.adoc b/docs/modules/ROOT/pages/reactive/oauth2/resource-server/multitenancy.adoc index 7dd3c6b5a1..9e91587996 100644 --- a/docs/modules/ROOT/pages/reactive/oauth2/resource-server/multitenancy.adoc +++ b/docs/modules/ROOT/pages/reactive/oauth2/resource-server/multitenancy.adoc @@ -23,8 +23,8 @@ Java:: + [source,java,role="primary"] ---- -JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver - .fromTrustedIssuers("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo"); +JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver + ("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo"); http .authorizeExchange(exchanges -> exchanges @@ -39,8 +39,7 @@ Kotlin:: + [source,kotlin,role="secondary"] ---- -val customAuthenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver - .fromTrustedIssuers("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo") +val customAuthenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo") return http { authorizeExchange { diff --git a/docs/modules/ROOT/pages/reactive/oauth2/resource-server/opaque-token.adoc b/docs/modules/ROOT/pages/reactive/oauth2/resource-server/opaque-token.adoc index 8cab012346..bd622258ed 100644 --- a/docs/modules/ROOT/pages/reactive/oauth2/resource-server/opaque-token.adoc +++ b/docs/modules/ROOT/pages/reactive/oauth2/resource-server/opaque-token.adoc @@ -214,8 +214,6 @@ Java:: + [source,java,role="primary"] ---- -import static org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope; - @Configuration @EnableWebFluxSecurity public class MyCustomSecurityConfiguration { @@ -223,7 +221,7 @@ public class MyCustomSecurityConfiguration { SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { http .authorizeExchange(exchanges -> exchanges - .pathMatchers("/messages/**").access(hasScope("message:read")) + .pathMatchers("/messages/**").hasAuthority("SCOPE_message:read") .anyExchange().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 @@ -240,13 +238,11 @@ Kotlin:: + [source,kotlin,role="secondary"] ---- -import org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope - @Bean fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { return http { authorizeExchange { - authorize("/messages/**", hasScope("message:read")) + authorize("/messages/**", hasAuthority("SCOPE_message:read")) authorize(anyExchange, authenticated) } oauth2ResourceServer { @@ -446,8 +442,6 @@ Java:: + [source,java,role="primary"] ---- -import static org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope; - @Configuration @EnableWebFluxSecurity public class MappedAuthorities { @@ -455,8 +449,8 @@ public class MappedAuthorities { SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { http .authorizeExchange(exchange -> exchange - .pathMatchers("/contacts/**").access(hasScope("contacts")) - .pathMatchers("/messages/**").access(hasScope("messages")) + .pathMatchers("/contacts/**").hasAuthority("SCOPE_contacts") + .pathMatchers("/messages/**").hasAuthority("SCOPE_messages") .anyExchange().authenticated() ) .oauth2ResourceServer(ServerHttpSecurity.OAuth2ResourceServerSpec::opaqueToken); @@ -469,14 +463,12 @@ Kotlin:: + [source,kotlin,role="secondary"] ---- -import org.springframework.security.oauth2.core.authorization.OAuth2ReactiveAuthorizationManagers.hasScope - @Bean fun springSecurityFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain { return http { authorizeExchange { - authorize("/contacts/**", hasScope("contacts")) - authorize("/messages/**", hasScope("messages")) + authorize("/contacts/**", hasAuthority("SCOPE_contacts")) + authorize("/messages/**", hasAuthority("SCOPE_messages")) authorize(anyExchange, authenticated) } oauth2ResourceServer { diff --git a/docs/modules/ROOT/pages/servlet/appendix/namespace/index.adoc b/docs/modules/ROOT/pages/servlet/appendix/namespace/index.adoc index 4460fef52a..69fb747389 100644 --- a/docs/modules/ROOT/pages/servlet/appendix/namespace/index.adoc +++ b/docs/modules/ROOT/pages/servlet/appendix/namespace/index.adoc @@ -6,4 +6,4 @@ This appendix provides a reference to the elements available in the security nam If you haven't used the namespace before, please read the xref:servlet/configuration/xml-namespace.adoc#ns-config[introductory chapter] on namespace configuration, as this is intended as a supplement to the information there. Using a good quality XML editor while editing a configuration based on the schema is recommended as this will provide contextual information on which elements and attributes are available as well as comments explaining their purpose. The namespace is written in https://relaxng.org/[RELAX NG] Compact format and later converted into an XSD schema. -If you are familiar with this format, you may wish to examine the https://raw.githubusercontent.com/spring-projects/spring-security/main/config/src/main/resources/org/springframework/security/config/spring-security-6.2.rnc[schema file] directly. +If you are familiar with this format, you may wish to examine the https://raw.githubusercontent.com/spring-projects/spring-security/main/config/src/main/resources/org/springframework/security/config/spring-security-6.1.rnc[schema file] directly. diff --git a/docs/modules/ROOT/pages/servlet/authentication/logout.adoc b/docs/modules/ROOT/pages/servlet/authentication/logout.adoc index 91d669ce93..232f818226 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/logout.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/logout.adoc @@ -24,8 +24,6 @@ When you include {spring-boot-reference-url}using.html#using.build-systems.start If you request `GET /logout`, then Spring Security displays a logout confirmation page. Aside from providing a valuable double-checking mechanism for the user, it also provides a simple way to provide xref:servlet/exploits/csrf.adoc[the needed CSRF token] to `POST /logout`. -Please note that if xref:servlet/exploits/csrf.adoc[CSRF protection] is disabled in configuration, no logout confirmation page is shown to the user and the logout is performed directly. - [TIP] In your application it is not necessary to use `GET /logout` to perform a logout. So long as xref:servlet/exploits/csrf.adoc[the needed CSRF token] is present in the request, your application can simply `POST /logout` to induce a logout. @@ -82,7 +80,7 @@ Xml:: and no authorization changes are necessary since it simply adjusts the `LogoutFilter`. [[permit-logout-endpoints]] -However, if you stand up your own logout success endpoint (or in a rare case, <>), say using {spring-framework-reference-url}web.html#spring-web[Spring MVC], you will need to permit it in Spring Security. +However, if you stand up your own logout success endpoint (or in a rare case, <>), say using {spring-framework-reference-url}web.html#spring-web[Spring MVC], you will need permit it in Spring Security. This is because Spring MVC processes your request after Spring Security does. You can do this using `authorizeHttpRequests` or `` like so: diff --git a/docs/modules/ROOT/pages/servlet/authentication/session-management.adoc b/docs/modules/ROOT/pages/servlet/authentication/session-management.adoc index f9434d3826..d46b9ffeb9 100644 --- a/docs/modules/ROOT/pages/servlet/authentication/session-management.adoc +++ b/docs/modules/ROOT/pages/servlet/authentication/session-management.adoc @@ -80,7 +80,7 @@ If you try to use any of these methods, an exception will be thrown. By default, Spring Security stores the security context for you in the HTTP session. However, here are several reasons you may want to customize that: -* You may want to call individual setters on the `HttpSessionSecurityContextRepository` instance +* You may want call individual setters on the `HttpSessionSecurityContextRepository` instance * You may want to store the security context in a cache or database to enable horizontal scaling First, you need to create an implementation of `SecurityContextRepository` or use an existing implementation like `HttpSessionSecurityContextRepository`, then you can set it in `HttpSecurity`. diff --git a/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc b/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc index 7dac6eb233..244ff724b7 100644 --- a/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc +++ b/docs/modules/ROOT/pages/servlet/authorization/authorize-http-requests.adoc @@ -81,7 +81,7 @@ This means that Spring Security's xref:servlet/authentication/index.adoc[authent If you add filters of your own before the `AuthorizationFilter`, they will also not require authorization; otherwise, they will. A place where this typically becomes important is when you are adding {spring-framework-reference-url}web.html#spring-web[Spring MVC] endpoints. -Because they are executed by the {spring-framework-reference-url}web.html#mvc-servlet[`DispatcherServlet`] and this comes after the `AuthorizationFilter`, your endpoints need to be <>. +Because they are executed by the {spring-framework-reference-url}web.html#mvc-servlet[`DispatcherServlet`] and this comes after the `AuthorizationFilter`, you're endpoints need to be <>. === All Dispatches Are Authorized @@ -571,156 +571,70 @@ http { ---- ==== -[[match-by-servlet-path]] -[[mvc-not-default-servlet]] [[match-by-mvc]] -=== Matching by Servlet Pattern +=== Using an MvcRequestMatcher -Generally speaking, you can use `requestMatchers(String...)` and `requestMatchers(HttpMethod, String...)` as demonstrated above. +Generally speaking, you can use `requestMatchers(String)` as demonstrated above. However, if you map Spring MVC to a different servlet path, then you need to account for that in your security configuration. -For example, if Spring MVC is mapped to `/mvc` instead of `/` (the default), then you may have an endpoint like `/mvc/my/controller` that you want to authorize. +For example, if Spring MVC is mapped to `/spring-mvc` instead of `/` (the default), then you may have an endpoint like `/spring-mvc/my/controller` that you want to authorize. -If you have multiple servlets, and `DispatcherServlet` is mapped in this way, you'll see an error that's something like this: - -[source,bash] ----- -This method cannot decide whether these patterns are Spring MVC patterns or not - -... - -For your reference, here is your servlet configuration: {default=[/], dispatcherServlet=[/mvc/*]} - -To address this, you need to specify the servlet path or pattern for each endpoint. -You can use .forServletPattern in conjunction with requestMatchers do to this ----- - -You can use `.forServletPattern` (or construct your own `MvcRequestMatcher` instance) to split the servlet path and the controller path in your configuration, like so: +You need to use `MvcRequestMatcher` to split the servlet path and the controller path in your configuration like so: .Match by MvcRequestMatcher ==== .Java [source,java,role="primary"] ---- +@Bean +MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) { + return new MvcRequestMatcher.Builder(introspector).servletPath("/spring-mvc"); +} + @Bean SecurityFilterChain appEndpoints(HttpSecurity http, MvcRequestMatcher.Builder mvc) { http .authorizeHttpRequests((authorize) -> authorize - .forServletPattern("/mvc/*", (mvc) -> mvc - .requestMatchers("/my/resource/**").hasAuthority("resource:read") - .anyRequest().authenticated() - ) + .requestMatchers(mvc.pattern("/my/controller/**")).hasAuthority("controller") + .anyRequest().authenticated() ); return http.build(); } ---- -==== -where `/mvc/*` is the matching pattern in your servlet configuration listed in the error message. +.Kotlin +[source,kotlin,role="secondary"] +---- +@Bean +fun mvc(introspector: HandlerMappingIntrospector): MvcRequestMatcher.Builder = + MvcRequestMatcher.Builder(introspector).servletPath("/spring-mvc"); + +@Bean +fun appEndpoints(http: HttpSecurity, mvc: MvcRequestMatcher.Builder): SecurityFilterChain = + http { + authorizeHttpRequests { + authorize(mvc.pattern("/my/controller/**"), hasAuthority("controller")) + authorize(anyRequest, authenticated) + } + } +---- + +.Xml +[source,xml,role="secondary"] +---- + + + + +---- +==== This need can arise in at least two different ways: * If you use the `spring.mvc.servlet.path` Boot property to change the default path (`/`) to something else -* If you register more than one Spring MVC `DispatcherServlet` (thus requiring that one of them not be the default servlet) - -Note that when either of these cases come up, all URIs need to be fully-qualified as above. - -For example, consider a more sophisticated setup where you have Spring MVC resources mapped to `/mvc/*` and Spring Boot H2 Console mapped to `/h2-console/*`. -In that case, each URI can be made absolute, listing the servlet path like so: - -.Match by Servlet Path -==== -.Java -[source,java,role="primary"] ----- -@Bean -SecurityFilterChain appSecurity(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests((authorize) -> authorize - .forServletPattern("/mvc/*", (mvc) -> mvc - .requestMatchers("/my/resource/**").hasAuthority("resource:read") - ) - .forServletPattern("/h2-console/*", (h2) -> h2 - .anyRequest().hasAuthority("h2") - ) - ) - // ... -} ----- -==== - -Alternatively, you can do one of three things to remove the need to disambiguate: - -1. Always deploy `DispatcherServlet` to `/` (the default behavior) -+ -When `DispatcherServlet` is mapped to `/`, it's clear that all the URIs supplied in `requestMatchers(String)` are absolute URIs. -Because of that, there is no ambiguity when interpreting them. -+ -2. Remove all other servlets -+ -When there is only `DispatcherServlet`, it's clear that all the URIs supplied in `requestMatchers(String)` are relative to the Spring MVC configuration. -Because of that, there is no ambiguity when interpreting them. - -At times, servlet containers add other servlets by default that you aren't actually using. -So, if these aren't needed, remove them, bringing you down to just `DispatcherServlet`. -+ -3. Create an `HttpRequestHandler` so that `DispatcherServlet` dispatches to your servlets instead of your servlet container. -+ -If you are deploying Spring MVC to a separate path to allow your container to serve static resources, consider instead {spring-framework-reference-url}web/webmvc/mvc-config/default-servlet-handler.html#page-title[notifying Spring MVC about this]. -Or, if you have a custom servlet, publishing {spring-framework-api-url}org/springframework/web/servlet/mvc/HttpRequestHandlerAdapter.html[a custom `HttpRequestHandler` bean within {spring-framework-api-url}org/springframework/web/servlet/DispatcherServlet.html[the `DispatcherServlet` configuration] instead. -+ - -=== Matching by the Default Servlet - -You can also match more generally by the matching pattern specified in your servlet configuration. - -For example, to match the default servlet (whichever servlet is mapped to `/`), use `forServletPattern` like so: - -.Match by the Default Servlet -==== -.Java -[source,java,role="primary"] ----- -@Bean -SecurityFilterChain appSecurity(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests((authorize) -> authorize - .forServletPattern("/", (root) -> root - .requestMatchers("/my/resource/**").hasAuthority("resource:read") - ) - ) - // ... -} ----- -==== - -Such will match on requests that the servlet container matches to your default servlet that start with the URI `/my/resource`. - -=== Matching by an Extension Servlet - -Or, to match to an extension servlet (like a servlet mapped to `*.jsp`), use `forServletPattern` as follows: - -.Match by an Extension Servlet -==== -.Java -[source,java,role="primary"] ----- -@Bean -SecurityFilterChain appSecurity(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests((authorize) -> authorize - .forServletPattern("*.jsp", (jsp) -> jsp - .requestMatchers("/my/resource/**").hasAuthority("resource:read") - ) - ) - // ... -} ----- -==== - -Such will match on requests that the servlet container matches to your `*.jsp` servlet that start with the URI `/my/resource` (for example a request like `/my/resource/page.jsp`). +* If you register more than one Spring MVC `DispatcherServlet` (thus requiring that one of them not be the default path) [[match-by-custom]] === Using a Custom Matcher diff --git a/docs/modules/ROOT/pages/servlet/configuration/java.adoc b/docs/modules/ROOT/pages/servlet/configuration/java.adoc index 03ac51d827..326eaa333b 100644 --- a/docs/modules/ROOT/pages/servlet/configuration/java.adoc +++ b/docs/modules/ROOT/pages/servlet/configuration/java.adoc @@ -227,11 +227,7 @@ This configuration is considered after `apiFilterChain`, since it has an `@Order You can provide your own custom DSLs in Spring Security: -[tabs] -====== -Java:: -+ -[source,java,role="primary"] +[source,java] ---- public class MyCustomDsl extends AbstractHttpConfigurer { private boolean flag; @@ -264,38 +260,6 @@ public class MyCustomDsl extends AbstractHttpConfigurer() { - var flag: Boolean = false - - override fun init(http: HttpSecurity) { - // any method that adds another configurer - // must be done in the init method - http.csrf().disable() - } - - override fun configure(http: HttpSecurity) { - val context: ApplicationContext = http.getSharedObject(ApplicationContext::class.java) - - // here we lookup from the ApplicationContext. You can also just create a new instance. - val myFilter: MyFilter = context.getBean(MyFilter::class.java) - myFilter.setFlag(flag) - http.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter::class.java) - } - - companion object { - @JvmStatic - fun customDsl(): MyCustomDsl { - return MyCustomDsl() - } - } -} ----- -====== - [NOTE] ==== This is actually how methods like `HttpSecurity.authorizeRequests()` are implemented. @@ -303,11 +267,7 @@ This is actually how methods like `HttpSecurity.authorizeRequests()` are impleme You can then use the custom DSL: -[tabs] -====== -Java:: -+ -[source,java,role="primary"] +[source,java] ---- @Configuration @EnableWebSecurity @@ -315,37 +275,15 @@ public class Config { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http - .with(MyCustomDsl.customDsl(), (dsl) -> dsl + .apply(customDsl()) .flag(true) - ) - // ... + .and() + ...; return http.build(); } } ---- -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -@Configuration -@EnableWebSecurity -class Config { - - @Bean - fun filterChain(http: HttpSecurity): SecurityFilterChain { - http - .with(MyCustomDsl.customDsl()) { - flag = true - } - // ... - - return http.build() - } -} ----- -====== - The code is invoked in the following order: * Code in the `Config.filterChain` method is invoked @@ -363,50 +301,21 @@ org.springframework.security.config.annotation.web.configurers.AbstractHttpConfi You can also explicit disable the default: -[tabs] -====== -Java:: -+ -[source,java,role="primary"] +[source,java] ---- - @Configuration @EnableWebSecurity public class Config { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http - .with(MyCustomDsl.customDsl(), (dsl) -> dsl - .disable() - ) + .apply(customDsl()).disable() ...; return http.build(); } } ---- -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -@Configuration -@EnableWebSecurity -class Config { - - @Bean - fun filterChain(http: HttpSecurity): SecurityFilterChain { - http - .with(MyCustomDsl.customDsl()) { - disable() - } - // ... - return http.build() - } - -} ----- -====== - [[post-processing-configured-objects]] == Post Processing Configured Objects diff --git a/docs/modules/ROOT/pages/servlet/exploits/csrf.adoc b/docs/modules/ROOT/pages/servlet/exploits/csrf.adoc index add98c3069..19b1859078 100644 --- a/docs/modules/ROOT/pages/servlet/exploits/csrf.adoc +++ b/docs/modules/ROOT/pages/servlet/exploits/csrf.adoc @@ -114,7 +114,7 @@ The following is an overview of the aspects of CSRF protection that have changed The changes in Spring Security 6 require additional configuration for single-page applications, and as such you may find the <> section particularly useful. ==== -See the https://docs.spring.io/spring-security/reference/5.8/migration/servlet/exploits.html[Exploit Protection] section of the https://docs.spring.io/spring-security/reference/5.8/migration/index.html[Migration] chapter for more information on migrating a Spring Security 5 application. +See the xref:migration/servlet/exploits.adoc[Exploit Protection] section of the xref:migration/servlet/index.adoc[Migration] chapter for more information on migrating a Spring Security 5 application. [[csrf-token-repository]] == Persisting the `CsrfToken` @@ -1609,4 +1609,4 @@ You can find more information in the https://docs.spring.io/spring/docs/current/ [[csrf-further-reading]] == Further Reading -Now that you have reviewed CSRF protection, consider learning more about xref:servlet/exploits/index.adoc[exploit protection] including xref:servlet/exploits/headers.adoc[secure headers] and the xref:servlet/exploits/firewall.adoc[HTTP firewall] or move on to learning how to xref:servlet/test/index.adoc[test] your application. +Now that you have reviewed CSRF protection, consider learning more about xref:servlet/exploits/index.adoc[exploit protection] including xref:servlet/exploits/headers.adoc[secure headers] and the xref:servlet/exploits/firewall.adoc[HTTP firewall] or move on to learning how to xref:servlet/test/index.adoc[test] your application. \ No newline at end of file diff --git a/docs/modules/ROOT/pages/servlet/exploits/firewall.adoc b/docs/modules/ROOT/pages/servlet/exploits/firewall.adoc index 4ab70e3669..a334a409d8 100644 --- a/docs/modules/ROOT/pages/servlet/exploits/firewall.adoc +++ b/docs/modules/ROOT/pages/servlet/exploits/firewall.adoc @@ -111,7 +111,7 @@ XML:: ---- + p:allowedHttpMethods="GET,HEAD"/> ---- diff --git a/docs/modules/ROOT/pages/servlet/integrations/cors.adoc b/docs/modules/ROOT/pages/servlet/integrations/cors.adoc index 115e9b8f9e..b3e5199570 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/cors.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/cors.adoc @@ -6,8 +6,7 @@ CORS must be processed before Spring Security, because the pre-flight request do If the request does not contain any cookies and Spring Security is first, the request determines that the user is not authenticated (since there are no cookies in the request) and rejects it. The easiest way to ensure that CORS is handled first is to use the `CorsFilter`. -Users can integrate the `CorsFilter` with Spring Security by providing a `CorsConfigurationSource`. -For example, the following will integrate CORS support within Spring Security: +Users can integrate the `CorsFilter` with Spring Security by providing a `CorsConfigurationSource` that uses the following: [tabs] ====== @@ -15,14 +14,28 @@ Java:: + [source,java,role="primary"] ---- -@Bean -CorsConfigurationSource corsConfigurationSource() { - CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOrigins(Arrays.asList("https://example.com")); - configuration.setAllowedMethods(Arrays.asList("GET","POST")); - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", configuration); - return source; +@Configuration +@EnableWebSecurity +public class WebSecurityConfig { + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + // by default uses a Bean by the name of corsConfigurationSource + .cors(withDefaults()) + ... + return http.build(); + } + + @Bean + CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins(Arrays.asList("https://example.com")); + configuration.setAllowedMethods(Arrays.asList("GET","POST")); + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } } ---- @@ -30,14 +43,28 @@ Kotlin:: + [source,kotlin,role="secondary"] ---- -@Bean -fun corsConfigurationSource(): CorsConfigurationSource { - val configuration = CorsConfiguration() - configuration.allowedOrigins = listOf("https://example.com") - configuration.allowedMethods = listOf("GET", "POST") - val source = UrlBasedCorsConfigurationSource() - source.registerCorsConfiguration("/**", configuration) - return source +@Configuration +@EnableWebSecurity +open class WebSecurityConfig { + @Bean + open fun filterChain(http: HttpSecurity): SecurityFilterChain { + http { + // by default uses a Bean by the name of corsConfigurationSource + cors { } + // ... + } + return http.build() + } + + @Bean + open fun corsConfigurationSource(): CorsConfigurationSource { + val configuration = CorsConfiguration() + configuration.allowedOrigins = listOf("https://example.com") + configuration.allowedMethods = listOf("GET", "POST") + val source = UrlBasedCorsConfigurationSource() + source.registerCorsConfiguration("/**", configuration) + return source + } } ---- ====== @@ -110,76 +137,3 @@ The following listing does the same thing in XML: ... ---- - -If you have more than one `CorsConfigurationSource` bean, Spring Security won't automatically configure CORS support for you, that is because it cannot decide which one to use. -If you want to specify different `CorsConfigurationSource` for each `SecurityFilterChain`, you can pass it directly into the `.cors()` DSL. - -[tabs] -====== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -@EnableWebSecurity -public class WebSecurityConfig { - - @Bean - @Order(0) - public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception { - http - .securityMatcher("/api/**") - .cors((cors) -> cors - .configurationSource(apiConfigurationSource()) - ) - ... - return http.build(); - } - - @Bean - @Order(1) - public SecurityFilterChain myOtherFilterChain(HttpSecurity http) throws Exception { - http - .cors((cors) -> cors - .configurationSource(myWebsiteConfigurationSource()) - ) - ... - return http.build(); - } - - CorsConfigurationSource apiConfigurationSource() { - CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOrigins(Arrays.asList("https://api.example.com")); - configuration.setAllowedMethods(Arrays.asList("GET","POST")); - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", configuration); - return source; - } - - CorsConfigurationSource myWebsiteConfigurationSource() { - CorsConfiguration configuration = new CorsConfiguration(); - configuration.setAllowedOrigins(Arrays.asList("https://example.com")); - configuration.setAllowedMethods(Arrays.asList("GET","POST")); - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", configuration); - return source; - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -@Bean -fun corsConfigurationSource(): CorsConfigurationSource { - val configuration = CorsConfiguration() - configuration.allowedOrigins = listOf("https://example.com") - configuration.allowedMethods = listOf("GET", "POST") - val source = UrlBasedCorsConfigurationSource() - source.registerCorsConfiguration("/**", configuration) - return source -} ----- -====== diff --git a/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc b/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc index 048f9b0b55..fb87ac25e4 100644 --- a/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc +++ b/docs/modules/ROOT/pages/servlet/integrations/websocket.adoc @@ -58,7 +58,7 @@ Kotlin:: @EnableWebSocketSecurity // <1> <2> open class WebSocketSecurityConfig { // <1> <2> @Bean - fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager> { + fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager> { messages.simpDestMatchers("/user/**").hasRole("USER") // <3> return messages.build() } @@ -108,7 +108,7 @@ Kotlin:: @EnableWebSocketSecurity // <1> <2> open class WebSocketSecurityConfig { @Bean - fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager> { + fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager> { return AuthorityAuthorizationManager.hasRole("USER") // <3> } } @@ -156,7 +156,7 @@ Kotlin:: ---- @Configuration open class WebSocketSecurityConfig { - fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager> { + fun messageAuthorizationManager(messages: MessageMatcherDelegatingAuthorizationManager.Builder): AuthorizationManager { messages .nullDestMatcher().authenticated() // <1> .simpSubscribeDestMatchers("/user/queue/errors").permitAll() // <2> @@ -394,7 +394,7 @@ open class WebSocketSecurityConfig : WebSocketMessageBrokerConfigurer { @Override override fun configureClientInboundChannel(registration: ChannelRegistration) { - var myAuthorizationRules: AuthorizationManager> = AuthenticatedAuthorizationManager.authenticated() + var myAuthorizationRules: AuthorizationManager> = AuthenticatedAuthorizationManager.authenticated() var authz: AuthorizationChannelInterceptor = AuthorizationChannelInterceptor(myAuthorizationRules) var publisher: AuthorizationEventPublisher = SpringAuthorizationEventPublisher(this.context) authz.setAuthorizationEventPublisher(publisher) diff --git a/docs/modules/ROOT/pages/servlet/oauth2/index.adoc b/docs/modules/ROOT/pages/servlet/oauth2/index.adoc index 512400577d..abcb2591d8 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/index.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/index.adoc @@ -1,1687 +1,7 @@ = OAuth2 +:page-section-summary-toc: 1 -Spring Security provides comprehensive OAuth 2.0 support. -This section discusses how to integrate OAuth 2.0 into your servlet based application. +Spring Security provides comprehensive OAuth 2 support. +This section discusses how to integrate OAuth 2 into your servlet based application. -[[oauth2-overview]] -== Overview -Spring Security's OAuth 2.0 support consists of two primary feature sets: - -* <> -* <> - -[NOTE] -==== -<> is a very powerful OAuth2 Client feature that deserves its own section in the reference documentation. -However, it does not exist as a standalone feature and requires OAuth2 Client in order to function. -==== - -These feature sets cover the _resource server_ and _client_ roles defined in the https://tools.ietf.org/html/rfc6749#section-1.1[OAuth 2.0 Authorization Framework], while the _authorization server_ role is covered by https://docs.spring.io/spring-authorization-server/reference/index.html[Spring Authorization Server], which is a separate project built on xref:index.adoc[Spring Security]. - -The _resource server_ and _client_ roles in OAuth2 are typically represented by one or more server-side applications. -Additionally, the _authorization server_ role can be represented by one or more third parties (as is the case when centralizing identity management and/or authentication within an organization) *-or-* it can be represented by an application (as is the case with Spring Authorization Server). - -For example, a typical OAuth2-based microservices architecture might consist of a single user-facing client application, several backend resource servers providing REST APIs and a third party authorization server for managing users and authentication concerns. -It is also common to have a single application representing only one of these roles with the need to integrate with one or more third parties that are providing the other roles. - -Spring Security handles these scenarios and more. -The following sections cover the roles provided by Spring Security and contain examples for common scenarios. - -[[oauth2-resource-server]] -== OAuth2 Resource Server - -[NOTE] -==== -This section contains a summary of OAuth2 Resource Server features with examples. -See xref:servlet/oauth2/resource-server/index.adoc[OAuth 2.0 Resource Server] for complete reference documentation. -==== - -To get started, add the `spring-security-oauth2-resource-server` dependency to your project. -When using Spring Boot, add the following starter: - -.OAuth2 Client with Spring Boot -[tabs] -====== -Gradle:: -+ -[source,gradle,role="primary"] ----- -implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server' ----- - -Maven:: -+ -[source,maven,role="secondary"] ----- - - org.springframework.boot - spring-boot-starter-oauth2-resource-server - ----- -====== - -[TIP] -==== -See xref:getting-spring-security.adoc[] for additional options when not using Spring Boot. -==== - -Consider the following use cases for OAuth2 Resource Server: - -* <> (authorization server provides JWT or opaque access token) -* <> (custom token) - -[[oauth2-resource-server-access-token]] -=== Protect Access with an OAuth2 Access Token - -It is very common to protect access to an API using OAuth2 access tokens. -In most cases, Spring Security requires only minimal configuration to secure an application with OAuth2. - -There are two types of `Bearer` tokens supported by Spring Security which each use a different component for validation: - -* <> uses a `JwtDecoder` bean to validate signatures and decode tokens -* <> uses an `OpaqueTokenIntrospector` bean to introspect tokens - -[[oauth2-resource-server-access-token-jwt]] -==== JWT Support - -The following example configures a `JwtDecoder` bean using Spring Boot configuration properties: - -[source,yaml] ----- -spring: - security: - oauth2: - resourceserver: - jwt: - issuer-uri: https://my-auth-server.com ----- - -When using Spring Boot, this is all that is required. -The default arrangement provided by Spring Boot is equivalent to the following: - -.Configure Resource Server with JWTs -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -@EnableWebSecurity -public class SecurityConfig { - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests((authorize) -> authorize - .anyRequest().authenticated() - ) - .oauth2ResourceServer((oauth2) -> oauth2 - .jwt(Customizer.withDefaults()) - ); - return http.build(); - } - - @Bean - public JwtDecoder jwtDecoder() { - return JwtDecoders.fromIssuerLocation("https://my-auth-server.com"); - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -import org.springframework.security.config.annotation.web.invoke - -@Configuration -@EnableWebSecurity -class SecurityConfig { - - @Bean - fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - http { - authorizeHttpRequests { - authorize(anyRequest, authenticated) - } - oauth2ResourceServer { - jwt { } - } - } - - return http.build() - } - - @Bean - fun jwtDecoder(): JwtDecoder { - return JwtDecoders.fromIssuerLocation("https://my-auth-server.com") - } - -} ----- -===== - -[[oauth2-resource-server-access-token-opaque]] -==== Opaque Token Support - -The following example configures an `OpaqueTokenIntrospector` bean using Spring Boot configuration properties: - -[source,yaml] ----- -spring: - security: - oauth2: - resourceserver: - opaquetoken: - introspection-uri: https://my-auth-server.com/oauth2/introspect - client-id: my-client-id - client-secret: my-client-secret ----- - -When using Spring Boot, this is all that is required. -The default arrangement provided by Spring Boot is equivalent to the following: - -.Configure Resource Server with Opaque Tokens -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -@EnableWebSecurity -public class SecurityConfig { - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests((authorize) -> authorize - .anyRequest().authenticated() - ) - .oauth2ResourceServer((oauth2) -> oauth2 - .opaqueToken(Customizer.withDefaults()) - ); - return http.build(); - } - - @Bean - public OpaqueTokenIntrospector opaqueTokenIntrospector() { - return new SpringOpaqueTokenIntrospector( - "https://my-auth-server.com/oauth2/introspect", "my-client-id", "my-client-secret"); - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -import org.springframework.security.config.annotation.web.invoke - -@Configuration -@EnableWebSecurity -class SecurityConfig { - - @Bean - fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - http { - authorizeHttpRequests { - authorize(anyRequest, authenticated) - } - oauth2ResourceServer { - opaqueToken { } - } - } - - return http.build() - } - - @Bean - fun opaqueTokenIntrospector(): OpaqueTokenIntrospector { - return SpringOpaqueTokenIntrospector( - "https://my-auth-server.com/oauth2/introspect", "my-client-id", "my-client-secret" - ) - } - -} ----- -===== - -[[oauth2-resource-server-custom-jwt]] -=== Protect Access with a custom JWT - -It is a fairly common goal to protect access to an API using JWTs, particularly when the frontend is developed as a single-page application. -The OAuth2 Resource Server support in Spring Security can be used for any type of `Bearer` token, including a custom JWT. - -All that is required to protect an API using JWTs is a `JwtDecoder` bean, which is used to validate signatures and decode tokens. -Spring Security will automatically use the provided bean to configure protection within the `SecurityFilterChain`. - -The following example configures a `JwtDecoder` bean using Spring Boot configuration properties: - -[source,yaml] ----- -spring: - security: - oauth2: - resourceserver: - jwt: - public-key-location: classpath:my-public-key.pub ----- - -[NOTE] -==== -You can provide the public key as a classpath resource (called `my-public-key.pub` in this example). -==== - -When using Spring Boot, this is all that is required. -The default arrangement provided by Spring Boot is equivalent to the following: - -.Configure Resource Server with Custom JWTs -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -@EnableWebSecurity -public class SecurityConfig { - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests((authorize) -> authorize - .anyRequest().authenticated() - ) - .oauth2ResourceServer((oauth2) -> oauth2 - .jwt(Customizer.withDefaults()) - ); - return http.build(); - } - - @Bean - public JwtDecoder jwtDecoder() { - return NimbusJwtDecoder.withPublicKey(publicKey()).build(); - } - - private RSAPublicKey publicKey() { - // ... - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -import org.springframework.security.config.annotation.web.invoke - -@Configuration -@EnableWebSecurity -class SecurityConfig { - - @Bean - fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - http { - authorizeHttpRequests { - authorize(anyRequest, authenticated) - } - oauth2ResourceServer { - jwt { } - } - } - - return http.build() - } - - @Bean - fun jwtDecoder(): JwtDecoder { - return NimbusJwtDecoder.withPublicKey(publicKey()).build() - } - - private fun publicKey(): RSAPublicKey { - // ... - } - -} ----- -===== - -[NOTE] -==== -Spring Security does not provide an endpoint for minting tokens. -However, Spring Security does provide the `JwtEncoder` interface along with one implementation, which is `NimbusJwtEncoder`. -==== - -[[oauth2-client]] -== OAuth2 Client - -[NOTE] -==== -This section contains a summary of OAuth2 Client features with examples. -See xref:servlet/oauth2/client/index.adoc[OAuth 2.0 Client] and xref:servlet/oauth2/login/index.adoc[OAuth 2.0 Login] for complete reference documentation. -==== - -To get started, add the `spring-security-oauth2-client` dependency to your project. -When using Spring Boot, add the following starter: - -.OAuth2 Client with Spring Boot -[tabs] -====== -Gradle:: -+ -[source,gradle,role="primary"] ----- -implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' ----- - -Maven:: -+ -[source,maven,role="secondary"] ----- - - org.springframework.boot - spring-boot-starter-oauth2-client - ----- -====== - -[TIP] -==== -See xref:getting-spring-security.adoc[] for additional options when not using Spring Boot. -==== - -Consider the following use cases for OAuth2 Client: - -* <> -* <> -* <> (log users in _and_ access a third-party API) -* <> -* <> -* <> -* <> - -[[oauth2-client-log-users-in]] -=== Log Users In with OAuth2 - -It is very common to require users to log in via OAuth2. -https://openid.net/specs/openid-connect-core-1_0.html[OpenID Connect 1.0] provides a special token called the `id_token` which is designed to provide an OAuth2 Client with the ability to perform user identity verification and log users in. -In certain cases, OAuth2 can be used directly to log users in (as is the case with popular social login providers that do not implement OpenID Connect such as GitHub and Facebook). - -The following example configures the application to act as an OAuth2 Client capable of logging users in with OAuth2 or OpenID Connect: - -.Configure OAuth2 Login -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -@EnableWebSecurity -public class SecurityConfig { - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - // ... - .oauth2Login(Customizer.withDefaults()); - return http.build(); - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -import org.springframework.security.config.annotation.web.invoke - -@Configuration -@EnableWebSecurity -class SecurityConfig { - - @Bean - fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - http { - // ... - oauth2Login { } - } - - return http.build() - } - -} ----- -===== - -In addition to the above configuration, the application requires at least one `ClientRegistration` to be configured through the use of a `ClientRegistrationRepository` bean. -The following example configures an `InMemoryClientRegistrationRepository` bean using Spring Boot configuration properties: - -[source,yaml] ----- -spring: - security: - oauth2: - client: - registration: - my-oidc-client: - provider: my-oidc-provider - client-id: my-client-id - client-secret: my-client-secret - authorization-grant-type: authorization_code - scope: openid,profile - provider: - my-oidc-provider: - issuer-uri: https://my-oidc-provider.com ----- - -With the above configuration, the application now supports two additional endpoints: - -1. The login endpoint (e.g. `/oauth2/authorization/my-oidc-client`) is used to initiate login and perform a redirect to the third party authorization server. -2. The redirection endpoint (e.g. `/login/oauth2/code/my-oidc-client`) is used by the authorization server to redirect back to the client application, and will contain a `code` parameter used to obtain an `id_token` and/or `access_token` via the access token request. - -[NOTE] -==== -The presence of the `openid` scope in the above configuration indicates that OpenID Connect 1.0 should be used. -This instructs Spring Security to use OIDC-specific components (such as `OidcUserService`) during request processing. -Without this scope, Spring Security will use OAuth2-specific components (such as `OAuth2UserService`) instead. -==== - -[[oauth2-client-access-protected-resources]] -=== Access Protected Resources - -Making requests to a third party API that is protected by OAuth2 is a core use case of OAuth2 Client. -This is accomplished by authorizing a client (represented by the `OAuth2AuthorizedClient` class in Spring Security) and accessing protected resources by placing a `Bearer` token in the `Authorization` header of an outbound request. - -The following example configures the application to act as an OAuth2 Client capable of requesting protected resources from a third party API: - -.Configure OAuth2 Client -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -@EnableWebSecurity -public class SecurityConfig { - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - // ... - .oauth2Client(Customizer.withDefaults()); - return http.build(); - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -import org.springframework.security.config.annotation.web.invoke - -@Configuration -@EnableWebSecurity -class SecurityConfig { - - @Bean - fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - http { - // ... - oauth2Client { } - } - - return http.build() - } - -} ----- -===== - -[NOTE] -==== -The above example does not provide a way to log users in. -You can use any other login mechanism (such as `formLogin()`). -See the <> for an example combining `oauth2Client()` with `oauth2Login()`. -==== - -In addition to the above configuration, the application requires at least one `ClientRegistration` to be configured through the use of a `ClientRegistrationRepository` bean. -The following example configures an `InMemoryClientRegistrationRepository` bean using Spring Boot configuration properties: - -[source,yaml] ----- -spring: - security: - oauth2: - client: - registration: - my-oauth2-client: - provider: my-auth-server - client-id: my-client-id - client-secret: my-client-secret - authorization-grant-type: authorization_code - scope: message.read,message.write - provider: - my-auth-server: - issuer-uri: https://my-auth-server.com ----- - -In addition to configuring Spring Security to support OAuth2 Client features, you will also need to decide how you will be accessing protected resources and configure your application accordingly. -Spring Security provides implementations of `OAuth2AuthorizedClientManager` for obtaining access tokens that can be used to access protected resources. - -[TIP] -==== -Spring Security registers a default `OAuth2AuthorizedClientManager` bean for you when one does not exist. -==== - -The easiest way to use an `OAuth2AuthorizedClientManager` is via an `ExchangeFilterFunction` that intercepts requests through a `WebClient`. -To use `WebClient`, you will need to add the `spring-webflux` dependency along with a reactive client implementation: - -.Add Spring WebFlux Dependency -[tabs] -====== -Gradle:: -+ -[source,gradle,role="primary"] ----- -implementation 'org.springframework:spring-webflux' -implementation 'io.projectreactor.netty:reactor-netty' ----- - -Maven:: -+ -[source,maven,role="secondary"] ----- - - org.springframework - spring-webflux - - - io.projectreactor.netty - reactor-netty - ----- -====== - -The following example uses the default `OAuth2AuthorizedClientManager` to configure a `WebClient` capable of accessing protected resources by placing `Bearer` tokens in the `Authorization` header of each request: - -.Configure `WebClient` with `ExchangeFilterFunction` -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -public class WebClientConfig { - - @Bean - public WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) { - ServletOAuth2AuthorizedClientExchangeFilterFunction filter = - new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager); - return WebClient.builder() - .apply(filter.oauth2Configuration()) - .build(); - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -@Configuration -class WebClientConfig { - - @Bean - fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager): WebClient { - val filter = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager) - return WebClient.builder() - .apply(filter.oauth2Configuration()) - .build() - } - -} ----- -===== - -This configured `WebClient` can be used as in the following example: - -[[oauth2-client-accessing-protected-resources-example]] -.Use `WebClient` to Access Protected Resources -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId; - -@RestController -public class MessagesController { - - private final WebClient webClient; - - public MessagesController(WebClient webClient) { - this.webClient = webClient; - } - - @GetMapping("/messages") - public ResponseEntity> messages() { - return this.webClient.get() - .uri("http://localhost:8090/messages") - .attributes(clientRegistrationId("my-oauth2-client")) - .retrieve() - .toEntityList(Message.class) - .block(); - } - - public record Message(String message) { - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.clientRegistrationId - -@RestController -class MessagesController(private val webClient: WebClient) { - - @GetMapping("/messages") - fun messages(): ResponseEntity> { - return webClient.get() - .uri("http://localhost:8090/messages") - .attributes(clientRegistrationId("my-oauth2-client")) - .retrieve() - .toEntityList(Message::class.java) - .block()!! - } - - data class Message(val message: String) - -} ----- -===== - -[[oauth2-client-access-protected-resources-current-user]] -=== Access Protected Resources for the Current User - -When a user is logged in via OAuth2 or OpenID Connect, the authorization server may provide an access token that can be used directly to access protected resources. -This is convenient because it only requires a single `ClientRegistration` to be configured for both use cases simultaneously. - -[NOTE] -==== -This section combines <> and <> into a single configuration. -Other advanced scenarios exist, such as configuring one `ClientRegistration` for login and another for accessing protected resources. -All such scenarios would use the same basic configuration. -==== - -The following example configures the application to act as an OAuth2 Client capable of logging the user in _and_ requesting protected resources from a third party API: - -.Configure OAuth2 Login and OAuth2 Client -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -@EnableWebSecurity -public class SecurityConfig { - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - http - // ... - .oauth2Login(Customizer.withDefaults()) - .oauth2Client(Customizer.withDefaults()); - return http.build(); - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -import org.springframework.security.config.annotation.web.invoke - -@Configuration -@EnableWebSecurity -class SecurityConfig { - - @Bean - fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - http { - // ... - oauth2Login { } - oauth2Client { } - } - - return http.build() - } - -} ----- -===== - -In addition to the above configuration, the application requires at least one `ClientRegistration` to be configured through the use of a `ClientRegistrationRepository` bean. -The following example configures an `InMemoryClientRegistrationRepository` bean using Spring Boot configuration properties: - -[source,yaml] ----- -spring: - security: - oauth2: - client: - registration: - my-combined-client: - provider: my-auth-server - client-id: my-client-id - client-secret: my-client-secret - authorization-grant-type: authorization_code - scope: openid,profile,message.read,message.write - provider: - my-auth-server: - issuer-uri: https://my-auth-server.com ----- - -[NOTE] -==== -The main difference between the previous examples (<>, <>) and this one is what is configured via the `scope` property, which combines the standard scopes `openid` and `profile` with the custom scopes `message.read` and `message.write`. -==== - -In addition to configuring Spring Security to support OAuth2 Client features, you will also need to decide how you will be accessing protected resources and configure your application accordingly. -Spring Security provides implementations of `OAuth2AuthorizedClientManager` for obtaining access tokens that can be used to access protected resources. - -[TIP] -==== -Spring Security registers a default `OAuth2AuthorizedClientManager` bean for you when one does not exist. -==== - -The easiest way to use an `OAuth2AuthorizedClientManager` is via an `ExchangeFilterFunction` that intercepts requests through a `WebClient`. -To use `WebClient`, you will need to add the `spring-webflux` dependency along with a reactive client implementation: - -.Add Spring WebFlux Dependency -[tabs] -====== -Gradle:: -+ -[source,gradle,role="primary"] ----- -implementation 'org.springframework:spring-webflux' -implementation 'io.projectreactor.netty:reactor-netty' ----- - -Maven:: -+ -[source,maven,role="secondary"] ----- - - org.springframework - spring-webflux - - - io.projectreactor.netty - reactor-netty - ----- -====== - -The following example uses the default `OAuth2AuthorizedClientManager` to configure a `WebClient` capable of accessing protected resources by placing `Bearer` tokens in the `Authorization` header of each request: - -.Configure `WebClient` with `ExchangeFilterFunction` -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -public class WebClientConfig { - - @Bean - public WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) { - ServletOAuth2AuthorizedClientExchangeFilterFunction filter = - new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager); - return WebClient.builder() - .apply(filter.oauth2Configuration()) - .build(); - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -@Configuration -class WebClientConfig { - - @Bean - fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager): WebClient { - val filter = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager) - return WebClient.builder() - .apply(filter.oauth2Configuration()) - .build() - } - -} ----- -===== - -This configured `WebClient` can be used as in the following example: - -[[oauth2-client-accessing-protected-resources-current-user-example]] -.Use `WebClient` to Access Protected Resources (Current User) -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@RestController -public class MessagesController { - - private final WebClient webClient; - - public MessagesController(WebClient webClient) { - this.webClient = webClient; - } - - @GetMapping("/messages") - public ResponseEntity> messages() { - return this.webClient.get() - .uri("http://localhost:8090/messages") - .retrieve() - .toEntityList(Message.class) - .block(); - } - - public record Message(String message) { - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -@RestController -class MessagesController(private val webClient: WebClient) { - - @GetMapping("/messages") - fun messages(): ResponseEntity> { - return webClient.get() - .uri("http://localhost:8090/messages") - .retrieve() - .toEntityList(Message::class.java) - .block()!! - } - - data class Message(val message: String) - -} ----- -===== - -[NOTE] -==== -Unlike the <>, notice that we do not need to tell Spring Security about the `clientRegistrationId` we'd like to use. -This is because it can be derived from the currently logged in user. -==== - -[[oauth2-client-enable-extension-grant-type]] -=== Enable an Extension Grant Type - -A common use case involves enabling and/or configuring an extension grant type. -For example, Spring Security provides support for the `jwt-bearer` grant type, but does not enable it by default because it is not part of the core OAuth 2.0 specification. - -With Spring Security 6.2 and later, we can simply publish a bean for one or more `OAuth2AuthorizedClientProvider` and they will be picked up automatically. -The following example simply enables the `jwt-bearer` grant type: - -.Enable `jwt-bearer` Grant Type -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -public class SecurityConfig { - - @Bean - public OAuth2AuthorizedClientProvider jwtBearer() { - return new JwtBearerOAuth2AuthorizedClientProvider(); - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -@Configuration -class SecurityConfig { - - @Bean - fun jwtBearer(): OAuth2AuthorizedClientProvider { - return JwtBearerOAuth2AuthorizedClientProvider() - } - -} ----- -===== - -A default `OAuth2AuthorizedClientManager` will be published automatically by Spring Security when one is not already provided. - -[TIP] -==== -Any custom `OAuth2AuthorizedClientProvider` bean will also be picked up and applied to the provided `OAuth2AuthorizedClientManager` after the default grant types. -==== - -In order to achieve the above configuration prior to Spring Security 6.2, we had to publish this bean ourselves and ensure we re-enabled default grant types as well. -To understand what is being configured behind the scenes, here's what the configuration might have looked like: - -.Enable `jwt-bearer` Grant Type (prior to 6.2) -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -public class SecurityConfig { - - @Bean - public OAuth2AuthorizedClientManager authorizedClientManager( - ClientRegistrationRepository clientRegistrationRepository, - OAuth2AuthorizedClientRepository authorizedClientRepository) { - - OAuth2AuthorizedClientProvider authorizedClientProvider = - OAuth2AuthorizedClientProviderBuilder.builder() - .authorizationCode() - .refreshToken() - .clientCredentials() - .password() - .provider(new JwtBearerOAuth2AuthorizedClientProvider()) - .build(); - - DefaultOAuth2AuthorizedClientManager authorizedClientManager = - new DefaultOAuth2AuthorizedClientManager( - clientRegistrationRepository, authorizedClientRepository); - authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); - - return authorizedClientManager; - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -@Configuration -class SecurityConfig { - - @Bean - fun authorizedClientManager( - clientRegistrationRepository: ClientRegistrationRepository, - authorizedClientRepository: OAuth2AuthorizedClientRepository - ): OAuth2AuthorizedClientManager { - val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder() - .authorizationCode() - .refreshToken() - .clientCredentials() - .password() - .provider(JwtBearerOAuth2AuthorizedClientProvider()) - .build() - - val authorizedClientManager = DefaultOAuth2AuthorizedClientManager( - clientRegistrationRepository, authorizedClientRepository - ) - authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider) - - return authorizedClientManager - } - -} ----- -===== - -[[oauth2-client-customize-existing-grant-type]] -=== Customize an Existing Grant Type - -The ability to <> by publishing a bean also provides the opportunity for customizing an existing grant type without the need to re-define the defaults. -For example, if we want to customize the clock skew of the `OAuth2AuthorizedClientProvider` for the `client_credentials` grant, we can simply publish a bean like so: - -.Customize Client Credentials Grant Type -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -public class SecurityConfig { - - @Bean - public OAuth2AuthorizedClientProvider clientCredentials() { - ClientCredentialsOAuth2AuthorizedClientProvider authorizedClientProvider = - new ClientCredentialsOAuth2AuthorizedClientProvider(); - authorizedClientProvider.setClockSkew(Duration.ofMinutes(5)); - - return authorizedClientProvider; - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -@Configuration -class SecurityConfig { - - @Bean - fun clientCredentials(): OAuth2AuthorizedClientProvider { - val authorizedClientProvider = ClientCredentialsOAuth2AuthorizedClientProvider() - authorizedClientProvider.setClockSkew(Duration.ofMinutes(5)) - return authorizedClientProvider - } - -} ----- -===== - -[[oauth2-client-customize-request-parameters]] -=== Customize Token Request Parameters - -The need to customize request parameters when obtaining an access token is fairly common. -For example, let's say we want to add a custom `audience` parameter to the token request because the provider requires this parameter for the `authorization_code` grant. - -With Spring Security 6.2 and later, we can simply publish a bean of type `OAuth2AccessTokenResponseClient` with the generic type `OAuth2AuthorizationCodeGrantRequest` and it will be used by Spring Security to configure OAuth2 Client components. - -The following example customizes token request parameters for the `authorization_code` grant without the DSL: - -.Customize Token Request Parameters for Authorization Code Grant -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -public class SecurityConfig { - - @Bean - public OAuth2AccessTokenResponseClient authorizationCodeAccessTokenResponseClient() { - OAuth2AuthorizationCodeGrantRequestEntityConverter requestEntityConverter = - new OAuth2AuthorizationCodeGrantRequestEntityConverter(); - requestEntityConverter.addParametersConverter(parametersConverter()); - - DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient = - new DefaultAuthorizationCodeTokenResponseClient(); - accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter); - - return accessTokenResponseClient; - } - - private static Converter> parametersConverter() { - return (grantRequest) -> { - MultiValueMap parameters = new LinkedMultiValueMap<>(); - parameters.set("audience", "xyz_value"); - - return parameters; - }; - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -@Configuration -class SecurityConfig { - - @Bean - fun authorizationCodeAccessTokenResponseClient(): OAuth2AccessTokenResponseClient { - val requestEntityConverter = OAuth2AuthorizationCodeGrantRequestEntityConverter() - requestEntityConverter.addParametersConverter(parametersConverter()) - - val accessTokenResponseClient = DefaultAuthorizationCodeTokenResponseClient() - accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter) - - return accessTokenResponseClient - } - - private fun parametersConverter(): Converter> { - return Converter> { grantRequest -> - LinkedMultiValueMap().also { parameters -> - parameters["audience"] = "xyz_value" - } - } - } - -} ----- -===== - -[TIP] -==== -Notice that we don't need to customize the `SecurityFilterChain` bean in this case, and can stick with the defaults. -If using Spring Boot with no additional customizations, we can actually omit the `SecurityFilterChain` bean entirely. -==== - -Prior to Spring Security 6.2, we had to ensure that this customization was applied for both OAuth2 Login (if we are using this feature) and OAuth2 Client components using the Spring Security DSL. -To understand what is being configured behind the scenes, here's what the configuration might have looked like: - -.Customize Token Request Parameters for Authorization Code Grant (prior to 6.2) -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -@EnableWebSecurity -public class SecurityConfig { - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - OAuth2AuthorizationCodeGrantRequestEntityConverter requestEntityConverter = - new OAuth2AuthorizationCodeGrantRequestEntityConverter(); - requestEntityConverter.addParametersConverter(parametersConverter()); - - DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient = - new DefaultAuthorizationCodeTokenResponseClient(); - accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter); - - http - .authorizeHttpRequests((authorize) -> authorize - .anyRequest().authenticated() - ) - .oauth2Login((oauth2Login) -> oauth2Login - .tokenEndpoint((tokenEndpoint) -> tokenEndpoint - .accessTokenResponseClient(accessTokenResponseClient) - ) - ) - .oauth2Client((oauth2Client) -> oauth2Client - .authorizationCodeGrant((authorizationCode) -> authorizationCode - .accessTokenResponseClient(accessTokenResponseClient) - ) - ); - - return http.build(); - } - - private static Converter> parametersConverter() { - // ... - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -import org.springframework.security.config.annotation.web.invoke - -@Configuration -@EnableWebSecurity -class SecurityConfig { - - @Bean - fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - val requestEntityConverter = OAuth2AuthorizationCodeGrantRequestEntityConverter() - requestEntityConverter.addParametersConverter(parametersConverter()) - - val tokenResponseClient = DefaultAuthorizationCodeTokenResponseClient() - tokenResponseClient.setRequestEntityConverter(requestEntityConverter) - - http { - authorizeHttpRequests { - authorize(anyRequest, authenticated) - } - oauth2Login { - tokenEndpoint { - accessTokenResponseClient = tokenResponseClient - } - } - oauth2Client { - authorizationCodeGrant { - accessTokenResponseClient = tokenResponseClient - } - } - } - - return http.build() - } - - private fun parametersConverter(): Converter> { - // ... - } - -} ----- -===== - -For other grant types we can publish additional `OAuth2AccessTokenResponseClient` beans to override the defaults. -For example, to customize token requests for the `client_credentials` grant we can publish the following bean: - -.Customize Token Request Parameters for Client Credentials Grant -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -public class SecurityConfig { - - @Bean - public OAuth2AccessTokenResponseClient clientCredentialsAccessTokenResponseClient() { - OAuth2ClientCredentialsGrantRequestEntityConverter requestEntityConverter = - new OAuth2ClientCredentialsGrantRequestEntityConverter(); - requestEntityConverter.addParametersConverter(parametersConverter()); - - DefaultClientCredentialsTokenResponseClient accessTokenResponseClient = - new DefaultClientCredentialsTokenResponseClient(); - accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter); - - return accessTokenResponseClient; - } - - private static Converter> parametersConverter() { - // ... - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -@Configuration -class SecurityConfig { - - @Bean - fun clientCredentialsAccessTokenResponseClient(): OAuth2AccessTokenResponseClient { - val requestEntityConverter = OAuth2ClientCredentialsGrantRequestEntityConverter() - requestEntityConverter.addParametersConverter(parametersConverter()) - - val accessTokenResponseClient = DefaultClientCredentialsTokenResponseClient() - accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter) - - return accessTokenResponseClient - } - - private fun parametersConverter(): Converter> { - // ... - } - -} ----- -===== - -Spring Security automatically resolves the following generic types of `OAuth2AccessTokenResponseClient` beans: - -* `OAuth2AuthorizationCodeGrantRequest` (see `DefaultAuthorizationCodeTokenResponseClient`) -* `OAuth2RefreshTokenGrantRequest` (see `DefaultRefreshTokenTokenResponseClient`) -* `OAuth2ClientCredentialsGrantRequest` (see `DefaultClientCredentialsTokenResponseClient`) -* `OAuth2PasswordGrantRequest` (see `DefaultPasswordTokenResponseClient`) -* `JwtBearerGrantRequest` (see `DefaultJwtBearerTokenResponseClient`) - -[TIP] -==== -Publishing a bean of type `OAuth2AccessTokenResponseClient` will automatically enable the `jwt-bearer` grant type without the need to <>. -==== - -[[oauth2-client-customize-rest-operations]] -=== Customize the `RestOperations` used by OAuth2 Client Components - -Another common use case is the need to customize the `RestOperations` used when obtaining an access token. -We might need to do this to customize processing of the response (via a custom `HttpMessageConverter`) or to apply proxy settings for a corporate network (via a customized `ClientHttpRequestFactory`). - -With Spring Security 6.2 and later, we can simply publish beans of type `OAuth2AccessTokenResponseClient` and Spring Security will configure and publish an `OAuth2AuthorizedClientManager` bean for us. - -The following example customizes the `RestOperations` for all of the supported grant types: - -.Customize `RestOperations` for OAuth2 Client -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -public class SecurityConfig { - - @Bean - public OAuth2AccessTokenResponseClient authorizationCodeAccessTokenResponseClient() { - DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient = - new DefaultAuthorizationCodeTokenResponseClient(); - accessTokenResponseClient.setRestOperations(restTemplate()); - - return accessTokenResponseClient; - } - - @Bean - public OAuth2AccessTokenResponseClient refreshTokenAccessTokenResponseClient() { - DefaultRefreshTokenTokenResponseClient accessTokenResponseClient = - new DefaultRefreshTokenTokenResponseClient(); - accessTokenResponseClient.setRestOperations(restTemplate()); - - return accessTokenResponseClient; - } - - @Bean - public OAuth2AccessTokenResponseClient clientCredentialsAccessTokenResponseClient() { - DefaultClientCredentialsTokenResponseClient accessTokenResponseClient = - new DefaultClientCredentialsTokenResponseClient(); - accessTokenResponseClient.setRestOperations(restTemplate()); - - return accessTokenResponseClient; - } - - @Bean - public OAuth2AccessTokenResponseClient passwordAccessTokenResponseClient() { - DefaultPasswordTokenResponseClient accessTokenResponseClient = - new DefaultPasswordTokenResponseClient(); - accessTokenResponseClient.setRestOperations(restTemplate()); - - return accessTokenResponseClient; - } - - @Bean - public OAuth2AccessTokenResponseClient jwtBearerAccessTokenResponseClient() { - DefaultJwtBearerTokenResponseClient accessTokenResponseClient = - new DefaultJwtBearerTokenResponseClient(); - accessTokenResponseClient.setRestOperations(restTemplate()); - - return accessTokenResponseClient; - } - - @Bean - public RestTemplate restTemplate() { - // ... - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -@Configuration -class SecurityConfig { - - @Bean - fun authorizationCodeAccessTokenResponseClient(): OAuth2AccessTokenResponseClient { - val accessTokenResponseClient = DefaultAuthorizationCodeTokenResponseClient() - accessTokenResponseClient.setRestOperations(restTemplate()) - - return accessTokenResponseClient - } - - @Bean - fun refreshTokenAccessTokenResponseClient(): OAuth2AccessTokenResponseClient { - val accessTokenResponseClient = DefaultRefreshTokenTokenResponseClient() - accessTokenResponseClient.setRestOperations(restTemplate()) - - return accessTokenResponseClient - } - - @Bean - fun clientCredentialsAccessTokenResponseClient(): OAuth2AccessTokenResponseClient { - val accessTokenResponseClient = DefaultClientCredentialsTokenResponseClient() - accessTokenResponseClient.setRestOperations(restTemplate()) - - return accessTokenResponseClient - } - - @Bean - fun passwordAccessTokenResponseClient(): OAuth2AccessTokenResponseClient { - val accessTokenResponseClient = DefaultPasswordTokenResponseClient() - accessTokenResponseClient.setRestOperations(restTemplate()) - - return accessTokenResponseClient - } - - @Bean - fun jwtBearerAccessTokenResponseClient(): OAuth2AccessTokenResponseClient { - val accessTokenResponseClient = DefaultJwtBearerTokenResponseClient() - accessTokenResponseClient.setRestOperations(restTemplate()) - - return accessTokenResponseClient - } - - @Bean - fun restTemplate(): RestTemplate { - // ... - } - -} ----- -===== - -A default `OAuth2AuthorizedClientManager` will be published automatically by Spring Security when one is not already provided. - -[TIP] -==== -Notice that we don't need to customize the `SecurityFilterChain` bean in this case, and can stick with the defaults. -If using Spring Boot with no additional customizations, we can actually omit the `SecurityFilterChain` bean entirely. -==== - -Prior to Spring Security 6.2, we had to ensure this customization was applied to both OAuth2 Login (if we are using this feature) and OAuth2 Client components. -We had to use both the Spring Security DSL (for the `authorization_code` grant) and publish a bean of type `OAuth2AuthorizedClientManager` for other grant types. -To understand what is being configured behind the scenes, here's what the configuration might have looked like: - -.Customize `RestOperations` for OAuth2 Client (prior to 6.2) -[tabs] -===== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -@EnableWebSecurity -public class SecurityConfig { - - @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient = - new DefaultAuthorizationCodeTokenResponseClient(); - accessTokenResponseClient.setRestOperations(restTemplate()); - - http - // ... - .oauth2Login((oauth2Login) -> oauth2Login - .tokenEndpoint((tokenEndpoint) -> tokenEndpoint - .accessTokenResponseClient(accessTokenResponseClient) - ) - ) - .oauth2Client((oauth2Client) -> oauth2Client - .authorizationCodeGrant((authorizationCode) -> authorizationCode - .accessTokenResponseClient(accessTokenResponseClient) - ) - ); - - return http.build(); - } - - @Bean - public OAuth2AuthorizedClientManager authorizedClientManager( - ClientRegistrationRepository clientRegistrationRepository, - OAuth2AuthorizedClientRepository authorizedClientRepository) { - - DefaultRefreshTokenTokenResponseClient refreshTokenAccessTokenResponseClient = - new DefaultRefreshTokenTokenResponseClient(); - refreshTokenAccessTokenResponseClient.setRestOperations(restTemplate()); - - DefaultClientCredentialsTokenResponseClient clientCredentialsAccessTokenResponseClient = - new DefaultClientCredentialsTokenResponseClient(); - clientCredentialsAccessTokenResponseClient.setRestOperations(restTemplate()); - - DefaultPasswordTokenResponseClient passwordAccessTokenResponseClient = - new DefaultPasswordTokenResponseClient(); - passwordAccessTokenResponseClient.setRestOperations(restTemplate()); - - DefaultJwtBearerTokenResponseClient jwtBearerAccessTokenResponseClient = - new DefaultJwtBearerTokenResponseClient(); - jwtBearerAccessTokenResponseClient.setRestOperations(restTemplate()); - - JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = - new JwtBearerOAuth2AuthorizedClientProvider(); - jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerAccessTokenResponseClient); - - OAuth2AuthorizedClientProvider authorizedClientProvider = - OAuth2AuthorizedClientProviderBuilder.builder() - .authorizationCode() - .refreshToken((refreshToken) -> refreshToken - .accessTokenResponseClient(refreshTokenAccessTokenResponseClient) - ) - .clientCredentials((clientCredentials) -> clientCredentials - .accessTokenResponseClient(clientCredentialsAccessTokenResponseClient) - ) - .password((password) -> password - .accessTokenResponseClient(passwordAccessTokenResponseClient) - ) - .provider(jwtBearerAuthorizedClientProvider) - .build(); - - DefaultOAuth2AuthorizedClientManager authorizedClientManager = - new DefaultOAuth2AuthorizedClientManager( - clientRegistrationRepository, authorizedClientRepository); - authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); - - return authorizedClientManager; - } - - @Bean - public RestTemplate restTemplate() { - // ... - } - -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -import org.springframework.security.config.annotation.web.invoke - -@Configuration -@EnableWebSecurity -class SecurityConfig { - - @Bean - fun securityFilterChain(http: HttpSecurity): SecurityFilterChain { - val tokenResponseClient = DefaultAuthorizationCodeTokenResponseClient() - tokenResponseClient.setRestOperations(restTemplate()) - - http { - // ... - oauth2Login { - tokenEndpoint { - accessTokenResponseClient = tokenResponseClient - } - } - oauth2Client { - authorizationCodeGrant { - accessTokenResponseClient = tokenResponseClient - } - } - } - - return http.build() - } - - @Bean - fun authorizedClientManager( - clientRegistrationRepository: ClientRegistrationRepository?, - authorizedClientRepository: OAuth2AuthorizedClientRepository? - ): OAuth2AuthorizedClientManager { - val refreshTokenAccessTokenResponseClient = DefaultRefreshTokenTokenResponseClient() - refreshTokenAccessTokenResponseClient.setRestOperations(restTemplate()) - - val clientCredentialsAccessTokenResponseClient = DefaultClientCredentialsTokenResponseClient() - clientCredentialsAccessTokenResponseClient.setRestOperations(restTemplate()) - - val passwordAccessTokenResponseClient = DefaultPasswordTokenResponseClient() - passwordAccessTokenResponseClient.setRestOperations(restTemplate()) - - val jwtBearerAccessTokenResponseClient = DefaultJwtBearerTokenResponseClient() - jwtBearerAccessTokenResponseClient.setRestOperations(restTemplate()) - - val jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider() - jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerAccessTokenResponseClient) - - val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder() - .authorizationCode() - .refreshToken { refreshToken -> - refreshToken.accessTokenResponseClient(refreshTokenAccessTokenResponseClient) - } - .clientCredentials { clientCredentials -> - clientCredentials.accessTokenResponseClient(clientCredentialsAccessTokenResponseClient) - } - .password { password -> - password.accessTokenResponseClient(passwordAccessTokenResponseClient) - } - .provider(jwtBearerAuthorizedClientProvider) - .build() - - val authorizedClientManager = DefaultOAuth2AuthorizedClientManager( - clientRegistrationRepository, authorizedClientRepository - ) - authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider) - - return authorizedClientManager - } - - @Bean - fun restTemplate(): RestTemplate { - // ... - } - -} ----- -===== - - -[[further-reading]] -== Further Reading - -This preceding sections introduced Spring Security's support for OAuth2 with examples for common scenarios. -You can read more about OAuth2 Client and Resource Server in the following sections of the reference documentation: - -* xref:servlet/oauth2/login/index.adoc[] -* xref:servlet/oauth2/client/index.adoc[] -* xref:servlet/oauth2/resource-server/index.adoc[] diff --git a/docs/modules/ROOT/pages/servlet/oauth2/login/advanced.adoc b/docs/modules/ROOT/pages/servlet/oauth2/login/advanced.adoc index 9d5fa9918a..a0ee7eeced 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/login/advanced.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/login/advanced.adoc @@ -929,5 +929,111 @@ For MAC-based algorithms (such as `HS256`, `HS384`, or `HS512`), the `client-sec If more than one `ClientRegistration` is configured for OpenID Connect 1.0 Authentication, the JWS algorithm resolver may evaluate the provided `ClientRegistration` to determine which algorithm to return. ==== + [[oauth2login-advanced-oidc-logout]] -Then, you can proceed to configure xref:reactive/oauth2/login/logout.adoc[logout] +== OpenID Connect 1.0 Logout + +OpenID Connect Session Management 1.0 allows the ability to log out the end user at the Provider by using the Client. +One of the strategies available is https://openid.net/specs/openid-connect-rpinitiated-1_0.html[RP-Initiated Logout]. + +If the OpenID Provider supports both Session Management and https://openid.net/specs/openid-connect-discovery-1_0.html[Discovery], the client can obtain the `end_session_endpoint` `URL` from the OpenID Provider's https://openid.net/specs/openid-connect-session-1_0.html#OPMetadata[Discovery Metadata]. +You can do so by configuring the `ClientRegistration` with the `issuer-uri`, as follows: + +[source,yaml] +---- +spring: + security: + oauth2: + client: + registration: + okta: + client-id: okta-client-id + client-secret: okta-client-secret + ... + provider: + okta: + issuer-uri: https://dev-1234.oktapreview.com +---- + +Also, you can configure `OidcClientInitiatedLogoutSuccessHandler`, which implements RP-Initiated Logout, as follows: + +[tabs] +====== +Java:: ++ +[source,java,role="primary"] +---- +@Configuration +@EnableWebSecurity +public class OAuth2LoginSecurityConfig { + + @Autowired + private ClientRegistrationRepository clientRegistrationRepository; + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests(authorize -> authorize + .anyRequest().authenticated() + ) + .oauth2Login(withDefaults()) + .logout(logout -> logout + .logoutSuccessHandler(oidcLogoutSuccessHandler()) + ); + return http.build(); + } + + private LogoutSuccessHandler oidcLogoutSuccessHandler() { + OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler = + new OidcClientInitiatedLogoutSuccessHandler(this.clientRegistrationRepository); + + // Sets the location that the End-User's User Agent will be redirected to + // after the logout has been performed at the Provider + oidcLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}"); + + return oidcLogoutSuccessHandler; + } +} +---- + +Kotlin:: ++ +[source,kotlin,role="secondary"] +---- +@Configuration +@EnableWebSecurity +class OAuth2LoginSecurityConfig { + @Autowired + private lateinit var clientRegistrationRepository: ClientRegistrationRepository + + @Bean + open fun filterChain(http: HttpSecurity): SecurityFilterChain { + http { + authorizeRequests { + authorize(anyRequest, authenticated) + } + oauth2Login { } + logout { + logoutSuccessHandler = oidcLogoutSuccessHandler() + } + } + return http.build() + } + + private fun oidcLogoutSuccessHandler(): LogoutSuccessHandler { + val oidcLogoutSuccessHandler = OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository) + + // Sets the location that the End-User's User Agent will be redirected to + // after the logout has been performed at the Provider + oidcLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}") + return oidcLogoutSuccessHandler + } +} +---- +====== + +[NOTE] +==== +`OidcClientInitiatedLogoutSuccessHandler` supports the `+{baseUrl}+` placeholder. +If used, the application's base URL, such as `https://app.example.org`, replaces it at request time. +==== diff --git a/docs/modules/ROOT/pages/servlet/oauth2/login/logout.adoc b/docs/modules/ROOT/pages/servlet/oauth2/login/logout.adoc deleted file mode 100644 index 24078cf61f..0000000000 --- a/docs/modules/ROOT/pages/servlet/oauth2/login/logout.adoc +++ /dev/null @@ -1,267 +0,0 @@ -= OIDC Logout - -Once an end user is able to login to your application, it's important to consider how they will log out. - -Generally speaking, there are three use cases for you to consider: - -1. I want to perform only a local logout -2. I want to log out both my application and the OIDC Provider, initiated by my application -3. I want to log out both my application and the OIDC Provider, initiated by the OIDC Provider - -[[configure-local-logout]] -== Local Logout - -To perform a local logout, no special OIDC configuration is needed. -Spring Security automatically stands up a local logout endpoint, which you can xref:servlet/authentication/logout.adoc[configure through the `logout()` DSL]. - -[[configure-client-initiated-oidc-logout]] -== OpenID Connect 1.0 Client-Initiated Logout - -OpenID Connect Session Management 1.0 allows the ability to log out the end user at the Provider by using the Client. -One of the strategies available is https://openid.net/specs/openid-connect-rpinitiated-1_0.html[RP-Initiated Logout]. - -If the OpenID Provider supports both Session Management and https://openid.net/specs/openid-connect-discovery-1_0.html[Discovery], the client can obtain the `end_session_endpoint` `URL` from the OpenID Provider's https://openid.net/specs/openid-connect-session-1_0.html#OPMetadata[Discovery Metadata]. -You can do so by configuring the `ClientRegistration` with the `issuer-uri`, as follows: - -[source,yaml] ----- -spring: - security: - oauth2: - client: - registration: - okta: - client-id: okta-client-id - client-secret: okta-client-secret - ... - provider: - okta: - issuer-uri: https://dev-1234.oktapreview.com ----- - -Also, you should configure `OidcClientInitiatedLogoutSuccessHandler`, which implements RP-Initiated Logout, as follows: - -[tabs] -====== -Java:: -+ -[source,java,role="primary"] ----- -@Configuration -@EnableWebSecurity -public class OAuth2LoginSecurityConfig { - - @Autowired - private ClientRegistrationRepository clientRegistrationRepository; - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests(authorize -> authorize - .anyRequest().authenticated() - ) - .oauth2Login(withDefaults()) - .logout(logout -> logout - .logoutSuccessHandler(oidcLogoutSuccessHandler()) - ); - return http.build(); - } - - private LogoutSuccessHandler oidcLogoutSuccessHandler() { - OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler = - new OidcClientInitiatedLogoutSuccessHandler(this.clientRegistrationRepository); - - // Sets the location that the End-User's User Agent will be redirected to - // after the logout has been performed at the Provider - oidcLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}"); - - return oidcLogoutSuccessHandler; - } -} ----- - -Kotlin:: -+ -[source,kotlin,role="secondary"] ----- -@Configuration -@EnableWebSecurity -class OAuth2LoginSecurityConfig { - @Autowired - private lateinit var clientRegistrationRepository: ClientRegistrationRepository - - @Bean - open fun filterChain(http: HttpSecurity): SecurityFilterChain { - http { - authorizeHttpRequests { - authorize(anyRequest, authenticated) - } - oauth2Login { } - logout { - logoutSuccessHandler = oidcLogoutSuccessHandler() - } - } - return http.build() - } - - private fun oidcLogoutSuccessHandler(): LogoutSuccessHandler { - val oidcLogoutSuccessHandler = OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository) - - // Sets the location that the End-User's User Agent will be redirected to - // after the logout has been performed at the Provider - oidcLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}") - return oidcLogoutSuccessHandler - } -} ----- -====== - -[NOTE] -==== -`OidcClientInitiatedLogoutSuccessHandler` supports the `+{baseUrl}+` placeholder. -If used, the application's base URL, such as `https://app.example.org`, replaces it at request time. -==== - -[[configure-provider-initiated-oidc-logout]] -== OpenID Connect 1.0 Back-Channel Logout - -OpenID Connect Session Management 1.0 allows the ability to log out the end user at the Client by having the Provider make an API call to the Client. -This is referred to as https://openid.net/specs/openid-connect-backchannel-1_0.html[OIDC Back-Channel Logout]. - -To enable this, you can stand up the Back-Channel Logout endpoint in the DSL like so: - -[tabs] -====== -Java:: -+ -[source=java,role="primary"] ----- -@Bean -public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http - .authorizeHttpRequests((authorize) -> authorize - .anyRequest().authenticated() - ) - .oauth2Login(withDefaults()) - .oidcLogout((logout) -> logout - .backChannel(Customizer.withDefaults()) - ); - return http.build(); -} ----- - -Kotlin:: -+ -[source=kotlin,role="secondary"] ----- -@Bean -open fun filterChain(http: HttpSecurity): SecurityFilterChain { - http { - authorizeRequests { - authorize(anyRequest, authenticated) - } - oauth2Login { } - oidcLogout { - backChannel { } - } - } - return http.build() -} ----- -====== - -And that's it! - -This will stand up the endpoint `+/logout/connect/back-channel/{registrationId}+` which the OIDC Provider can request to invalidate a given session of an end user in your application. - -[NOTE] -`oidcLogout` requires that `oauth2Login` also be configured. - -[NOTE] -`oidcLogout` requires that the session cookie be called `JSESSIONID` in order to correctly log out each session through a backchannel. - -=== Back-Channel Logout Architecture - -Consider a `ClientRegistration` whose identifier is `registrationId`. - -The overall flow for a Back-Channel logout is like this: - -1. At login time, Spring Security correlates the ID Token, CSRF Token, and Provider Session ID (if any) to your application's session id in its `OidcSessionStrategy` implementation. -2. Then at logout time, your OIDC Provider makes an API call to `/logout/connect/back-channel/registrationId` including a Logout Token that indicates either the `sub` (the End User) or the `sid` (the Provider Session ID) to logout. -3. Spring Security validates the token's signature and claims. -4. If the token contains a `sid` claim, then only the Client's session that correlates to that provider session is terminated. -5. Otherwise, if the token contains a `sub` claim, then all that Client's sessions for that End User are terminated. - -[NOTE] -Remember that Spring Security's OIDC support is multi-tenant. -This means that it will only terminate sessions whose Client matches the `aud` claim in the Logout Token. - -=== Customizing the OIDC Provider Session Strategy - -By default, Spring Security stores in-memory all links between the OIDC Provider session and the Client session. - -There are a number of circumstances, like a clustered application, where it would be nice to store this instead in a separate location, like a database. - -You can achieve this by configuring a custom `OidcSessionStrategy`, like so: - -[tabs] -====== -Java:: -+ -[source=java,role="primary"] ----- -@Component -public final class MySpringDataOidcSessionStrategy implements OidcSessionStrategy { - private final OidcProviderSessionRepository sessions; - - // ... - - @Override - public void saveSessionInformation(OidcSessionInformation info) { - this.sessions.save(info); - } - - @Override - public OidcSessionInformation(String clientSessionId) { - return this.sessions.removeByClientSessionId(clientSessionId); - } - - @Override - public Iterable removeSessionInformation(OidcLogoutToken token) { - return token.getSessionId() != null ? - this.sessions.removeBySessionIdAndIssuerAndAudience(...) : - this.sessions.removeBySubjectAndIssuerAndAudience(...); - } -} ----- - -Kotlin:: -+ -[source=kotlin,role="secondary"] ----- -@Component -class MySpringDataOidcSessionStrategy: OidcSessionStrategy { - val sessions: OidcProviderSessionRepository - - // ... - - @Override - fun saveSessionInformation(info: OidcSessionInformation) { - this.sessions.save(info) - } - - @Override - fun removeSessionInformation(clientSessionId: String): OidcSessionInformation { - return this.sessions.removeByClientSessionId(clientSessionId); - } - - @Override - fun removeSessionInformation(token: OidcLogoutToken): Iterable { - return token.getSessionId() != null ? - this.sessions.removeBySessionIdAndIssuerAndAudience(...) : - this.sessions.removeBySubjectAndIssuerAndAudience(...); - } -} ----- -====== - diff --git a/docs/modules/ROOT/pages/servlet/oauth2/resource-server/jwt.adoc b/docs/modules/ROOT/pages/servlet/oauth2/resource-server/jwt.adoc index 5353163642..0c03d4ecd6 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/resource-server/jwt.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/resource-server/jwt.adoc @@ -211,8 +211,6 @@ Java:: + [source,java,role="primary"] ---- -import static org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope; - @Configuration @EnableWebSecurity public class MyCustomSecurityConfiguration { @@ -220,7 +218,7 @@ public class MyCustomSecurityConfiguration { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize -> authorize - .requestMatchers("/messages/**").access(hasScope("message:read")) + .requestMatchers("/messages/**").hasAuthority("SCOPE_message:read") .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 @@ -237,8 +235,6 @@ Kotlin:: + [source,kotlin,role="secondary"] ---- -import org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope - @Configuration @EnableWebSecurity class MyCustomSecurityConfiguration { @@ -246,7 +242,7 @@ class MyCustomSecurityConfiguration { open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { authorizeRequests { - authorize("/messages/**", hasScope("message:read")) + authorize("/messages/**", hasAuthority("SCOPE_message:read")) authorize(anyRequest, authenticated) } oauth2ResourceServer { @@ -866,8 +862,6 @@ Java:: + [source,java,role="primary"] ---- -import static org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope; - @Configuration @EnableWebSecurity public class DirectlyConfiguredJwkSetUri { @@ -875,8 +869,8 @@ public class DirectlyConfiguredJwkSetUri { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize -> authorize - .requestMatchers("/contacts/**").access(hasScope("contacts")) - .requestMatchers("/messages/**").access(hasScope("messages")) + .requestMatchers("/contacts/**").hasAuthority("SCOPE_contacts") + .requestMatchers("/messages/**").hasAuthority("SCOPE_messages") .anyRequest().authenticated() ) .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt); @@ -889,8 +883,6 @@ Kotlin:: + [source,kotlin,role="secondary"] ---- -import org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope; - @Configuration @EnableWebSecurity class DirectlyConfiguredJwkSetUri { @@ -898,8 +890,8 @@ class DirectlyConfiguredJwkSetUri { open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { authorizeRequests { - authorize("/contacts/**", hasScope("contacts")) - authorize("/messages/**", hasScope("messages")) + authorize("/contacts/**", hasAuthority("SCOPE_contacts")) + authorize("/messages/**", hasAuthority("SCOPE_messages")) authorize(anyRequest, authenticated) } oauth2ResourceServer { diff --git a/docs/modules/ROOT/pages/servlet/oauth2/resource-server/multitenancy.adoc b/docs/modules/ROOT/pages/servlet/oauth2/resource-server/multitenancy.adoc index c13abb7704..0cdc14aee8 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/resource-server/multitenancy.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/resource-server/multitenancy.adoc @@ -114,8 +114,8 @@ Java:: + [source,java,role="primary"] ---- -JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver - .fromTrustedIssuers("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo"); +JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver + ("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo"); http .authorizeHttpRequests(authorize -> authorize @@ -131,7 +131,7 @@ Kotlin:: [source,kotlin,role="secondary"] ---- val customAuthenticationManagerResolver = JwtIssuerAuthenticationManagerResolver - .fromTrustedIssuers("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo") + ("https://idp.example.org/issuerOne", "https://idp.example.org/issuerTwo") http { authorizeRequests { authorize(anyRequest, authenticated) diff --git a/docs/modules/ROOT/pages/servlet/oauth2/resource-server/opaque-token.adoc b/docs/modules/ROOT/pages/servlet/oauth2/resource-server/opaque-token.adoc index fcd3b4f2c4..45a6f20be9 100644 --- a/docs/modules/ROOT/pages/servlet/oauth2/resource-server/opaque-token.adoc +++ b/docs/modules/ROOT/pages/servlet/oauth2/resource-server/opaque-token.adoc @@ -239,8 +239,6 @@ Java:: + [source,java,role="primary"] ---- -import static org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope; - @Configuration @EnableWebSecurity public class MyCustomSecurityConfiguration { @@ -248,7 +246,7 @@ public class MyCustomSecurityConfiguration { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize -> authorize - .requestMatchers("/messages/**").access(hasScope("message:read")) + .requestMatchers("/messages/**").hasAuthority("SCOPE_message:read") .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 @@ -265,8 +263,6 @@ Kotlin:: + [source,kotlin,role="secondary"] ---- -import org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope; - @Configuration @EnableWebSecurity class MyCustomSecurityConfiguration { @@ -274,7 +270,7 @@ class MyCustomSecurityConfiguration { open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { authorizeRequests { - authorize("/messages/**", hasScope("SCOPE_message:read")) + authorize("/messages/**", hasAuthority("SCOPE_message:read")) authorize(anyRequest, authenticated) } oauth2ResourceServer { @@ -551,8 +547,6 @@ Java:: + [source,java,role="primary"] ---- -import static org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope; - @Configuration @EnableWebSecurity public class MappedAuthorities { @@ -560,8 +554,8 @@ public class MappedAuthorities { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorizeRequests -> authorizeRequests - .requestMatchers("/contacts/**").access(hasScope("contacts")) - .requestMatchers("/messages/**").access(hasScope("messages")) + .requestMatchers("/contacts/**").hasAuthority("SCOPE_contacts") + .requestMatchers("/messages/**").hasAuthority("SCOPE_messages") .anyRequest().authenticated() ) .oauth2ResourceServer(OAuth2ResourceServerConfigurer::opaqueToken); @@ -574,8 +568,6 @@ Kotlin:: + [source,kotlin,role="secondary"] ---- -import org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope - @Configuration @EnableWebSecurity class MappedAuthorities { @@ -583,8 +575,8 @@ class MappedAuthorities { open fun filterChain(http: HttpSecurity): SecurityFilterChain { http { authorizeRequests { - authorize("/contacts/**", hasScope("contacts")) - authorize("/messages/**", hasScope("messages")) + authorize("/contacts/**", hasAuthority("SCOPE_contacts")) + authorize("/messages/**", hasAuthority("SCOPE_messages")) authorize(anyRequest, authenticated) } oauth2ResourceServer { diff --git a/docs/modules/ROOT/pages/servlet/test/mockmvc/request-builders.adoc b/docs/modules/ROOT/pages/servlet/test/mockmvc/request-builders.adoc index f62ddd79c1..ebbf07c68d 100644 --- a/docs/modules/ROOT/pages/servlet/test/mockmvc/request-builders.adoc +++ b/docs/modules/ROOT/pages/servlet/test/mockmvc/request-builders.adoc @@ -1,4 +1,4 @@ -= SecurityMockMvcRequestBuilders +== SecurityMockMvcRequestBuilders Spring MVC Test also provides a `RequestBuilder` interface that can be used to create the `MockHttpServletRequest` used in your test. Spring Security provides a few `RequestBuilder` implementations that can be used to make testing easier. diff --git a/docs/modules/ROOT/pages/servlet/test/mockmvc/result-handlers.adoc b/docs/modules/ROOT/pages/servlet/test/mockmvc/result-handlers.adoc index 778469106d..48c767f468 100644 --- a/docs/modules/ROOT/pages/servlet/test/mockmvc/result-handlers.adoc +++ b/docs/modules/ROOT/pages/servlet/test/mockmvc/result-handlers.adoc @@ -1,4 +1,4 @@ -= SecurityMockMvcResultHandlers +=== SecurityMockMvcResultHandlers Spring Security provides a few ``ResultHandler``s implementations. In order to use Spring Security's ``ResultHandler``s implementations ensure the following static import is used: @@ -8,7 +8,7 @@ In order to use Spring Security's ``ResultHandler``s implementations ensure the import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultHandlers.*; ---- -== Exporting the SecurityContext +==== Exporting the SecurityContext Often times we want to query a repository to see if some `MockMvc` request actually persisted in the database. In some cases our repository query uses the xref:features/integrations/data.adoc[Spring Data Integration] to filter the results based on current user's username or any other property. diff --git a/docs/modules/ROOT/pages/servlet/test/mockmvc/result-matchers.adoc b/docs/modules/ROOT/pages/servlet/test/mockmvc/result-matchers.adoc index 40a2a69d48..8571418584 100644 --- a/docs/modules/ROOT/pages/servlet/test/mockmvc/result-matchers.adoc +++ b/docs/modules/ROOT/pages/servlet/test/mockmvc/result-matchers.adoc @@ -1,4 +1,4 @@ -= SecurityMockMvcResultMatchers +== SecurityMockMvcResultMatchers At times it is desirable to make various security related assertions about a request. To accommodate this need, Spring Security Test support implements Spring MVC Test's `ResultMatcher` interface. @@ -22,7 +22,7 @@ import org.springframework.security.test.web.servlet.response.SecurityMockMvcRes ---- ====== -== Unauthenticated Assertion +=== Unauthenticated Assertion At times it may be valuable to assert that there is no authenticated user associated with the result of a `MockMvc` invocation. For example, you might want to test submitting an invalid username and password and verify that no user is authenticated. @@ -49,7 +49,7 @@ mvc ---- ====== -== Authenticated Assertion +=== Authenticated Assertion It is often times that we must assert that an authenticated user exists. For example, we may want to verify that we authenticated successfully. diff --git a/docs/modules/ROOT/pages/whats-new.adoc b/docs/modules/ROOT/pages/whats-new.adoc index ebcc9eda06..1e711ef5f3 100644 --- a/docs/modules/ROOT/pages/whats-new.adoc +++ b/docs/modules/ROOT/pages/whats-new.adoc @@ -1,13 +1,43 @@ [[new]] -= What's New in Spring Security 6.2 += What's New in Spring Security 6.1 -Spring Security 6.2 provides a number of new features. +Spring Security 6.1 provides a number of new features. Below are the highlights of the release. -== Configuration +== Core -* https://github.com/spring-projects/spring-security/issues/5011[gh-5011] - xref:servlet/integrations/cors.adoc[(docs)] Automatically enable `.cors()` if `CorsConfigurationSource` bean is present -* https://github.com/spring-projects/spring-security/issues/13204[gh-13204] - xref:migration-7/configuration.adoc#_use_with_instead_of_apply_for_custom_dsls[(docs)] Add `AbstractConfiguredSecurityBuilder.with(...)` method to apply configurers returning the builder -* https://github.com/spring-projects/spring-security/pull/13587[gh-13587] - https://spring.io/blog/2023/08/22/tackling-the-oauth2-client-component-model-in-spring-security/[blog post] Simplify configuration of OAuth2 Client component model -* https://github.com/spring-projects/spring-security/issues/7845[gh-7845] - xref:reactive/oauth2/login/logout.adoc#configure-provider-initiated-oidc-logout[docs] Add OIDC Back-channel Logout Support -* https://github.com/spring-projects/spring-security/pull/13857[gh-13857] - xref:servlet/authorization/authorize-http-requests.adoc#match-by-mvc[docs] Add servlet pattern support to AuthorizeHttpRequests +* https://github.com/spring-projects/spring-security/issues/12233[gh-12233] - SecuredAuthorizationManager allows customizing underlying AuthorizationManager +* https://github.com/spring-projects/spring-security/issues/12231[gh-12231] - Add Authority Collection Authorization Manager + +== OAuth 2.0 + +* https://github.com/spring-projects/spring-security/issues/10309[gh-10309] - xref:servlet/oauth2/resource-server/jwt.adoc[(docs)] - Add Nimbus(Reactive)JwtDecoder#withIssuerLocation +* https://github.com/spring-projects/spring-security/issues/12907[gh-12907] - Configure principal claim name in ReactiveJwtAuthenticationConverter + +== SAML 2.0 + +* https://github.com/spring-projects/spring-security/issues/12604[gh-12604] - Support AuthnRequestSigned metadata attribute +* https://github.com/spring-projects/spring-security/issues/12846[gh-12846] - Metadata supports multiple entities and EntitiesDescriptor +* https://github.com/spring-projects/spring-security/issues/11828[gh-11828] - xref:servlet/saml2/metadata.adoc[(docs)] - Add saml2Metadata to DSL +* https://github.com/spring-projects/spring-security/issues/12843[gh-12843] - xref:servlet/saml2/logout.adoc[(docs)] - Allow Relying Party to be Deduced from LogoutRequest +* https://github.com/spring-projects/spring-security/issues/10243[gh-10243] - xref:servlet/saml2/login/authentication.adoc[(docs)] - Allow Relying Party to be Deduced from SAML Response +* https://github.com/spring-projects/spring-security/issues/12842[gh-12842] - Add RelyingPartyRegistration placeholder resolution component +* https://github.com/spring-projects/spring-security/issues/12845[gh-12845] - Support issuing LogoutResponse after already logged out + +== Observability + +* https://github.com/spring-projects/spring-security/issues/12534[gh-12534] - Customize Authentication and Authorization observation conventions + +== Web + +* https://github.com/spring-projects/spring-security/issues/12751[gh-12751] - Add RequestMatchers factory class +* https://github.com/spring-projects/spring-security/issues/12847[gh-12847] - Propagate variables through And and OrRequestMatcher + +== Docs + +In our ongoing efforts to update Spring Security's documentation, several additional sections were fully re-written: + +* https://github.com/spring-projects/spring-security/issues/13088[gh-13088] - xref:servlet/authorization/index.adoc[(docs)] - Revisit Authorization documentation +* https://github.com/spring-projects/spring-security/issues/12681[gh-12681] - xref:servlet/authentication/session-management.adoc[(docs)] - Revisit Session Management documentation +* https://github.com/spring-projects/spring-security/issues/13062[gh-13062] - xref:servlet/authentication/logout.adoc[(docs)] - Revisit Logout documentation +* https://github.com/spring-projects/spring-security/issues/13089[gh-13089] - Revisit CSRF Documentation \ No newline at end of file diff --git a/etc/nohttp/allowlist.lines b/etc/nohttp/allowlist.lines index 330ed0f5ce..a378625640 100644 --- a/etc/nohttp/allowlist.lines +++ b/etc/nohttp/allowlist.lines @@ -10,5 +10,4 @@ ^http://www.w3.org/2001/04/xmlenc ^http://www.springframework.org/schema/security/.* ^http://openoffice.org/.* -^http://www.w3.org/2003/g/data-view -^http://schemas.openid.net/event/backchannel-logout +^http://www.w3.org/2003/g/data-view \ No newline at end of file diff --git a/git/hooks/.forward-merge.swp b/git/hooks/.forward-merge.swp deleted file mode 100644 index 9fb46808e5..0000000000 Binary files a/git/hooks/.forward-merge.swp and /dev/null differ diff --git a/git/hooks/prepare-forward-merge b/git/hooks/prepare-forward-merge index 19cdafe970..20554c504a 100755 --- a/git/hooks/prepare-forward-merge +++ b/git/hooks/prepare-forward-merge @@ -4,7 +4,7 @@ require 'net/http' require 'yaml' require 'logger' -$main_branch = "6.2.x" +$main_branch = "6.1.x" $log = Logger.new(STDOUT) $log.level = Logger::WARN diff --git a/gradle.properties b/gradle.properties index 32dcbc7d09..7499fe67b2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ springBootVersion=3.1.1 -version=6.2.0-SNAPSHOT +version=6.1.6-SNAPSHOT samplesBranch=main org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError org.gradle.parallel=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d2a5b8a271..5cc77d10e7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,19 +5,19 @@ io-spring-javaformat = "0.0.39" io-spring-nohttp = "0.0.11" jakarta-websocket = "2.1.1" org-apache-directory-server = "1.5.5" -org-apache-maven-resolver = "1.9.16" +org-apache-maven-resolver = "1.8.2" org-aspectj = "1.9.20.1" org-bouncycastle = "1.70" -org-eclipse-jetty = "11.0.18" -org-jetbrains-kotlin = "1.9.20" -org-jetbrains-kotlinx = "1.7.3" -org-mockito = "5.5.0" -org-opensaml = "4.3.0" -org-springframework = "6.1.0-RC2" +org-eclipse-jetty = "11.0.17" +org-jetbrains-kotlin = "1.8.22" +org-jetbrains-kotlinx = "1.6.4" +org-mockito = "4.8.1" +org-opensaml = "4.1.1" +org-springframework = "6.0.13" [libraries] ch-qos-logback-logback-classic = "ch.qos.logback:logback-classic:1.4.11" -com-fasterxml-jackson-jackson-bom = "com.fasterxml.jackson:jackson-bom:2.15.3" +com-fasterxml-jackson-jackson-bom = "com.fasterxml.jackson:jackson-bom:2.14.3" com-google-inject-guice = "com.google.inject:guice:3.0" com-netflix-nebula-nebula-project-plugin = "com.netflix.nebula:nebula-project-plugin:8.2.0" com-nimbusds-nimbus-jose-jwt = "com.nimbusds:nimbus-jose-jwt:9.24.4" @@ -26,9 +26,10 @@ com-squareup-okhttp3-mockwebserver = { module = "com.squareup.okhttp3:mockwebser com-squareup-okhttp3-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "com-squareup-okhttp3" } com-unboundid-unboundid-ldapsdk = "com.unboundid:unboundid-ldapsdk:6.0.10" commons-collections = "commons-collections:commons-collections:3.2.2" -io-micrometer-micrometer-observation = "io.micrometer:micrometer-observation:1.12.0-RC1" +io-freefair-gradle-aspectj-plugin = "io.freefair.gradle:aspectj-plugin:6.6.3" +io-micrometer-micrometer-observation = "io.micrometer:micrometer-observation:1.10.12" io-mockk = "io.mockk:mockk:1.13.8" -io-projectreactor-reactor-bom = "io.projectreactor:reactor-bom:2023.0.0-RC1" +io-projectreactor-reactor-bom = "io.projectreactor:reactor-bom:2022.0.12" io-rsocket-rsocket-bom = { module = "io.rsocket:rsocket-bom", version.ref = "io-rsocket" } io-spring-javaformat-spring-javaformat-checkstyle = { module = "io.spring.javaformat:spring-javaformat-checkstyle", version.ref = "io-spring-javaformat" } io-spring-javaformat-spring-javaformat-gradle-plugin = { module = "io.spring.javaformat:spring-javaformat-gradle-plugin", version.ref = "io-spring-javaformat" } @@ -44,7 +45,7 @@ jakarta-websocket-jakarta-websocket-api = { module = "jakarta.websocket:jakarta. jakarta-websocket-jakarta-websocket-client-api = { module = "jakarta.websocket:jakarta.websocket-client-api", version.ref = "jakarta-websocket" } jakarta-xml-bind-jakarta-xml-bind-api = "jakarta.xml.bind:jakarta.xml.bind-api:4.0.1" ldapsdk = "ldapsdk:ldapsdk:4.1" -net-sourceforge-htmlunit = "net.sourceforge.htmlunit:htmlunit:2.70.0" +net-sourceforge-htmlunit = "net.sourceforge.htmlunit:htmlunit:2.66.0" org-apache-directory-server-apacheds-core = { module = "org.apache.directory.server:apacheds-core", version.ref = "org-apache-directory-server" } org-apache-directory-server-apacheds-entry = { module = "org.apache.directory.server:apacheds-core-entry", version.ref = "org-apache-directory-server" } org-apache-directory-server-apacheds-protocol-ldap = { module = "org.apache.directory.server:apacheds-protocol-ldap", version.ref = "org-apache-directory-server" } @@ -52,12 +53,11 @@ org-apache-directory-server-apacheds-protocol-shared = { module = "org.apache.di org-apache-directory-server-apacheds-server-jndi = { module = "org.apache.directory.server:apacheds-server-jndi", version.ref = "org-apache-directory-server" } org-apache-directory-shared-shared-ldap = "org.apache.directory.shared:shared-ldap:0.9.15" org-apache-httpcomponents-httpclient = "org.apache.httpcomponents:httpclient:4.5.14" -org-apache-maven-maven-resolver-provider = "org.apache.maven:maven-resolver-provider:3.9.5" +org-apache-maven-maven-resolver-provider = "org.apache.maven:maven-resolver-provider:3.8.8" org-apache-maven-resolver-maven-resolver-connector-basic = { module = "org.apache.maven.resolver:maven-resolver-connector-basic", version.ref = "org-apache-maven-resolver" } org-apache-maven-resolver-maven-resolver-impl = { module = "org.apache.maven.resolver:maven-resolver-impl", version.ref = "org-apache-maven-resolver" } org-apache-maven-resolver-maven-resolver-transport-http = { module = "org.apache.maven.resolver:maven-resolver-transport-http", version.ref = "org-apache-maven-resolver" } org-apereo-cas-client-cas-client-core = "org.apereo.cas.client:cas-client-core:4.0.3" -io-freefair-gradle-aspectj-plugin = "io.freefair.gradle:aspectj-plugin:8.4" org-aspectj-aspectjrt = { module = "org.aspectj:aspectjrt", version.ref = "org-aspectj" } org-aspectj-aspectjweaver = { module = "org.aspectj:aspectjweaver", version.ref = "org-aspectj" } org-assertj-assertj-core = "org.assertj:assertj-core:3.24.2" @@ -66,25 +66,25 @@ org-bouncycastle-bcprov-jdk15on = { module = "org.bouncycastle:bcprov-jdk15on", org-eclipse-jetty-jetty-server = { module = "org.eclipse.jetty:jetty-server", version.ref = "org-eclipse-jetty" } org-eclipse-jetty-jetty-servlet = { module = "org.eclipse.jetty:jetty-servlet", version.ref = "org-eclipse-jetty" } org-hamcrest = "org.hamcrest:hamcrest:2.2" -org-hibernate-orm-hibernate-core = "org.hibernate.orm:hibernate-core:6.3.1.Final" +org-hibernate-orm-hibernate-core = "org.hibernate.orm:hibernate-core:6.1.7.Final" org-hsqldb = "org.hsqldb:hsqldb:2.7.2" org-jetbrains-kotlin-kotlin-bom = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "org-jetbrains-kotlin" } -org-jetbrains-kotlin-kotlin-gradle-plugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.20" +org-jetbrains-kotlin-kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "org-jetbrains-kotlin" } org-jetbrains-kotlinx-kotlinx-coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version.ref = "org-jetbrains-kotlinx" } -org-junit-junit-bom = "org.junit:junit-bom:5.10.0" +org-junit-junit-bom = "org.junit:junit-bom:5.9.3" org-mockito-mockito-bom = { module = "org.mockito:mockito-bom", version.ref = "org-mockito" } org-opensaml-opensaml-core = { module = "org.opensaml:opensaml-core", version.ref = "org-opensaml" } org-opensaml-opensaml-saml-api = { module = "org.opensaml:opensaml-saml-api", version.ref = "org-opensaml" } org-opensaml-opensaml-saml-impl = { module = "org.opensaml:opensaml-saml-impl", version.ref = "org-opensaml" } -org-python-jython = { module = "org.python:jython", version = "2.5.3" } -org-seleniumhq-selenium-htmlunit-driver = "org.seleniumhq.selenium:htmlunit-driver:2.70.0" +org-python-jython = "org.python:jython:2.5.3" +org-seleniumhq-selenium-htmlunit-driver = "org.seleniumhq.selenium:htmlunit-driver:2.66.0" org-seleniumhq-selenium-selenium-java = "org.seleniumhq.selenium:selenium-java:3.141.59" org-seleniumhq-selenium-selenium-support = "org.seleniumhq.selenium:selenium-support:3.141.59" org-skyscreamer-jsonassert = "org.skyscreamer:jsonassert:1.5.1" org-slf4j-log4j-over-slf4j = "org.slf4j:log4j-over-slf4j:1.7.36" org-slf4j-slf4j-api = "org.slf4j:slf4j-api:2.0.9" -org-springframework-data-spring-data-bom = "org.springframework.data:spring-data-bom:2023.1.0-RC1" -org-springframework-ldap-spring-ldap-core = "org.springframework.ldap:spring-ldap-core:3.2.0-RC1" +org-springframework-data-spring-data-bom = "org.springframework.data:spring-data-bom:2022.0.11" +org-springframework-ldap-spring-ldap-core = "org.springframework.ldap:spring-ldap-core:3.0.6" org-springframework-spring-framework-bom = { module = "org.springframework:spring-framework-bom", version.ref = "org-springframework" } org-synchronoss-cloud-nio-multipart-parser = "org.synchronoss.cloud:nio-multipart-parser:1.1.0" @@ -103,5 +103,3 @@ org-jfrog-buildinfo-build-info-extractor-gradle = "org.jfrog.buildinfo:build-inf org-sonarsource-scanner-gradle-sonarqube-gradle-plugin = "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1" [plugins] - -org-gradle-wrapper-upgrade = "org.gradle.wrapper-upgrade:0.11.4" diff --git a/itest/context/src/main/java/org/springframework/security/integration/python/PythonInterpreterPreInvocationAdvice.java b/itest/context/src/main/java/org/springframework/security/integration/python/PythonInterpreterPreInvocationAdvice.java index e7b4b8dbc8..475e778237 100644 --- a/itest/context/src/main/java/org/springframework/security/integration/python/PythonInterpreterPreInvocationAdvice.java +++ b/itest/context/src/main/java/org/springframework/security/integration/python/PythonInterpreterPreInvocationAdvice.java @@ -26,8 +26,8 @@ import org.python.core.Py; import org.python.core.PyObject; import org.python.util.PythonInterpreter; +import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.ParameterNameDiscoverer; -import org.springframework.core.StandardReflectionParameterNameDiscoverer; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.security.access.prepost.PreInvocationAttribute; @@ -37,7 +37,7 @@ import org.springframework.util.ClassUtils; public class PythonInterpreterPreInvocationAdvice implements PreInvocationAuthorizationAdvice { - private final ParameterNameDiscoverer parameterNameDiscoverer = new StandardReflectionParameterNameDiscoverer(); + private final ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); @Override public boolean before(Authentication authentication, MethodInvocation mi, PreInvocationAttribute preAttr) { diff --git a/ldap/src/integration-test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateITests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateITests.java index 5b7ca9f69f..033b70d2eb 100644 --- a/ldap/src/integration-test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateITests.java +++ b/ldap/src/integration-test/java/org/springframework/security/ldap/SpringSecurityLdapTemplateITests.java @@ -104,9 +104,9 @@ public class SpringSecurityLdapTemplateITests { new String[] { param }, "ou"); assertThat(values).as("Expected 3 results from search").hasSize(3); - assertThat(values).contains("developer"); - assertThat(values).contains("manager"); - assertThat(values).contains("submanager"); + assertThat(values.contains("developer")).isTrue(); + assertThat(values.contains("manager")).isTrue(); + assertThat(values.contains("submanager")).isTrue(); } @Test @@ -149,7 +149,7 @@ public class SpringSecurityLdapTemplateITests { } protected void assertAttributeValue(Map> record, String attributeName, String... values) { - assertThat(record).containsKey(attributeName); + assertThat(record.containsKey(attributeName)).isTrue(); assertThat(record.get(attributeName)).hasSize(values.length); for (int i = 0; i < values.length; i++) { assertThat(record.get(attributeName).get(i)).isEqualTo(values[i]); diff --git a/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorGetGrantedAuthoritiesTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorGetGrantedAuthoritiesTests.java index 95181b7651..9321ef16c6 100644 --- a/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorGetGrantedAuthoritiesTests.java +++ b/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorGetGrantedAuthoritiesTests.java @@ -72,8 +72,8 @@ public class DefaultLdapAuthoritiesPopulatorGetGrantedAuthoritiesTests { assertThat(authorities).as("Should have 1 role").hasSize(2); - assertThat(authorities).contains("ROLE_DEVELOPER"); - assertThat(authorities).contains("ROLE_"); + assertThat(authorities.contains("ROLE_DEVELOPER")).isTrue(); + assertThat(authorities.contains("ROLE_")).isTrue(); } @Configuration diff --git a/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java index 1d6e2d6664..cef1e20c08 100644 --- a/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java +++ b/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/DefaultLdapAuthoritiesPopulatorTests.java @@ -68,7 +68,7 @@ public class DefaultLdapAuthoritiesPopulatorTests { Collection authorities = this.populator.getGrantedAuthorities(ctx, "notfound"); assertThat(authorities).hasSize(1); - assertThat(AuthorityUtils.authorityListToSet(authorities)).contains("ROLE_USER"); + assertThat(AuthorityUtils.authorityListToSet(authorities).contains("ROLE_USER")).isTrue(); } @Test @@ -79,7 +79,7 @@ public class DefaultLdapAuthoritiesPopulatorTests { Collection authorities = this.populator .getGrantedAuthorities(new DirContextAdapter(new DistinguishedName("cn=notused")), "notused"); assertThat(authorities).hasSize(1); - assertThat(AuthorityUtils.authorityListToSet(authorities)).contains("ROLE_USER"); + assertThat(AuthorityUtils.authorityListToSet(authorities).contains("ROLE_USER")).isTrue(); } @Test @@ -98,8 +98,8 @@ public class DefaultLdapAuthoritiesPopulatorTests { assertThat(authorities).as("Should have 2 roles").hasSize(2); - assertThat(authorities).contains("ROLE_DEVELOPER"); - assertThat(authorities).contains("ROLE_MANAGER"); + assertThat(authorities.contains("ROLE_DEVELOPER")).isTrue(); + assertThat(authorities.contains("ROLE_MANAGER")).isTrue(); } @Test @@ -115,7 +115,7 @@ public class DefaultLdapAuthoritiesPopulatorTests { .authorityListToSet(this.populator.getGrantedAuthorities(ctx, "manager")); assertThat(authorities).as("Should have 1 role").hasSize(1); - assertThat(authorities).contains("ROLE_MANAGER"); + assertThat(authorities.contains("ROLE_MANAGER")).isTrue(); } @Test @@ -130,8 +130,8 @@ public class DefaultLdapAuthoritiesPopulatorTests { .authorityListToSet(this.populator.getGrantedAuthorities(ctx, "manager")); assertThat(authorities).as("Should have 2 roles").hasSize(2); - assertThat(authorities).contains("ROLE_MANAGER"); - assertThat(authorities).contains("ROLE_DEVELOPER"); + assertThat(authorities.contains("ROLE_MANAGER")).isTrue(); + assertThat(authorities.contains("ROLE_DEVELOPER")).isTrue(); } @Test @@ -147,9 +147,9 @@ public class DefaultLdapAuthoritiesPopulatorTests { .authorityListToSet(this.populator.getGrantedAuthorities(ctx, "manager")); assertThat(authorities).as("Should have 3 roles").hasSize(3); - assertThat(authorities).contains("ROLE_MANAGER"); - assertThat(authorities).contains("ROLE_SUBMANAGER"); - assertThat(authorities).contains("ROLE_DEVELOPER"); + assertThat(authorities.contains("ROLE_MANAGER")).isTrue(); + assertThat(authorities.contains("ROLE_SUBMANAGER")).isTrue(); + assertThat(authorities.contains("ROLE_DEVELOPER")).isTrue(); } @Test @@ -164,7 +164,7 @@ public class DefaultLdapAuthoritiesPopulatorTests { Collection authorities = this.populator .getGrantedAuthorities(new DirContextAdapter(new DistinguishedName("cn=notused")), "notused"); assertThat(authorities).hasSize(1); - assertThat(AuthorityUtils.authorityListToSet(authorities)).contains("ROLE_EXTRA"); + assertThat(AuthorityUtils.authorityListToSet(authorities).contains("ROLE_EXTRA")).isTrue(); } @Test @@ -180,7 +180,7 @@ public class DefaultLdapAuthoritiesPopulatorTests { .authorityListToSet(this.populator.getGrantedAuthorities(ctx, "notused")); assertThat(authorities).as("Should have 1 role").hasSize(1); - assertThat(authorities).contains("ROLE_MANAGER"); + assertThat(authorities.contains("ROLE_MANAGER")).isTrue(); } @Test diff --git a/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/NestedLdapAuthoritiesPopulatorTests.java b/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/NestedLdapAuthoritiesPopulatorTests.java index aec53f235c..beed1a45ba 100644 --- a/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/NestedLdapAuthoritiesPopulatorTests.java +++ b/ldap/src/integration-test/java/org/springframework/security/ldap/userdetails/NestedLdapAuthoritiesPopulatorTests.java @@ -128,14 +128,14 @@ public class NestedLdapAuthoritiesPopulatorTests { LdapAuthority[] ldapAuthorities = authorities.toArray(new LdapAuthority[0]); assertThat(ldapAuthorities).hasSize(5); // groovy-developers group - assertThat(ldapAuthorities[0].getAttributes()).containsKey("member"); + assertThat(ldapAuthorities[0].getAttributes().containsKey("member")).isTrue(); assertThat(ldapAuthorities[0].getAttributes().get("member")).isNotNull(); assertThat(ldapAuthorities[0].getAttributes().get("member")).hasSize(3); assertThat(ldapAuthorities[0].getFirstAttributeValue("member")) .isEqualTo("cn=groovy-developers,ou=jdeveloper,dc=springframework,dc=org"); // java group - assertThat(ldapAuthorities[1].getAttributes()).containsKey("member"); + assertThat(ldapAuthorities[1].getAttributes().containsKey("member")).isTrue(); assertThat(ldapAuthorities[1].getAttributes().get("member")).isNotNull(); assertThat(ldapAuthorities[1].getAttributes().get("member")).hasSize(3); assertThat(this.groovyDevelopers.getDn()).isEqualTo(ldapAuthorities[1].getFirstAttributeValue("member")); diff --git a/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java b/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java index d04e13d7b4..4dcb42aee0 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java +++ b/ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java @@ -260,30 +260,44 @@ public final class ActiveDirectoryLdapAuthenticationProvider extends AbstractLda String hexString = Integer.toHexString(code); Throwable cause = new ActiveDirectoryAuthenticationException(hexString, exception.getMessage(), exception); switch (code) { - case PASSWORD_EXPIRED -> throw new CredentialsExpiredException(this.messages - .getMessage("LdapAuthenticationProvider.credentialsExpired", "User credentials have expired"), cause); - case ACCOUNT_DISABLED -> throw new DisabledException( - this.messages.getMessage("LdapAuthenticationProvider.disabled", "User is disabled"), cause); - case ACCOUNT_EXPIRED -> throw new AccountExpiredException( - this.messages.getMessage("LdapAuthenticationProvider.expired", "User account has expired"), cause); - case ACCOUNT_LOCKED -> throw new LockedException( - this.messages.getMessage("LdapAuthenticationProvider.locked", "User account is locked"), cause); - default -> throw badCredentials(cause); + case PASSWORD_EXPIRED: + throw new CredentialsExpiredException(this.messages.getMessage( + "LdapAuthenticationProvider.credentialsExpired", "User credentials have expired"), cause); + case ACCOUNT_DISABLED: + throw new DisabledException( + this.messages.getMessage("LdapAuthenticationProvider.disabled", "User is disabled"), cause); + case ACCOUNT_EXPIRED: + throw new AccountExpiredException( + this.messages.getMessage("LdapAuthenticationProvider.expired", "User account has expired"), + cause); + case ACCOUNT_LOCKED: + throw new LockedException( + this.messages.getMessage("LdapAuthenticationProvider.locked", "User account is locked"), cause); + default: + throw badCredentials(cause); } } private String subCodeToLogMessage(int code) { - return switch (code) { - case USERNAME_NOT_FOUND -> "User was not found in directory"; - case INVALID_PASSWORD -> "Supplied password was invalid"; - case NOT_PERMITTED -> "User not permitted to logon at this time"; - case PASSWORD_EXPIRED -> "Password has expired"; - case ACCOUNT_DISABLED -> "Account is disabled"; - case ACCOUNT_EXPIRED -> "Account expired"; - case PASSWORD_NEEDS_RESET -> "User must reset password"; - case ACCOUNT_LOCKED -> "Account locked"; - default -> "Unknown (error code " + Integer.toHexString(code) + ")"; - }; + switch (code) { + case USERNAME_NOT_FOUND: + return "User was not found in directory"; + case INVALID_PASSWORD: + return "Supplied password was invalid"; + case NOT_PERMITTED: + return "User not permitted to logon at this time"; + case PASSWORD_EXPIRED: + return "Password has expired"; + case ACCOUNT_DISABLED: + return "Account is disabled"; + case ACCOUNT_EXPIRED: + return "Account expired"; + case PASSWORD_NEEDS_RESET: + return "User must reset password"; + case ACCOUNT_LOCKED: + return "Account locked"; + } + return "Unknown (error code " + Integer.toHexString(code) + ")"; } private BadCredentialsException badCredentials() { diff --git a/ldap/src/main/java/org/springframework/security/ldap/server/ApacheDSContainer.java b/ldap/src/main/java/org/springframework/security/ldap/server/ApacheDSContainer.java index 1fe397ac51..379faed965 100644 --- a/ldap/src/main/java/org/springframework/security/ldap/server/ApacheDSContainer.java +++ b/ldap/src/main/java/org/springframework/security/ldap/server/ApacheDSContainer.java @@ -73,10 +73,10 @@ import org.springframework.util.Assert; * @author Rob Winch * @author Gunnar Hillert * @author Evgeniy Cheban - * @deprecated For removal in 7.0. Use {@link UnboundIdContainer} instead because ApacheDS - * 1.x is no longer supported with no GA version to replace it. + * @deprecated Use {@link UnboundIdContainer} instead because ApacheDS 1.x is no longer + * supported with no GA version to replace it. */ -@Deprecated(since = "5.2", forRemoval = true) +@Deprecated public class ApacheDSContainer implements EmbeddedLdapServerContainer, InitializingBean, DisposableBean, Lifecycle, ApplicationContextAware { diff --git a/ldap/src/test/java/org/springframework/security/ldap/userdetails/InetOrgPersonTests.java b/ldap/src/test/java/org/springframework/security/ldap/userdetails/InetOrgPersonTests.java index a572c61765..a1279c3471 100644 --- a/ldap/src/test/java/org/springframework/security/ldap/userdetails/InetOrgPersonTests.java +++ b/ldap/src/test/java/org/springframework/security/ldap/userdetails/InetOrgPersonTests.java @@ -46,7 +46,7 @@ public class InetOrgPersonTests { InetOrgPerson p2 = (InetOrgPerson) essence.createUserDetails(); Set set = new HashSet<>(); set.add(p); - assertThat(set).contains(p2); + assertThat(set.contains(p2)).isTrue(); } @Test diff --git a/ldap/src/test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsServiceTests.java b/ldap/src/test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsServiceTests.java index a9007ad052..b9d2ffa45e 100644 --- a/ldap/src/test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsServiceTests.java +++ b/ldap/src/test/java/org/springframework/security/ldap/userdetails/LdapUserDetailsServiceTests.java @@ -60,7 +60,7 @@ public class LdapUserDetailsServiceTests { UserDetails user = service.loadUserByUsername("doesntmatterwegetjoeanyway"); Set authorities = AuthorityUtils.authorityListToSet(user.getAuthorities()); assertThat(authorities).hasSize(1); - assertThat(authorities).contains("ROLE_FROM_POPULATOR"); + assertThat(authorities.contains("ROLE_FROM_POPULATOR")).isTrue(); } @Test diff --git a/ldap/src/test/java/org/springframework/security/ldap/userdetails/UserDetailsServiceLdapAuthoritiesPopulatorTests.java b/ldap/src/test/java/org/springframework/security/ldap/userdetails/UserDetailsServiceLdapAuthoritiesPopulatorTests.java index 486dda34c2..b2912790af 100644 --- a/ldap/src/test/java/org/springframework/security/ldap/userdetails/UserDetailsServiceLdapAuthoritiesPopulatorTests.java +++ b/ldap/src/test/java/org/springframework/security/ldap/userdetails/UserDetailsServiceLdapAuthoritiesPopulatorTests.java @@ -47,7 +47,7 @@ public class UserDetailsServiceLdapAuthoritiesPopulatorTests { UserDetailsServiceLdapAuthoritiesPopulator populator = new UserDetailsServiceLdapAuthoritiesPopulator(uds); Collection auths = populator.getGrantedAuthorities(new DirContextAdapter(), "joe"); assertThat(auths).hasSize(1); - assertThat(AuthorityUtils.authorityListToSet(auths)).contains("ROLE_USER"); + assertThat(AuthorityUtils.authorityListToSet(auths).contains("ROLE_USER")).isTrue(); } } diff --git a/messaging/src/main/java/org/springframework/security/messaging/access/intercept/MessageMatcherDelegatingAuthorizationManager.java b/messaging/src/main/java/org/springframework/security/messaging/access/intercept/MessageMatcherDelegatingAuthorizationManager.java index d442e7532c..68c1be5d7b 100644 --- a/messaging/src/main/java/org/springframework/security/messaging/access/intercept/MessageMatcherDelegatingAuthorizationManager.java +++ b/messaging/src/main/java/org/springframework/security/messaging/access/intercept/MessageMatcherDelegatingAuthorizationManager.java @@ -85,7 +85,8 @@ public final class MessageMatcherDelegatingAuthorizationManager implements Autho if (!matcher.matches((Message) message)) { return null; } - if (matcher instanceof SimpDestinationMessageMatcher simp) { + if (matcher instanceof SimpDestinationMessageMatcher) { + SimpDestinationMessageMatcher simp = (SimpDestinationMessageMatcher) matcher; return new MessageAuthorizationContext<>(message, simp.extractPathVariables(message)); } if (matcher instanceof Builder.LazySimpDestinationMessageMatcher) { @@ -110,7 +111,7 @@ public final class MessageMatcherDelegatingAuthorizationManager implements Autho private final List>>> mappings = new ArrayList<>(); - private Supplier pathMatcher = AntPathMatcher::new; + private Supplier pathMatcher = () -> new AntPathMatcher(); public Builder() { } diff --git a/messaging/src/main/java/org/springframework/security/messaging/context/SecurityContextPropagationChannelInterceptor.java b/messaging/src/main/java/org/springframework/security/messaging/context/SecurityContextPropagationChannelInterceptor.java deleted file mode 100644 index be68265948..0000000000 --- a/messaging/src/main/java/org/springframework/security/messaging/context/SecurityContextPropagationChannelInterceptor.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright 2002-2023 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.messaging.context; - -import java.util.Stack; - -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.MessageHandler; -import org.springframework.messaging.simp.SimpMessageHeaderAccessor; -import org.springframework.messaging.support.ExecutorChannelInterceptor; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.context.SecurityContextHolderStrategy; -import org.springframework.util.Assert; - -/** - * An {@link ExecutorChannelInterceptor} that takes an {@link Authentication} from the - * current {@link SecurityContext} (if any) in the - * {@link #preSend(Message, MessageChannel)} callback and stores it into an - * {@link #authenticationHeaderName} message header. Then sets the context from this - * header in the {@link #beforeHandle(Message, MessageChannel, MessageHandler)} and - * {@link #postReceive(Message, MessageChannel)} both of which typically happen on a - * different thread. - *

    - * Note: cannot be used in combination with a {@link SecurityContextChannelInterceptor} on - * the same channel since both these interceptors modify a security context on a handling - * and receiving operations. - * - * @author Artem Bilan - * @since 6.2 - * @see SecurityContextChannelInterceptor - */ -public final class SecurityContextPropagationChannelInterceptor implements ExecutorChannelInterceptor { - - private static final ThreadLocal> originalContext = new ThreadLocal<>(); - - private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder - .getContextHolderStrategy(); - - private SecurityContext empty = this.securityContextHolderStrategy.createEmptyContext(); - - private final String authenticationHeaderName; - - private Authentication anonymous = new AnonymousAuthenticationToken("key", "anonymous", - AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); - - /** - * Create a new instance using the header of the name - * {@link SimpMessageHeaderAccessor#USER_HEADER}. - */ - public SecurityContextPropagationChannelInterceptor() { - this(SimpMessageHeaderAccessor.USER_HEADER); - } - - /** - * Create a new instance that uses the specified header to populate the - * {@link Authentication}. - * @param authenticationHeaderName the header name to populate the - * {@link Authentication}. Cannot be null. - */ - public SecurityContextPropagationChannelInterceptor(String authenticationHeaderName) { - Assert.notNull(authenticationHeaderName, "authenticationHeaderName cannot be null"); - this.authenticationHeaderName = authenticationHeaderName; - } - - public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy) { - this.securityContextHolderStrategy = strategy; - this.empty = this.securityContextHolderStrategy.createEmptyContext(); - } - - /** - * Configure an Authentication used for anonymous authentication. Default is:

    -	 * new AnonymousAuthenticationToken("key", "anonymous",
    -	 * 		AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
    -	 * 
    - * @param authentication the Authentication used for anonymous authentication. Cannot - * be null. - */ - public void setAnonymousAuthentication(Authentication authentication) { - Assert.notNull(authentication, "authentication cannot be null"); - this.anonymous = authentication; - } - - @Override - public Message preSend(Message message, MessageChannel channel) { - Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication(); - if (authentication == null) { - authentication = this.anonymous; - } - return MessageBuilder.fromMessage(message).setHeader(this.authenticationHeaderName, authentication).build(); - } - - @Override - public Message beforeHandle(Message message, MessageChannel channel, MessageHandler handler) { - return postReceive(message, channel); - } - - @Override - public Message postReceive(Message message, MessageChannel channel) { - setup(message); - return message; - } - - @Override - public void afterMessageHandled(Message message, MessageChannel channel, MessageHandler handler, Exception ex) { - cleanup(); - } - - private void setup(Message message) { - Authentication authentication = message.getHeaders().get(this.authenticationHeaderName, Authentication.class); - SecurityContext currentContext = this.securityContextHolderStrategy.getContext(); - Stack contextStack = originalContext.get(); - if (contextStack == null) { - contextStack = new Stack<>(); - originalContext.set(contextStack); - } - contextStack.push(currentContext); - SecurityContext context = this.securityContextHolderStrategy.createEmptyContext(); - context.setAuthentication(authentication); - this.securityContextHolderStrategy.setContext(context); - } - - private void cleanup() { - Stack contextStack = originalContext.get(); - if (contextStack == null || contextStack.isEmpty()) { - this.securityContextHolderStrategy.clearContext(); - originalContext.remove(); - return; - } - SecurityContext context = contextStack.pop(); - try { - if (this.empty.equals(context)) { - this.securityContextHolderStrategy.clearContext(); - originalContext.remove(); - } - else { - this.securityContextHolderStrategy.setContext(context); - } - } - catch (Throwable ex) { - this.securityContextHolderStrategy.clearContext(); - } - } - -} diff --git a/messaging/src/main/java/org/springframework/security/messaging/util/matcher/SimpMessageTypeMatcher.java b/messaging/src/main/java/org/springframework/security/messaging/util/matcher/SimpMessageTypeMatcher.java index 2b2e2cfae5..00aa6e8a64 100644 --- a/messaging/src/main/java/org/springframework/security/messaging/util/matcher/SimpMessageTypeMatcher.java +++ b/messaging/src/main/java/org/springframework/security/messaging/util/matcher/SimpMessageTypeMatcher.java @@ -57,9 +57,10 @@ public class SimpMessageTypeMatcher implements MessageMatcher { if (this == other) { return true; } - if (!(other instanceof SimpMessageTypeMatcher otherMatcher)) { + if (!(other instanceof SimpMessageTypeMatcher)) { return false; } + SimpMessageTypeMatcher otherMatcher = (SimpMessageTypeMatcher) other; return ObjectUtils.nullSafeEquals(this.typeToMatch, otherMatcher.typeToMatch); } diff --git a/messaging/src/test/java/org/springframework/security/messaging/context/SecurityContextPropagationChannelInterceptorTests.java b/messaging/src/test/java/org/springframework/security/messaging/context/SecurityContextPropagationChannelInterceptorTests.java deleted file mode 100644 index 1682b96200..0000000000 --- a/messaging/src/test/java/org/springframework/security/messaging/context/SecurityContextPropagationChannelInterceptorTests.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2002-2023 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.messaging.context; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.messaging.Message; -import org.springframework.messaging.MessageChannel; -import org.springframework.messaging.MessageHandler; -import org.springframework.messaging.simp.SimpMessageHeaderAccessor; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.security.authentication.AnonymousAuthenticationToken; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.context.SecurityContextHolderStrategy; -import org.springframework.security.core.context.SecurityContextImpl; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -@ExtendWith(MockitoExtension.class) -public class SecurityContextPropagationChannelInterceptorTests { - - @Mock - MessageChannel channel; - - @Mock - MessageHandler handler; - - MessageBuilder messageBuilder; - - Authentication authentication; - - SecurityContextPropagationChannelInterceptor interceptor; - - @BeforeEach - public void setup() { - this.authentication = new TestingAuthenticationToken("user", "pass", "ROLE_USER"); - this.messageBuilder = MessageBuilder.withPayload("payload"); - this.interceptor = new SecurityContextPropagationChannelInterceptor(); - } - - @AfterEach - public void cleanup() { - this.interceptor.afterMessageHandled(this.messageBuilder.build(), this.channel, this.handler, null); - SecurityContextHolder.clearContext(); - } - - @Test - public void preSendDefaultHeader() { - SecurityContextHolder.getContext().setAuthentication(this.authentication); - Message message = this.interceptor.preSend(this.messageBuilder.build(), this.channel); - assertThat(message.getHeaders()).containsEntry(SimpMessageHeaderAccessor.USER_HEADER, this.authentication); - } - - @Test - public void preSendCustomHeader() { - SecurityContextHolder.getContext().setAuthentication(this.authentication); - String headerName = "header"; - this.interceptor = new SecurityContextPropagationChannelInterceptor(headerName); - Message message = this.interceptor.preSend(this.messageBuilder.build(), this.channel); - assertThat(message.getHeaders()).containsEntry(headerName, this.authentication); - } - - @Test - public void preSendWhenCustomSecurityContextHolderStrategyThenUserSet() { - SecurityContextHolderStrategy strategy = spy(SecurityContextHolder.getContextHolderStrategy()); - strategy.setContext(new SecurityContextImpl(this.authentication)); - this.interceptor.setSecurityContextHolderStrategy(strategy); - Message message = this.interceptor.preSend(this.messageBuilder.build(), this.channel); - this.interceptor.beforeHandle(message, this.channel, this.handler); - verify(strategy, times(2)).getContext(); - assertThat(strategy.getContext().getAuthentication()).isSameAs(this.authentication); - } - - @Test - public void preSendUserNoContext() { - Message message = this.interceptor.preSend(this.messageBuilder.build(), this.channel); - assertThat(message.getHeaders()).containsKey(SimpMessageHeaderAccessor.USER_HEADER); - assertThat(message.getHeaders().get(SimpMessageHeaderAccessor.USER_HEADER)) - .isInstanceOf(AnonymousAuthenticationToken.class); - } - - @Test - public void beforeHandleUserSet() { - this.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.authentication); - this.interceptor.beforeHandle(this.messageBuilder.build(), this.channel, this.handler); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.authentication); - } - - @Test - public void postReceiveUserSet() { - this.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.authentication); - this.interceptor.postReceive(this.messageBuilder.build(), this.channel); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.authentication); - } - - @Test - public void authenticationIsPropagatedFromPreSendToPostReceive() { - SecurityContextHolder.getContext().setAuthentication(this.authentication); - Message message = this.interceptor.preSend(this.messageBuilder.build(), this.channel); - assertThat(message.getHeaders().get(SimpMessageHeaderAccessor.USER_HEADER)).isSameAs(this.authentication); - this.interceptor.postReceive(message, this.channel); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.authentication); - } - - @Test - public void beforeHandleUserNotSet() { - this.interceptor.beforeHandle(this.messageBuilder.build(), this.channel, this.handler); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); - } - - @Test - public void afterMessageHandledUserNotSet() { - this.interceptor.afterMessageHandled(this.messageBuilder.build(), this.channel, this.handler, null); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); - } - - @Test - public void afterMessageHandled() { - SecurityContextHolder.getContext().setAuthentication(this.authentication); - this.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.authentication); - this.interceptor.afterMessageHandled(this.messageBuilder.build(), this.channel, this.handler, null); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); - } - - @Test - public void restoresOriginalContext() { - TestingAuthenticationToken original = new TestingAuthenticationToken("original", "original", "ROLE_USER"); - SecurityContextHolder.getContext().setAuthentication(original); - this.messageBuilder.setHeader(SimpMessageHeaderAccessor.USER_HEADER, this.authentication); - this.interceptor.beforeHandle(this.messageBuilder.build(), this.channel, this.handler); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(this.authentication); - this.interceptor.afterMessageHandled(this.messageBuilder.build(), this.channel, this.handler, null); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(original); - } - -} diff --git a/messaging/src/test/java/org/springframework/security/messaging/handler/invocation/ResolvableMethod.java b/messaging/src/test/java/org/springframework/security/messaging/handler/invocation/ResolvableMethod.java index 3dd316660f..dbeaf95a62 100644 --- a/messaging/src/test/java/org/springframework/security/messaging/handler/invocation/ResolvableMethod.java +++ b/messaging/src/test/java/org/springframework/security/messaging/handler/invocation/ResolvableMethod.java @@ -40,7 +40,7 @@ import org.springframework.cglib.proxy.Callback; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.Factory; import org.springframework.cglib.proxy.MethodProxy; -import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.MethodIntrospector; import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; @@ -132,7 +132,7 @@ public final class ResolvableMethod { private static final SpringObjenesis objenesis = new SpringObjenesis(); - private static final ParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer(); + private static final ParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); // Matches ValueConstants.DEFAULT_NONE (spring-web and spring-messaging) private static final String DEFAULT_VALUE_NONE = "\n\t\t\n\t\t\n\uE000\uE001\uE002\n\t\t\t\t\n"; diff --git a/messaging/src/test/java/org/springframework/security/messaging/util/matcher/SimpDestinationMessageMatcherTests.java b/messaging/src/test/java/org/springframework/security/messaging/util/matcher/SimpDestinationMessageMatcherTests.java index b13bdab5dc..608b3216ae 100644 --- a/messaging/src/test/java/org/springframework/security/messaging/util/matcher/SimpDestinationMessageMatcherTests.java +++ b/messaging/src/test/java/org/springframework/security/messaging/util/matcher/SimpDestinationMessageMatcherTests.java @@ -113,7 +113,7 @@ public class SimpDestinationMessageMatcherTests { this.matcher = new SimpDestinationMessageMatcher("/topics/{topic}/**"); this.messageBuilder.setHeader(SimpMessageHeaderAccessor.DESTINATION_HEADER, "/topics/someTopic/sub1"); this.messageBuilder.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER, SimpMessageType.MESSAGE); - assertThat(this.matcher.extractPathVariables(this.messageBuilder.build())).containsEntry("topic", "someTopic"); + assertThat(this.matcher.extractPathVariables(this.messageBuilder.build()).get("topic")).isEqualTo("someTopic"); } @Test diff --git a/messaging/src/test/java/org/springframework/security/messaging/web/socket/server/CsrfTokenHandshakeInterceptorTests.java b/messaging/src/test/java/org/springframework/security/messaging/web/socket/server/CsrfTokenHandshakeInterceptorTests.java index 09ad468728..760fe3aa1b 100644 --- a/messaging/src/test/java/org/springframework/security/messaging/web/socket/server/CsrfTokenHandshakeInterceptorTests.java +++ b/messaging/src/test/java/org/springframework/security/messaging/web/socket/server/CsrfTokenHandshakeInterceptorTests.java @@ -75,7 +75,7 @@ public class CsrfTokenHandshakeInterceptorTests { CsrfToken token = new DefaultCsrfToken("header", "param", "token"); this.httpRequest.setAttribute(DeferredCsrfToken.class.getName(), new TestDeferredCsrfToken(token)); this.interceptor.beforeHandshake(this.request, this.response, this.wsHandler, this.attributes); - assertThat(this.attributes).containsOnlyKeys(CsrfToken.class.getName()); + assertThat(this.attributes.keySet()).containsOnly(CsrfToken.class.getName()); CsrfToken csrfToken = (CsrfToken) this.attributes.get(CsrfToken.class.getName()); assertThat(csrfToken.getHeaderName()).isEqualTo(token.getHeaderName()); assertThat(csrfToken.getParameterName()).isEqualTo(token.getParameterName()); diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/JdbcOAuth2AuthorizedClientService.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/JdbcOAuth2AuthorizedClientService.java index 19e373fe72..f5847c1ad2 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/JdbcOAuth2AuthorizedClientService.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/JdbcOAuth2AuthorizedClientService.java @@ -396,7 +396,8 @@ public class JdbcOAuth2AuthorizedClientService implements OAuth2AuthorizedClient @Override protected void doSetValue(PreparedStatement ps, int parameterPosition, Object argValue) throws SQLException { - if (argValue instanceof SqlParameterValue paramValue) { + if (argValue instanceof SqlParameterValue) { + SqlParameterValue paramValue = (SqlParameterValue) argValue; if (paramValue.getSqlType() == Types.BLOB) { if (paramValue.getValue() != null) { Assert.isInstanceOf(byte[].class, paramValue.getValue(), diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RemoveAuthorizedClientOAuth2AuthorizationFailureHandler.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RemoveAuthorizedClientOAuth2AuthorizationFailureHandler.java index 4f174b5c5e..5dc1896796 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RemoveAuthorizedClientOAuth2AuthorizationFailureHandler.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RemoveAuthorizedClientOAuth2AuthorizationFailureHandler.java @@ -110,8 +110,9 @@ public class RemoveAuthorizedClientOAuth2AuthorizationFailureHandler implements @Override public void onAuthorizationFailure(OAuth2AuthorizationException authorizationException, Authentication principal, Map attributes) { - if (authorizationException instanceof ClientAuthorizationException clientAuthorizationException + if (authorizationException instanceof ClientAuthorizationException && hasRemovalErrorCode(authorizationException)) { + ClientAuthorizationException clientAuthorizationException = (ClientAuthorizationException) authorizationException; this.delegate.removeAuthorizedClient(clientAuthorizationException.getClientRegistrationId(), principal, attributes); } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler.java index 0c92422234..0fd36f7f01 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler.java @@ -112,8 +112,9 @@ public class RemoveAuthorizedClientReactiveOAuth2AuthorizationFailureHandler @Override public Mono onAuthorizationFailure(OAuth2AuthorizationException authorizationException, Authentication principal, Map attributes) { - if (authorizationException instanceof ClientAuthorizationException clientAuthorizationException + if (authorizationException instanceof ClientAuthorizationException && hasRemovalErrorCode(authorizationException)) { + ClientAuthorizationException clientAuthorizationException = (ClientAuthorizationException) authorizationException; return this.delegate.removeAuthorizedClient(clientAuthorizationException.getClientRegistrationId(), principal, attributes); } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/AbstractWebClientReactiveOAuth2AccessTokenResponseClient.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/AbstractWebClientReactiveOAuth2AccessTokenResponseClient.java index 1ce0263431..8716ed874e 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/AbstractWebClientReactiveOAuth2AccessTokenResponseClient.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/AbstractWebClientReactiveOAuth2AccessTokenResponseClient.java @@ -115,7 +115,8 @@ public abstract class AbstractWebClientReactiveOAuth2AccessTokenResponseClient String.format( "This class supports `client_secret_basic`, `client_secret_post`, and `none` by default. Client [%s] is using [%s] instead. Please use a supported client authentication method, or use `setRequestEntityConverter` to supply an instance that supports [%s].", - registrationId, clientAuthenticationMethod, clientAuthenticationMethod)); + registrationId, clientAuthenticationMethod.getValue(), clientAuthenticationMethod.getValue())); return this.delegate.convert(grantRequest); } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/logout/LogoutTokenClaimAccessor.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/logout/LogoutTokenClaimAccessor.java deleted file mode 100644 index 49aeff4c3c..0000000000 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/logout/LogoutTokenClaimAccessor.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.client.oidc.authentication.logout; - -import java.net.URL; -import java.time.Instant; -import java.util.List; -import java.util.Map; - -import org.springframework.security.oauth2.core.ClaimAccessor; - -/** - * A {@link ClaimAccessor} for the "claims" that can be returned in OIDC Logout - * Tokens - * - * @author Josh Cummings - * @since 6.2 - * @see OidcLogoutToken - * @see OIDC - * Back-Channel Logout Token - */ -public interface LogoutTokenClaimAccessor extends ClaimAccessor { - - /** - * Returns the Issuer identifier {@code (iss)}. - * @return the Issuer identifier - */ - default URL getIssuer() { - return this.getClaimAsURL(LogoutTokenClaimNames.ISS); - } - - /** - * Returns the Subject identifier {@code (sub)}. - * @return the Subject identifier - */ - default String getSubject() { - return this.getClaimAsString(LogoutTokenClaimNames.SUB); - } - - /** - * Returns the Audience(s) {@code (aud)} that this ID Token is intended for. - * @return the Audience(s) that this ID Token is intended for - */ - default List getAudience() { - return this.getClaimAsStringList(LogoutTokenClaimNames.AUD); - } - - /** - * Returns the time at which the ID Token was issued {@code (iat)}. - * @return the time at which the ID Token was issued - */ - default Instant getIssuedAt() { - return this.getClaimAsInstant(LogoutTokenClaimNames.IAT); - } - - /** - * Returns a {@link Map} that identifies this token as a logout token - * @return the identifying {@link Map} - */ - default Map getEvents() { - return getClaimAsMap(LogoutTokenClaimNames.EVENTS); - } - - /** - * Returns a {@code String} value {@code (sid)} representing the OIDC Provider session - * @return the value representing the OIDC Provider session - */ - default String getSessionId() { - return getClaimAsString(LogoutTokenClaimNames.SID); - } - - /** - * Returns the JWT ID {@code (jti)} claim which provides a unique identifier for the - * JWT. - * @return the JWT ID claim which provides a unique identifier for the JWT - */ - default String getId() { - return this.getClaimAsString(LogoutTokenClaimNames.JTI); - } - -} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/logout/LogoutTokenClaimNames.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/logout/LogoutTokenClaimNames.java deleted file mode 100644 index 9893aa350a..0000000000 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/logout/LogoutTokenClaimNames.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.oauth2.client.oidc.authentication.logout; - -/** - * The names of the "claims" defined by the OpenID Back-Channel Logout 1.0 - * specification that can be returned in a Logout Token. - * - * @author Josh Cummings - * @since 6.2 - * @see OidcLogoutToken - * @see OIDC - * Back-Channel Logout Token - */ -public final class LogoutTokenClaimNames { - - /** - * {@code jti} - the JTI identifier - */ - public static final String JTI = "jti"; - - /** - * {@code iss} - the Issuer identifier - */ - public static final String ISS = "iss"; - - /** - * {@code sub} - the Subject identifier - */ - public static final String SUB = "sub"; - - /** - * {@code aud} - the Audience(s) that the ID Token is intended for - */ - public static final String AUD = "aud"; - - /** - * {@code iat} - the time at which the ID Token was issued - */ - public static final String IAT = "iat"; - - /** - * {@code events} - a JSON object that identifies this token as a logout token - */ - public static final String EVENTS = "events"; - - /** - * {@code sid} - the session id for the OIDC provider - */ - public static final String SID = "sid"; - - private LogoutTokenClaimNames() { - } - -} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/logout/OidcLogoutToken.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/logout/OidcLogoutToken.java deleted file mode 100644 index 41b425bf40..0000000000 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/authentication/logout/OidcLogoutToken.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.client.oidc.authentication.logout; - -import java.time.Instant; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.function.Consumer; - -import org.springframework.security.oauth2.core.AbstractOAuth2Token; -import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames; -import org.springframework.util.Assert; - -/** - * An implementation of an {@link AbstractOAuth2Token} representing an OpenID Backchannel - * Logout Token. - * - *

    - * The {@code OidcLogoutToken} is a security token that contains "claims" about - * terminating sessions for a given OIDC Provider session id or End User. - * - * @author Josh Cummings - * @since 6.2 - * @see AbstractOAuth2Token - * @see LogoutTokenClaimAccessor - * @see Logout - * Token - */ -public class OidcLogoutToken extends AbstractOAuth2Token implements LogoutTokenClaimAccessor { - - private static final String BACKCHANNEL_LOGOUT_TOKEN_EVENT_NAME = "http://schemas.openid.net/event/backchannel-logout"; - - private final Map claims; - - /** - * Constructs a {@link OidcLogoutToken} using the provided parameters. - * @param tokenValue the Logout Token value - * @param issuedAt the time at which the Logout Token was issued {@code (iat)} - * @param claims the claims about the logout statement - */ - OidcLogoutToken(String tokenValue, Instant issuedAt, Map claims) { - super(tokenValue, issuedAt, Instant.MAX); - this.claims = Collections.unmodifiableMap(claims); - Assert.notNull(claims, "claims must not be null"); - } - - @Override - public Map getClaims() { - return this.claims; - } - - /** - * Create a {@link OidcLogoutToken.Builder} based on the given token value - * @param tokenValue the token value to use - * @return the {@link OidcLogoutToken.Builder} for further configuration - */ - public static Builder withTokenValue(String tokenValue) { - return new Builder(tokenValue); - } - - /** - * A builder for {@link OidcLogoutToken}s - * - * @author Josh Cummings - */ - public static final class Builder { - - private String tokenValue; - - private final Map claims = new LinkedHashMap<>(); - - private Builder(String tokenValue) { - this.tokenValue = tokenValue; - this.claims.put(LogoutTokenClaimNames.EVENTS, - Collections.singletonMap(BACKCHANNEL_LOGOUT_TOKEN_EVENT_NAME, Collections.emptyMap())); - } - - /** - * Use this token value in the resulting {@link OidcLogoutToken} - * @param tokenValue The token value to use - * @return the {@link Builder} for further configurations - */ - public Builder tokenValue(String tokenValue) { - this.tokenValue = tokenValue; - return this; - } - - /** - * Use this claim in the resulting {@link OidcLogoutToken} - * @param name The claim name - * @param value The claim value - * @return the {@link Builder} for further configurations - */ - public Builder claim(String name, Object value) { - this.claims.put(name, value); - return this; - } - - /** - * Provides access to every {@link #claim(String, Object)} declared so far with - * the possibility to add, replace, or remove. - * @param claimsConsumer the consumer - * @return the {@link Builder} for further configurations - */ - public Builder claims(Consumer> claimsConsumer) { - claimsConsumer.accept(this.claims); - return this; - } - - /** - * Use this audience in the resulting {@link OidcLogoutToken} - * @param audience The audience(s) to use - * @return the {@link Builder} for further configurations - */ - public Builder audience(Collection audience) { - return claim(LogoutTokenClaimNames.AUD, audience); - } - - /** - * Use this issued-at timestamp in the resulting {@link OidcLogoutToken} - * @param issuedAt The issued-at timestamp to use - * @return the {@link Builder} for further configurations - */ - public Builder issuedAt(Instant issuedAt) { - return claim(LogoutTokenClaimNames.IAT, issuedAt); - } - - /** - * Use this issuer in the resulting {@link OidcLogoutToken} - * @param issuer The issuer to use - * @return the {@link Builder} for further configurations - */ - public Builder issuer(String issuer) { - return claim(LogoutTokenClaimNames.ISS, issuer); - } - - /** - * Use this id to identify the resulting {@link OidcLogoutToken} - * @param jti The unique identifier to use - * @return the {@link Builder} for further configurations - */ - public Builder jti(String jti) { - return claim(LogoutTokenClaimNames.JTI, jti); - } - - /** - * Use this subject in the resulting {@link OidcLogoutToken} - * @param subject The subject to use - * @return the {@link Builder} for further configurations - */ - public Builder subject(String subject) { - return claim(LogoutTokenClaimNames.SUB, subject); - } - - /** - * A JSON object that identifies this token as a logout token - * @param events The JSON object to use - * @return the {@link Builder} for further configurations - */ - public Builder events(Map events) { - return claim(LogoutTokenClaimNames.EVENTS, events); - } - - /** - * Use this session id to correlate the OIDC Provider session - * @param sessionId The session id to use - * @return the {@link Builder} for further configurations - */ - public Builder sessionId(String sessionId) { - return claim(LogoutTokenClaimNames.SID, sessionId); - } - - public OidcLogoutToken build() { - Assert.notNull(this.claims.get(LogoutTokenClaimNames.ISS), "issuer must not be null"); - Assert.isInstanceOf(Collection.class, this.claims.get(LogoutTokenClaimNames.AUD), - "audience must be a collection"); - Assert.notEmpty((Collection) this.claims.get(LogoutTokenClaimNames.AUD), "audience must not be empty"); - Assert.notNull(this.claims.get(LogoutTokenClaimNames.JTI), "jti must not be null"); - Assert.isTrue(hasLogoutTokenIdentifyingMember(), - "logout token must contain an events claim that contains a member called " + "'" - + BACKCHANNEL_LOGOUT_TOKEN_EVENT_NAME + "' whose value is an empty Map"); - Assert.isNull(this.claims.get("nonce"), "logout token must not contain a nonce claim"); - Instant iat = toInstant(this.claims.get(IdTokenClaimNames.IAT)); - return new OidcLogoutToken(this.tokenValue, iat, this.claims); - } - - private boolean hasLogoutTokenIdentifyingMember() { - if (!(this.claims.get(LogoutTokenClaimNames.EVENTS) instanceof Map events)) { - return false; - } - if (!(events.get(BACKCHANNEL_LOGOUT_TOKEN_EVENT_NAME) instanceof Map object)) { - return false; - } - return object.isEmpty(); - } - - private Instant toInstant(Object timestamp) { - if (timestamp != null) { - Assert.isInstanceOf(Instant.class, timestamp, "timestamps must be of type Instant"); - } - return (Instant) timestamp; - } - - } - -} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/server/session/InMemoryReactiveOidcSessionRegistry.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/server/session/InMemoryReactiveOidcSessionRegistry.java deleted file mode 100644 index cd1e92ac93..0000000000 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/server/session/InMemoryReactiveOidcSessionRegistry.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.client.oidc.server.session; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken; -import org.springframework.security.oauth2.client.oidc.session.InMemoryOidcSessionRegistry; -import org.springframework.security.oauth2.client.oidc.session.OidcSessionInformation; - -/** - * An in-memory implementation of - * {@link org.springframework.security.oauth2.client.oidc.server.session.ReactiveOidcSessionRegistry} - * - * @author Josh Cummings - * @since 6.2 - */ -public final class InMemoryReactiveOidcSessionRegistry implements ReactiveOidcSessionRegistry { - - private final InMemoryOidcSessionRegistry delegate = new InMemoryOidcSessionRegistry(); - - @Override - public Mono saveSessionInformation(OidcSessionInformation info) { - this.delegate.saveSessionInformation(info); - return Mono.empty(); - } - - @Override - public Mono removeSessionInformation(String clientSessionId) { - return Mono.justOrEmpty(this.delegate.removeSessionInformation(clientSessionId)); - } - - @Override - public Flux removeSessionInformation(OidcLogoutToken token) { - return Flux.fromIterable(this.delegate.removeSessionInformation(token)); - } - -} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/server/session/ReactiveOidcSessionRegistry.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/server/session/ReactiveOidcSessionRegistry.java deleted file mode 100644 index a4cbb39f46..0000000000 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/server/session/ReactiveOidcSessionRegistry.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.client.oidc.server.session; - -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -import org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken; -import org.springframework.security.oauth2.client.oidc.session.OidcSessionInformation; - -/** - * A registry to record the tie between the OIDC Provider session and the Client session. - * This is handy when a provider makes a logout request that indicates the OIDC Provider - * session or the End User. - * - * @author Josh Cummings - * @since 6.2 - * @see Logout - * Token - */ -public interface ReactiveOidcSessionRegistry { - - /** - * Register a OIDC Provider session with the provided client session. Generally - * speaking, the client session should be the session tied to the current login. - * @param info the {@link OidcSessionInformation} to use - */ - Mono saveSessionInformation(OidcSessionInformation info); - - /** - * Deregister the OIDC Provider session tied to the provided client session. Generally - * speaking, the client session should be the session tied to the current logout. - * @param clientSessionId the client session - * @return any found {@link OidcSessionInformation}, could be {@code null} - */ - Mono removeSessionInformation(String clientSessionId); - - /** - * Deregister the OIDC Provider sessions referenced by the provided OIDC Logout Token - * by its session id or its subject. Note that the issuer and audience should also - * match the corresponding values found in each {@link OidcSessionInformation} - * returned. - * @param logoutToken the {@link OidcLogoutToken} - * @return any found {@link OidcSessionInformation}s, could be empty - */ - Flux removeSessionInformation(OidcLogoutToken logoutToken); - -} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/InMemoryOidcSessionRegistry.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/InMemoryOidcSessionRegistry.java deleted file mode 100644 index f5bb6235df..0000000000 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/InMemoryOidcSessionRegistry.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.client.oidc.session; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Predicate; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.security.oauth2.client.oidc.authentication.logout.LogoutTokenClaimNames; -import org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken; - -/** - * An in-memory implementation of {@link OidcSessionRegistry} - * - * @author Josh Cummings - * @since 6.2 - */ -public final class InMemoryOidcSessionRegistry implements OidcSessionRegistry { - - private final Log logger = LogFactory.getLog(InMemoryOidcSessionRegistry.class); - - private final Map sessions = new ConcurrentHashMap<>(); - - @Override - public void saveSessionInformation(OidcSessionInformation info) { - this.sessions.put(info.getSessionId(), info); - } - - @Override - public OidcSessionInformation removeSessionInformation(String clientSessionId) { - OidcSessionInformation information = this.sessions.remove(clientSessionId); - if (information != null) { - this.logger.trace("Removed client session"); - } - return information; - } - - @Override - public Iterable removeSessionInformation(OidcLogoutToken token) { - List audience = token.getAudience(); - String issuer = token.getIssuer().toString(); - String subject = token.getSubject(); - String providerSessionId = token.getSessionId(); - Predicate matcher = (providerSessionId != null) - ? sessionIdMatcher(audience, issuer, providerSessionId) : subjectMatcher(audience, issuer, subject); - if (this.logger.isTraceEnabled()) { - String message = "Looking up sessions by issuer [%s] and %s [%s]"; - if (providerSessionId != null) { - this.logger.trace(String.format(message, issuer, LogoutTokenClaimNames.SID, providerSessionId)); - } - else { - this.logger.trace(String.format(message, issuer, LogoutTokenClaimNames.SUB, subject)); - } - } - int size = this.sessions.size(); - Set infos = new HashSet<>(); - this.sessions.values().removeIf((info) -> { - boolean result = matcher.test(info); - if (result) { - infos.add(info); - } - return result; - }); - if (infos.isEmpty()) { - this.logger.debug("Failed to remove any sessions since none matched"); - } - else if (this.logger.isTraceEnabled()) { - String message = "Found and removed %d session(s) from mapping of %d session(s)"; - this.logger.trace(String.format(message, infos.size(), size)); - } - return infos; - } - - private static Predicate sessionIdMatcher(List audience, String issuer, - String sessionId) { - return (session) -> { - List thatAudience = session.getPrincipal().getAudience(); - String thatIssuer = session.getPrincipal().getIssuer().toString(); - String thatSessionId = session.getPrincipal().getClaimAsString(LogoutTokenClaimNames.SID); - if (thatAudience == null) { - return false; - } - return !Collections.disjoint(audience, thatAudience) && issuer.equals(thatIssuer) - && sessionId.equals(thatSessionId); - }; - } - - private static Predicate subjectMatcher(List audience, String issuer, - String subject) { - return (session) -> { - List thatAudience = session.getPrincipal().getAudience(); - String thatIssuer = session.getPrincipal().getIssuer().toString(); - String thatSubject = session.getPrincipal().getSubject(); - if (thatAudience == null) { - return false; - } - return !Collections.disjoint(audience, thatAudience) && issuer.equals(thatIssuer) - && subject.equals(thatSubject); - }; - } - -} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/OidcSessionInformation.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/OidcSessionInformation.java deleted file mode 100644 index d746315178..0000000000 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/OidcSessionInformation.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.client.oidc.session; - -import java.util.Collections; -import java.util.Date; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.springframework.security.core.session.SessionInformation; -import org.springframework.security.oauth2.core.oidc.user.OidcUser; - -/** - * A {@link SessionInformation} extension that enforces the principal be of type - * {@link OidcUser}. - * - * @author Josh Cummings - * @since 6.2 - */ -public class OidcSessionInformation extends SessionInformation { - - private final Map authorities; - - /** - * Construct an {@link OidcSessionInformation} - * @param sessionId the Client's session id - * @param authorities any material that authorizes operating on the session - * @param user the OIDC Provider's session and end user - */ - public OidcSessionInformation(String sessionId, Map authorities, OidcUser user) { - super(user, sessionId, new Date()); - this.authorities = (authorities != null) ? new LinkedHashMap<>(authorities) : Collections.emptyMap(); - } - - /** - * Any material needed to authorize operations on this session - * @return the {@link Map} of credentials - */ - public Map getAuthorities() { - return this.authorities; - } - - /** - * {@inheritDoc} - */ - @Override - public OidcUser getPrincipal() { - return (OidcUser) super.getPrincipal(); - } - - /** - * Copy this {@link OidcSessionInformation}, using a new session identifier - * @param sessionId the new session identifier to use - * @return a new {@link OidcSessionInformation} instance - */ - public OidcSessionInformation withSessionId(String sessionId) { - return new OidcSessionInformation(sessionId, getAuthorities(), getPrincipal()); - } - -} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/OidcSessionRegistry.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/OidcSessionRegistry.java deleted file mode 100644 index 26bae499db..0000000000 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/OidcSessionRegistry.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.client.oidc.session; - -import org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken; - -/** - * A registry to record the tie between the OIDC Provider session and the Client session. - * This is handy when a provider makes a logout request that indicates the OIDC Provider - * session or the End User. - * - * @author Josh Cummings - * @since 6.2 - * @see Logout - * Token - */ -public interface OidcSessionRegistry { - - /** - * Register a OIDC Provider session with the provided client session. Generally - * speaking, the client session should be the session tied to the current login. - * @param info the {@link OidcSessionInformation} to use - */ - void saveSessionInformation(OidcSessionInformation info); - - /** - * Deregister the OIDC Provider session tied to the provided client session. Generally - * speaking, the client session should be the session tied to the current logout. - * @param clientSessionId the client session - * @return any found {@link OidcSessionInformation}, could be {@code null} - */ - OidcSessionInformation removeSessionInformation(String clientSessionId); - - /** - * Deregister the OIDC Provider sessions referenced by the provided OIDC Logout Token - * by its session id or its subject. Note that the issuer and audience should also - * match the corresponding values found in each {@link OidcSessionInformation} - * returned. - * @param logoutToken the {@link OidcLogoutToken} - * @return any found {@link OidcSessionInformation}s, could be empty - */ - Iterable removeSessionInformation(OidcLogoutToken logoutToken); - -} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserRequestUtils.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserRequestUtils.java index 1cd71aa072..e8e6362479 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserRequestUtils.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserRequestUtils.java @@ -45,7 +45,7 @@ final class OidcUserRequestUtils { static boolean shouldRetrieveUserInfo(OidcUserRequest userRequest) { // Auto-disabled if UserInfo Endpoint URI is not provided ClientRegistration clientRegistration = userRequest.getClientRegistration(); - if (!StringUtils.hasLength(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri())) { + if (StringUtils.isEmpty(clientRegistration.getProviderDetails().getUserInfoEndpoint().getUri())) { return false; } // The Claims requested by the profile, email, address, and phone scope values diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserService.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserService.java index 0ae4727ff7..abf3632964 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserService.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserService.java @@ -157,7 +157,7 @@ public class OidcUserService implements OAuth2UserServiceRP-Initiated Logout * @see org.springframework.security.web.authentication.logout.LogoutSuccessHandler */ -public class OidcClientInitiatedLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { +public final class OidcClientInitiatedLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler { private final ClientRegistrationRepository clientRegistrationRepository; diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/SupplierClientRegistrationRepository.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/SupplierClientRegistrationRepository.java deleted file mode 100644 index d044eb33a2..0000000000 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/registration/SupplierClientRegistrationRepository.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.client.registration; - -import java.util.Iterator; -import java.util.function.Supplier; - -import org.springframework.util.Assert; -import org.springframework.util.function.SingletonSupplier; - -/** - * A {@link ClientRegistrationRepository} that lazily calls to retrieve - * {@link ClientRegistration}(s) when requested. - * - * @author Justin Tay - * @since 6.2 - * @see ClientRegistrationRepository - * @see ClientRegistration - */ -public final class SupplierClientRegistrationRepository - implements ClientRegistrationRepository, Iterable { - - private final Supplier repositorySupplier; - - /** - * Constructs an {@code SupplierClientRegistrationRepository} using the provided - * parameters. - * @param repositorySupplier the client registration repository supplier - */ - public > SupplierClientRegistrationRepository( - Supplier repositorySupplier) { - Assert.notNull(repositorySupplier, "repositorySupplier cannot be null"); - this.repositorySupplier = SingletonSupplier.of(repositorySupplier); - } - - @Override - public ClientRegistration findByRegistrationId(String registrationId) { - Assert.hasText(registrationId, "registrationId cannot be empty"); - return this.repositorySupplier.get().findByRegistrationId(registrationId); - } - - /** - * Returns an {@code Iterator} of {@link ClientRegistration}. - * @return an {@code Iterator} - */ - @Override - public Iterator iterator() { - return ((Iterable) this.repositorySupplier.get()).iterator(); - } - -} diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java index 90c33fd41b..f24461e3fe 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserService.java @@ -195,7 +195,7 @@ public class DefaultReactiveOAuth2UserService implements ReactiveOAuth2UserServi private static Mono parse(ClientResponse httpResponse) { String wwwAuth = httpResponse.headers().asHttpHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE); - if (StringUtils.hasLength(wwwAuth)) { + if (!StringUtils.isEmpty(wwwAuth)) { // Bearer token error? return Mono.fromCallable(() -> UserInfoErrorResponse.parse(wwwAuth)); } diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationCodeGrantFilter.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationCodeGrantFilter.java index 9c04e1d56d..69a9987669 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationCodeGrantFilter.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/OAuth2AuthorizationCodeGrantFilter.java @@ -238,10 +238,10 @@ public class OAuth2AuthorizationCodeGrantFilter extends OncePerRequestFilter { OAuth2Error error = ex.getError(); UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(authorizationRequest.getRedirectUri()) .queryParam(OAuth2ParameterNames.ERROR, error.getErrorCode()); - if (StringUtils.hasLength(error.getDescription())) { + if (!StringUtils.isEmpty(error.getDescription())) { uriBuilder.queryParam(OAuth2ParameterNames.ERROR_DESCRIPTION, error.getDescription()); } - if (StringUtils.hasLength(error.getUri())) { + if (!StringUtils.isEmpty(error.getUri())) { uriBuilder.queryParam(OAuth2ParameterNames.ERROR_URI, error.getUri()); } this.redirectStrategy.sendRedirect(request, response, uriBuilder.build().encode().toString()); diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/method/annotation/OAuth2AuthorizedClientArgumentResolver.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/method/annotation/OAuth2AuthorizedClientArgumentResolver.java index 4ac3d7d7ea..6524d9230d 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/method/annotation/OAuth2AuthorizedClientArgumentResolver.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/method/annotation/OAuth2AuthorizedClientArgumentResolver.java @@ -111,7 +111,7 @@ public final class OAuth2AuthorizedClientArgumentResolver implements HandlerMeth public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) { String clientRegistrationId = this.resolveClientRegistrationId(parameter); - if (!StringUtils.hasLength(clientRegistrationId)) { + if (StringUtils.isEmpty(clientRegistrationId)) { throw new IllegalArgumentException("Unable to resolve the Client Registration Identifier. " + "It must be provided via @RegisteredOAuth2AuthorizedClient(\"client1\") or " + "@RegisteredOAuth2AuthorizedClient(registrationId = \"client1\")."); @@ -137,10 +137,10 @@ public final class OAuth2AuthorizedClientArgumentResolver implements HandlerMeth RegisteredOAuth2AuthorizedClient authorizedClientAnnotation = AnnotatedElementUtils .findMergedAnnotation(parameter.getParameter(), RegisteredOAuth2AuthorizedClient.class); Authentication principal = this.securityContextHolderStrategy.getContext().getAuthentication(); - if (StringUtils.hasLength(authorizedClientAnnotation.registrationId())) { + if (!StringUtils.isEmpty(authorizedClientAnnotation.registrationId())) { return authorizedClientAnnotation.registrationId(); } - if (StringUtils.hasLength(authorizedClientAnnotation.value())) { + if (!StringUtils.isEmpty(authorizedClientAnnotation.value())) { return authorizedClientAnnotation.value(); } if (principal != null && OAuth2AuthenticationToken.class.isAssignableFrom(principal.getClass())) { diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunction.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunction.java index ffdb93d165..136cf6ebe6 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunction.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunction.java @@ -524,7 +524,7 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements authParameters.get(OAuth2ParameterNames.ERROR_URI)); } } - return resolveErrorIfPossible(response.statusCode().value()); + return resolveErrorIfPossible(response.rawStatusCode()); } private OAuth2Error resolveErrorIfPossible(int statusCode) { @@ -538,7 +538,7 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements private Map parseAuthParameters(String wwwAuthenticateHeader) { // @formatter:off return Stream.of(wwwAuthenticateHeader) - .filter((header) -> StringUtils.hasLength(header)) + .filter((header) -> !StringUtils.isEmpty(header)) .filter((header) -> header.toLowerCase().startsWith("bearer")) .map((header) -> header.substring("bearer".length())) .map((header) -> header.split(",")) diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java index d03f648d04..b2eb584051 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunction.java @@ -640,7 +640,7 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement authParameters.get(OAuth2ParameterNames.ERROR_URI)); } } - return resolveErrorIfPossible(response.statusCode().value()); + return resolveErrorIfPossible(response.rawStatusCode()); } private OAuth2Error resolveErrorIfPossible(int statusCode) { @@ -653,7 +653,7 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement private Map parseAuthParameters(String wwwAuthenticateHeader) { // @formatter:off - return Stream.of(wwwAuthenticateHeader).filter((header) -> StringUtils.hasLength(header)) + return Stream.of(wwwAuthenticateHeader).filter((header) -> !StringUtils.isEmpty(header)) .filter((header) -> header.toLowerCase().startsWith("bearer")) .map((header) -> header.substring("bearer".length())) .map((header) -> header.split(",")) diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeReactiveAuthenticationManagerTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeReactiveAuthenticationManagerTests.java index e250ebbe78..af856a2396 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeReactiveAuthenticationManagerTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/authentication/OAuth2AuthorizationCodeReactiveAuthenticationManagerTests.java @@ -70,13 +70,13 @@ public class OAuth2AuthorizationCodeReactiveAuthenticationManagerTests { @Test public void authenticateWhenErrorThenOAuth2AuthorizationException() { this.authorizationResponse = TestOAuth2AuthorizationResponses.error(); - assertThatExceptionOfType(OAuth2AuthorizationException.class).isThrownBy(this::authenticate); + assertThatExceptionOfType(OAuth2AuthorizationException.class).isThrownBy(() -> authenticate()); } @Test public void authenticateWhenStateNotEqualThenOAuth2AuthorizationException() { this.authorizationRequest.state("notequal"); - assertThatExceptionOfType(OAuth2AuthorizationException.class).isThrownBy(this::authenticate); + assertThatExceptionOfType(OAuth2AuthorizationException.class).isThrownBy(() -> authenticate()); } @Test diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultAuthorizationCodeTokenResponseClientTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultAuthorizationCodeTokenResponseClientTests.java index 4aed182e02..d5e4cd9949 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultAuthorizationCodeTokenResponseClientTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultAuthorizationCodeTokenResponseClientTests.java @@ -130,7 +130,7 @@ public class DefaultAuthorizationCodeTokenResponseClientTests { assertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("read", "write"); assertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo("refresh-token-1234"); - assertThat(accessTokenResponse.getAdditionalParameters()).hasSize(2); + assertThat(accessTokenResponse.getAdditionalParameters().size()).isEqualTo(2); assertThat(accessTokenResponse.getAdditionalParameters()).containsEntry("custom_parameter_1", "custom-value-1"); assertThat(accessTokenResponse.getAdditionalParameters()).containsEntry("custom_parameter_2", "custom-value-2"); } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultClientCredentialsTokenResponseClientTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultClientCredentialsTokenResponseClientTests.java index cc8a62f8aa..89431381d5 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultClientCredentialsTokenResponseClientTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/DefaultClientCredentialsTokenResponseClientTests.java @@ -132,7 +132,7 @@ public class DefaultClientCredentialsTokenResponseClientTests { assertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("read", "write"); assertThat(accessTokenResponse.getRefreshToken()).isNull(); - assertThat(accessTokenResponse.getAdditionalParameters()).hasSize(2); + assertThat(accessTokenResponse.getAdditionalParameters().size()).isEqualTo(2); assertThat(accessTokenResponse.getAdditionalParameters()).containsEntry("custom_parameter_1", "custom-value-1"); assertThat(accessTokenResponse.getAdditionalParameters()).containsEntry("custom_parameter_2", "custom-value-2"); } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/NimbusJwtClientAuthenticationParametersConverterTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/NimbusJwtClientAuthenticationParametersConverterTests.java index f4cd199170..6fd2833cd5 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/NimbusJwtClientAuthenticationParametersConverterTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/NimbusJwtClientAuthenticationParametersConverterTests.java @@ -140,8 +140,8 @@ public class NimbusJwtClientAuthenticationParametersConverterTests { NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withPublicKey(rsaJwk.toRSAPublicKey()).build(); Jwt jws = jwtDecoder.decode(encodedJws); - assertThat(jws.getHeaders()).containsEntry(JoseHeaderNames.ALG, SignatureAlgorithm.RS256.getName()); - assertThat(jws.getHeaders()).containsEntry(JoseHeaderNames.KID, rsaJwk.getKeyID()); + assertThat(jws.getHeaders().get(JoseHeaderNames.ALG)).isEqualTo(SignatureAlgorithm.RS256.getName()); + assertThat(jws.getHeaders().get(JoseHeaderNames.KID)).isEqualTo(rsaJwk.getKeyID()); assertThat(jws.getClaim(JwtClaimNames.ISS)).isEqualTo(clientRegistration.getClientId()); assertThat(jws.getSubject()).isEqualTo(clientRegistration.getClientId()); assertThat(jws.getAudience()) @@ -174,8 +174,8 @@ public class NimbusJwtClientAuthenticationParametersConverterTests { NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withSecretKey(secretJwk.toSecretKey()).build(); Jwt jws = jwtDecoder.decode(encodedJws); - assertThat(jws.getHeaders()).containsEntry(JoseHeaderNames.ALG, MacAlgorithm.HS256.getName()); - assertThat(jws.getHeaders()).containsEntry(JoseHeaderNames.KID, secretJwk.getKeyID()); + assertThat(jws.getHeaders().get(JoseHeaderNames.ALG)).isEqualTo(MacAlgorithm.HS256.getName()); + assertThat(jws.getHeaders().get(JoseHeaderNames.KID)).isEqualTo(secretJwk.getKeyID()); assertThat(jws.getClaim(JwtClaimNames.ISS)).isEqualTo(clientRegistration.getClientId()); assertThat(jws.getSubject()).isEqualTo(clientRegistration.getClientId()); assertThat(jws.getAudience()) @@ -217,9 +217,9 @@ public class NimbusJwtClientAuthenticationParametersConverterTests { NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withSecretKey(secretJwk.toSecretKey()).build(); Jwt jws = jwtDecoder.decode(encodedJws); - assertThat(jws.getHeaders()).containsEntry(JoseHeaderNames.ALG, MacAlgorithm.HS256.getName()); - assertThat(jws.getHeaders()).containsEntry(JoseHeaderNames.KID, secretJwk.getKeyID()); - assertThat(jws.getHeaders()).containsEntry(headerName, headerValue); + assertThat(jws.getHeaders().get(JoseHeaderNames.ALG)).isEqualTo(MacAlgorithm.HS256.getName()); + assertThat(jws.getHeaders().get(JoseHeaderNames.KID)).isEqualTo(secretJwk.getKeyID()); + assertThat(jws.getHeaders().get(headerName)).isEqualTo(headerValue); assertThat(jws.getClaim(JwtClaimNames.ISS)).isEqualTo(clientRegistration.getClientId()); assertThat(jws.getSubject()).isEqualTo(clientRegistration.getClientId()); assertThat(jws.getAudience()) diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveAuthorizationCodeTokenResponseClientTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveAuthorizationCodeTokenResponseClientTests.java index cd7c1d31f5..1da58cab87 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveAuthorizationCodeTokenResponseClientTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveAuthorizationCodeTokenResponseClientTests.java @@ -118,7 +118,7 @@ public class WebClientReactiveAuthorizationCodeTokenResponseClientTests { assertThat(accessTokenResponse.getAccessToken().getExpiresAt()).isBetween(expiresAtBefore, expiresAtAfter); assertThat(accessTokenResponse.getAccessToken().getScopes()).containsExactly("openid", "profile"); assertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo("refresh-token-1234"); - assertThat(accessTokenResponse.getAdditionalParameters()).hasSize(2); + assertThat(accessTokenResponse.getAdditionalParameters().size()).isEqualTo(2); assertThat(accessTokenResponse.getAdditionalParameters()).containsEntry("custom_parameter_1", "custom-value-1"); assertThat(accessTokenResponse.getAdditionalParameters()).containsEntry("custom_parameter_2", "custom-value-2"); } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveClientCredentialsTokenResponseClientTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveClientCredentialsTokenResponseClientTests.java index 380240b189..99e8443e0c 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveClientCredentialsTokenResponseClientTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/endpoint/WebClientReactiveClientCredentialsTokenResponseClientTests.java @@ -101,7 +101,7 @@ public class WebClientReactiveClientCredentialsTokenResponseClientTests { this.clientRegistration.build()); OAuth2AccessTokenResponse response = this.client.getTokenResponse(request).block(); RecordedRequest actualRequest = this.server.takeRequest(); - String body = actualRequest.getBody().readUtf8(); + String body = actualRequest.getUtf8Body(); assertThat(response.getAccessToken()).isNotNull(); assertThat(response.getAccessToken().getScopes()).containsExactly("create"); assertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION)) @@ -157,7 +157,7 @@ public class WebClientReactiveClientCredentialsTokenResponseClientTests { OAuth2ClientCredentialsGrantRequest request = new OAuth2ClientCredentialsGrantRequest(registration); OAuth2AccessTokenResponse response = this.client.getTokenResponse(request).block(); RecordedRequest actualRequest = this.server.takeRequest(); - String body = actualRequest.getBody().readUtf8(); + String body = actualRequest.getUtf8Body(); assertThat(response.getAccessToken()).isNotNull(); assertThat(response.getAccessToken().getScopes()).containsExactly("create"); assertThat(actualRequest.getHeader(HttpHeaders.AUTHORIZATION)).isNull(); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthenticationTokenMixinTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthenticationTokenMixinTests.java index e4c76b69db..8d100fddca 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthenticationTokenMixinTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/OAuth2AuthenticationTokenMixinTests.java @@ -229,7 +229,7 @@ public class OAuth2AuthenticationTokenMixinTests { String authoritiesJson = (oidcUserAuthority != null) ? asJson(oidcUserAuthority) : (oauth2UserAuthority != null) ? asJson(oauth2UserAuthority) : ""; if (!simpleAuthorities.isEmpty()) { - if (StringUtils.hasLength(authoritiesJson)) { + if (!StringUtils.isEmpty(authoritiesJson)) { authoritiesJson += ","; } authoritiesJson += asJson(simpleAuthorities); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/logout/TestOidcLogoutTokens.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/logout/TestOidcLogoutTokens.java deleted file mode 100644 index 64602687f6..0000000000 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/authentication/logout/TestOidcLogoutTokens.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.client.oidc.authentication.logout; - -import java.time.Instant; -import java.util.Collections; - -import org.springframework.security.oauth2.core.oidc.user.OidcUser; - -public final class TestOidcLogoutTokens { - - public static OidcLogoutToken.Builder withUser(OidcUser user) { - OidcLogoutToken.Builder builder = OidcLogoutToken.withTokenValue("token") - .audience(Collections.singleton("client-id")) - .issuedAt(Instant.now()) - .issuer(user.getIssuer().toString()) - .jti("id") - .subject(user.getSubject()); - if (user.hasClaim(LogoutTokenClaimNames.SID)) { - builder.sessionId(user.getClaimAsString(LogoutTokenClaimNames.SID)); - } - return builder; - } - - public static OidcLogoutToken.Builder withSessionId(String issuer, String sessionId) { - return OidcLogoutToken.withTokenValue("token") - .audience(Collections.singleton("client-id")) - .issuedAt(Instant.now()) - .issuer(issuer) - .jti("id") - .sessionId(sessionId); - } - - public static OidcLogoutToken.Builder withSubject(String issuer, String subject) { - return OidcLogoutToken.withTokenValue("token") - .audience(Collections.singleton("client-id")) - .issuedAt(Instant.now()) - .issuer(issuer) - .jti("id") - .subject(subject); - } - - private TestOidcLogoutTokens() { - - } - -} diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/session/InMemoryOidcSessionRegistryTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/session/InMemoryOidcSessionRegistryTests.java deleted file mode 100644 index 6064e74b03..0000000000 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/session/InMemoryOidcSessionRegistryTests.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.client.oidc.session; - -import org.junit.jupiter.api.Test; - -import org.springframework.security.core.authority.AuthorityUtils; -import org.springframework.security.oauth2.client.oidc.authentication.logout.OidcLogoutToken; -import org.springframework.security.oauth2.client.oidc.authentication.logout.TestOidcLogoutTokens; -import org.springframework.security.oauth2.core.oidc.OidcIdToken; -import org.springframework.security.oauth2.core.oidc.TestOidcIdTokens; -import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser; -import org.springframework.security.oauth2.core.oidc.user.OidcUser; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link InMemoryOidcSessionRegistry} - */ -public class InMemoryOidcSessionRegistryTests { - - @Test - public void registerWhenDefaultsThenStoresSessionInformation() { - InMemoryOidcSessionRegistry sessionRegistry = new InMemoryOidcSessionRegistry(); - String sessionId = "client"; - OidcSessionInformation info = TestOidcSessionInformations.create(sessionId); - sessionRegistry.saveSessionInformation(info); - OidcLogoutToken logoutToken = TestOidcLogoutTokens.withUser(info.getPrincipal()).build(); - Iterable infos = sessionRegistry.removeSessionInformation(logoutToken); - assertThat(infos).containsExactly(info); - } - - @Test - public void registerWhenIdTokenHasSessionIdThenStoresSessionInformation() { - InMemoryOidcSessionRegistry sessionRegistry = new InMemoryOidcSessionRegistry(); - OidcIdToken idToken = TestOidcIdTokens.idToken().claim("sid", "provider").build(); - OidcUser user = new DefaultOidcUser(AuthorityUtils.NO_AUTHORITIES, idToken); - OidcSessionInformation info = TestOidcSessionInformations.create("client", user); - sessionRegistry.saveSessionInformation(info); - OidcLogoutToken logoutToken = TestOidcLogoutTokens.withSessionId(idToken.getIssuer().toString(), "provider") - .build(); - Iterable infos = sessionRegistry.removeSessionInformation(logoutToken); - assertThat(infos).containsExactly(info); - } - - @Test - public void unregisterWhenMultipleSessionsThenRemovesAllMatching() { - InMemoryOidcSessionRegistry sessionRegistry = new InMemoryOidcSessionRegistry(); - OidcIdToken idToken = TestOidcIdTokens.idToken().claim("sid", "providerOne").subject("otheruser").build(); - OidcUser user = new DefaultOidcUser(AuthorityUtils.NO_AUTHORITIES, idToken); - OidcSessionInformation oneSession = TestOidcSessionInformations.create("clientOne", user); - sessionRegistry.saveSessionInformation(oneSession); - idToken = TestOidcIdTokens.idToken().claim("sid", "providerTwo").build(); - user = new DefaultOidcUser(AuthorityUtils.NO_AUTHORITIES, idToken); - OidcSessionInformation twoSession = TestOidcSessionInformations.create("clientTwo", user); - sessionRegistry.saveSessionInformation(twoSession); - idToken = TestOidcIdTokens.idToken().claim("sid", "providerThree").build(); - user = new DefaultOidcUser(AuthorityUtils.NO_AUTHORITIES, idToken); - OidcSessionInformation threeSession = TestOidcSessionInformations.create("clientThree", user); - sessionRegistry.saveSessionInformation(threeSession); - OidcLogoutToken logoutToken = TestOidcLogoutTokens - .withSubject(idToken.getIssuer().toString(), idToken.getSubject()) - .build(); - Iterable infos = sessionRegistry.removeSessionInformation(logoutToken); - assertThat(infos).containsExactlyInAnyOrder(twoSession, threeSession); - logoutToken = TestOidcLogoutTokens.withSubject(idToken.getIssuer().toString(), "otheruser").build(); - infos = sessionRegistry.removeSessionInformation(logoutToken); - assertThat(infos).containsExactly(oneSession); - } - - @Test - public void unregisterWhenNoSessionsThenEmptyList() { - InMemoryOidcSessionRegistry sessionRegistry = new InMemoryOidcSessionRegistry(); - OidcIdToken idToken = TestOidcIdTokens.idToken().claim("sid", "provider").build(); - OidcUser user = new DefaultOidcUser(AuthorityUtils.NO_AUTHORITIES, idToken); - OidcSessionInformation info = TestOidcSessionInformations.create("client", user); - sessionRegistry.saveSessionInformation(info); - OidcLogoutToken logoutToken = TestOidcLogoutTokens.withSessionId(idToken.getIssuer().toString(), "wrong") - .build(); - Iterable infos = sessionRegistry.removeSessionInformation(logoutToken); - assertThat(infos).isNotNull(); - assertThat(infos).isEmpty(); - logoutToken = TestOidcLogoutTokens.withSessionId("https://wrong", "provider").build(); - infos = sessionRegistry.removeSessionInformation(logoutToken); - assertThat(infos).isNotNull(); - assertThat(infos).isEmpty(); - } - -} diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/session/TestOidcSessionInformations.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/session/TestOidcSessionInformations.java deleted file mode 100644 index 47f64868de..0000000000 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/session/TestOidcSessionInformations.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.client.oidc.session; - -import java.util.Map; - -import org.springframework.security.oauth2.core.oidc.user.OidcUser; -import org.springframework.security.oauth2.core.oidc.user.TestOidcUsers; - -/** - * Sample {@link OidcSessionInformation} instances - */ -public final class TestOidcSessionInformations { - - public static OidcSessionInformation create() { - return create("sessionId"); - } - - public static OidcSessionInformation create(String sessionId) { - return create(sessionId, TestOidcUsers.create()); - } - - public static OidcSessionInformation create(String sessionId, OidcUser user) { - return new OidcSessionInformation(sessionId, Map.of("_csrf", "token"), user); - } - - private TestOidcSessionInformations() { - - } - -} diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserServiceTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserServiceTests.java index 310667e2ff..45ca6f34b1 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserServiceTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserServiceTests.java @@ -235,7 +235,7 @@ public class OidcUserServiceTests { .loadUser(new OidcUserRequest(clientRegistration, this.accessToken, this.idToken)); assertThat(user.getIdToken()).isNotNull(); assertThat(user.getUserInfo()).isNotNull(); - assertThat(user.getUserInfo().getClaims()).hasSize(6); + assertThat(user.getUserInfo().getClaims().size()).isEqualTo(6); assertThat(user.getIdToken()).isEqualTo(this.idToken); assertThat(user.getName()).isEqualTo("subject1"); assertThat(user.getUserInfo().getSubject()).isEqualTo("subject1"); @@ -244,7 +244,7 @@ public class OidcUserServiceTests { assertThat(user.getUserInfo().getFamilyName()).isEqualTo("last"); assertThat(user.getUserInfo().getPreferredUsername()).isEqualTo("user1"); assertThat(user.getUserInfo().getEmail()).isEqualTo("user1@example.com"); - assertThat(user.getAuthorities()).hasSize(3); + assertThat(user.getAuthorities().size()).isEqualTo(3); assertThat(user.getAuthorities().iterator().next()).isInstanceOf(OidcUserAuthority.class); OidcUserAuthority userAuthority = (OidcUserAuthority) user.getAuthorities().iterator().next(); assertThat(userAuthority.getAuthority()).isEqualTo("OIDC_USER"); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java index b3b74e805d..2da662c82e 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/ClientRegistrationsTests.java @@ -479,12 +479,12 @@ public class ClientRegistrationsTests { final Dispatcher dispatcher = new Dispatcher() { @Override public MockResponse dispatch(RecordedRequest request) { - return switch (request.getPath()) { - case "/.well-known/oauth-authorization-server/issuer1", - "/.well-known/oauth-authorization-server/" -> - buildSuccessMockResponse(responseBody); - default -> new MockResponse().setResponseCode(404); - }; + switch (request.getPath()) { + case "/.well-known/oauth-authorization-server/issuer1": + case "/.well-known/oauth-authorization-server/": + return buildSuccessMockResponse(responseBody); + } + return new MockResponse().setResponseCode(404); } }; this.server.setDispatcher(dispatcher); @@ -515,11 +515,12 @@ public class ClientRegistrationsTests { final Dispatcher dispatcher = new Dispatcher() { @Override public MockResponse dispatch(RecordedRequest request) { - return switch (request.getPath()) { - case "/issuer1/.well-known/openid-configuration", "/.well-known/openid-configuration/" -> - buildSuccessMockResponse(responseBody); - default -> new MockResponse().setResponseCode(404); - }; + switch (request.getPath()) { + case "/issuer1/.well-known/openid-configuration": + case "/.well-known/openid-configuration/": + return buildSuccessMockResponse(responseBody); + } + return new MockResponse().setResponseCode(404); } }; this.server.setDispatcher(dispatcher); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/SupplierClientRegistrationRepositoryTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/SupplierClientRegistrationRepositoryTests.java deleted file mode 100644 index e7ae981d87..0000000000 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/registration/SupplierClientRegistrationRepositoryTests.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.client.registration; - -import java.util.Collections; -import java.util.function.Supplier; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link SupplierClientRegistrationRepository}. - * - * @author Justin Tay - * @since 6.2 - */ -@ExtendWith(MockitoExtension.class) -public class SupplierClientRegistrationRepositoryTests { - - private ClientRegistration registration = TestClientRegistrations.clientRegistration().build(); - - private SupplierClientRegistrationRepository clients = new SupplierClientRegistrationRepository( - () -> new InMemoryClientRegistrationRepository(this.registration)); - - @Mock - Supplier clientRegistrationRepositorySupplier; - - @Test - public void constructorMapClientRegistrationWhenNullThenIllegalArgumentException() { - assertThatIllegalArgumentException().isThrownBy(() -> new SupplierClientRegistrationRepository(null)); - } - - @Test - public void constructorMapClientRegistrationWhenEmptyMapThenRepositoryIsEmpty() { - SupplierClientRegistrationRepository clients = new SupplierClientRegistrationRepository( - () -> new InMemoryClientRegistrationRepository(Collections.emptyMap())); - assertThat(clients).isEmpty(); - } - - @Test - public void findByRegistrationIdWhenFoundThenFound() { - String id = this.registration.getRegistrationId(); - assertThat(this.clients.findByRegistrationId(id)).isEqualTo(this.registration); - } - - @Test - public void findByRegistrationIdWhenNotFoundThenNull() { - String id = this.registration.getRegistrationId() + "MISSING"; - assertThat(this.clients.findByRegistrationId(id)).isNull(); - } - - @Test - public void findByRegistrationIdWhenNullIdThenIllegalArgumentException() { - assertThatIllegalArgumentException().isThrownBy(() -> this.clients.findByRegistrationId(null)); - } - - @Test - public void findByRegistrationIdThenSingletonSupplierCached() { - SupplierClientRegistrationRepository test = new SupplierClientRegistrationRepository( - this.clientRegistrationRepositorySupplier); - given(this.clientRegistrationRepositorySupplier.get()) - .willReturn(new InMemoryClientRegistrationRepository(this.registration)); - String id = this.registration.getRegistrationId(); - assertThat(test.findByRegistrationId(id)).isEqualTo(this.registration); - id = this.registration.getRegistrationId(); - assertThat(test.findByRegistrationId(id)).isEqualTo(this.registration); - verify(this.clientRegistrationRepositorySupplier, times(1)).get(); - } - - @Test - public void iteratorWhenRemoveThenThrowsUnsupportedOperationException() { - assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(this.clients.iterator()::remove); - } - - @Test - public void iteratorWhenGetThenContainsAll() { - assertThat(this.clients).containsOnly(this.registration); - } - -} diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserServiceTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserServiceTests.java index 361100ec6f..871d20effe 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserServiceTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultOAuth2UserServiceTests.java @@ -144,14 +144,14 @@ public class DefaultOAuth2UserServiceTests { .build(); OAuth2User user = this.userService.loadUser(new OAuth2UserRequest(clientRegistration, this.accessToken)); assertThat(user.getName()).isEqualTo("user1"); - assertThat(user.getAttributes()).hasSize(6); + assertThat(user.getAttributes().size()).isEqualTo(6); assertThat((String) user.getAttribute("user-name")).isEqualTo("user1"); assertThat((String) user.getAttribute("first-name")).isEqualTo("first"); assertThat((String) user.getAttribute("last-name")).isEqualTo("last"); assertThat((String) user.getAttribute("middle-name")).isEqualTo("middle"); assertThat((String) user.getAttribute("address")).isEqualTo("address"); assertThat((String) user.getAttribute("email")).isEqualTo("user1@example.com"); - assertThat(user.getAuthorities()).hasSize(1); + assertThat(user.getAuthorities().size()).isEqualTo(1); assertThat(user.getAuthorities().iterator().next()).isInstanceOf(OAuth2UserAuthority.class); OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next(); assertThat(userAuthority.getAuthority()).isEqualTo("OAUTH2_USER"); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserServiceTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserServiceTests.java index c9989ae320..039b09205e 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserServiceTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/userinfo/DefaultReactiveOAuth2UserServiceTests.java @@ -132,14 +132,14 @@ public class DefaultReactiveOAuth2UserServiceTests { enqueueApplicationJsonBody(userInfoResponse); OAuth2User user = this.userService.loadUser(oauth2UserRequest()).block(); assertThat(user.getName()).isEqualTo("user1"); - assertThat(user.getAttributes()).hasSize(6); + assertThat(user.getAttributes().size()).isEqualTo(6); assertThat((String) user.getAttribute("id")).isEqualTo("user1"); assertThat((String) user.getAttribute("first-name")).isEqualTo("first"); assertThat((String) user.getAttribute("last-name")).isEqualTo("last"); assertThat((String) user.getAttribute("middle-name")).isEqualTo("middle"); assertThat((String) user.getAttribute("address")).isEqualTo("address"); assertThat((String) user.getAttribute("email")).isEqualTo("user1@example.com"); - assertThat(user.getAuthorities()).hasSize(1); + assertThat(user.getAuthorities().size()).isEqualTo(1); assertThat(user.getAuthorities().iterator().next()).isInstanceOf(OAuth2UserAuthority.class); OAuth2UserAuthority userAuthority = (OAuth2UserAuthority) user.getAuthorities().iterator().next(); assertThat(userAuthority.getAuthority()).isEqualTo("OAUTH2_USER"); diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunctionTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunctionTests.java index f847d2b5ec..eb83df5b27 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunctionTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServerOAuth2AuthorizedClientExchangeFilterFunctionTests.java @@ -495,7 +495,7 @@ public class ServerOAuth2AuthorizedClientExchangeFilterFunctionTests { .attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient)) .build(); // @formatter:on - given(this.exchange.getResponse().statusCode()).willReturn(HttpStatus.UNAUTHORIZED); + given(this.exchange.getResponse().rawStatusCode()).willReturn(HttpStatus.UNAUTHORIZED.value()); this.function.filter(request, this.exchange).contextWrite(serverWebExchange()).block(); assertThat(publisherProbe.wasSubscribed()).isTrue(); verify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(), @@ -570,7 +570,7 @@ public class ServerOAuth2AuthorizedClientExchangeFilterFunctionTests { .attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient)) .build(); // @formatter:on - given(this.exchange.getResponse().statusCode()).willReturn(HttpStatus.FORBIDDEN); + given(this.exchange.getResponse().rawStatusCode()).willReturn(HttpStatus.FORBIDDEN.value()); this.function.filter(request, this.exchange).contextWrite(serverWebExchange()).block(); assertThat(publisherProbe.wasSubscribed()).isTrue(); verify(this.authorizationFailureHandler).onAuthorizationFailure(this.authorizationExceptionCaptor.capture(), @@ -701,7 +701,7 @@ public class ServerOAuth2AuthorizedClientExchangeFilterFunctionTests { ClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create("https://example.com")) .attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient)) .build(); - given(this.exchange.getResponse().statusCode()).willReturn(HttpStatus.BAD_REQUEST); + given(this.exchange.getResponse().rawStatusCode()).willReturn(HttpStatus.BAD_REQUEST.value()); this.function.filter(request, this.exchange).contextWrite(serverWebExchange()).block(); verify(this.authorizationFailureHandler, never()).onAuthorizationFailure(any(), any(), any()); } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunctionTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunctionTests.java index c963a11897..872781be59 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunctionTests.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/web/reactive/function/client/ServletOAuth2AuthorizedClientExchangeFilterFunctionTests.java @@ -680,7 +680,7 @@ public class ServletOAuth2AuthorizedClientExchangeFilterFunctionTests { .attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletRequest(servletRequest)) .attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletResponse(servletResponse)) .build(); - given(this.exchange.getResponse().statusCode()).willReturn(httpStatus); + given(this.exchange.getResponse().rawStatusCode()).willReturn(httpStatus.value()); given(this.exchange.getResponse().headers()).willReturn(mock(ClientResponse.Headers.class)); this.function.setAuthorizationFailureHandler(this.authorizationFailureHandler); this.function.filter(request, this.exchange).block(); @@ -825,7 +825,7 @@ public class ServletOAuth2AuthorizedClientExchangeFilterFunctionTests { .attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletRequest(servletRequest)) .attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction.httpServletResponse(servletResponse)) .build(); - given(this.exchange.getResponse().statusCode()).willReturn(HttpStatus.BAD_REQUEST); + given(this.exchange.getResponse().rawStatusCode()).willReturn(HttpStatus.BAD_REQUEST.value()); given(this.exchange.getResponse().headers()).willReturn(mock(ClientResponse.Headers.class)); this.function.setAuthorizationFailureHandler(this.authorizationFailureHandler); this.function.filter(request, this.exchange).block(); diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClaimAccessor.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClaimAccessor.java index 46256b58d3..09e253ebb3 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClaimAccessor.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClaimAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -107,7 +107,7 @@ public interface ClaimAccessor { } Object claimValue = getClaims().get(claim); Instant convertedValue = ClaimConversionService.getSharedInstance().convert(claimValue, Instant.class); - Assert.notNull(convertedValue, + Assert.isTrue(convertedValue != null, () -> "Unable to convert claim '" + claim + "' of type '" + claimValue.getClass() + "' to Instant."); return convertedValue; } @@ -123,7 +123,7 @@ public interface ClaimAccessor { } Object claimValue = getClaims().get(claim); URL convertedValue = ClaimConversionService.getSharedInstance().convert(claimValue, URL.class); - Assert.notNull(convertedValue, + Assert.isTrue(convertedValue != null, () -> "Unable to convert claim '" + claim + "' of type '" + claimValue.getClass() + "' to URL."); return convertedValue; } @@ -148,7 +148,7 @@ public interface ClaimAccessor { Object claimValue = getClaims().get(claim); Map convertedValue = (Map) ClaimConversionService.getSharedInstance() .convert(claimValue, sourceDescriptor, targetDescriptor); - Assert.notNull(convertedValue, + Assert.isTrue(convertedValue != null, () -> "Unable to convert claim '" + claim + "' of type '" + claimValue.getClass() + "' to Map."); return convertedValue; } @@ -173,7 +173,7 @@ public interface ClaimAccessor { Object claimValue = getClaims().get(claim); List convertedValue = (List) ClaimConversionService.getSharedInstance() .convert(claimValue, sourceDescriptor, targetDescriptor); - Assert.notNull(convertedValue, + Assert.isTrue(convertedValue != null, () -> "Unable to convert claim '" + claim + "' of type '" + claimValue.getClass() + "' to List."); return convertedValue; } diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClientAuthenticationMethod.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClientAuthenticationMethod.java index 0b986dcc55..fdb843a1ab 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClientAuthenticationMethod.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClientAuthenticationMethod.java @@ -98,9 +98,4 @@ public final class ClientAuthenticationMethod implements Serializable { return getValue().hashCode(); } - @Override - public String toString() { - return this.value; - } - } diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/authorization/OAuth2AuthorizationManagers.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/authorization/OAuth2AuthorizationManagers.java deleted file mode 100644 index 7697b4a9d6..0000000000 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/authorization/OAuth2AuthorizationManagers.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.core.authorization; - -import org.springframework.security.authorization.AuthorityAuthorizationManager; -import org.springframework.security.authorization.AuthorizationManager; -import org.springframework.security.core.Authentication; -import org.springframework.util.Assert; - -/** - * A convenience class for creating OAuth 2.0-specific {@link AuthorizationManager}s. - * - * @author Mario Petrovski - * @author Josh Cummings - * @since 6.2 - * @see AuthorityAuthorizationManager - */ -public final class OAuth2AuthorizationManagers { - - private OAuth2AuthorizationManagers() { - } - - /** - * Create an {@link AuthorizationManager} that requires an {@link Authentication} to - * have a {@code SCOPE_scope} authority. - * - *

    - * For example, if you call {@code hasScope("read")}, then this will require that each - * authentication have a {@link org.springframework.security.core.GrantedAuthority} - * whose value is {@code SCOPE_read}. - * - *

    - * This would equivalent to calling - * {@code AuthorityAuthorizationManager#hasAuthority("SCOPE_read")}. - * @param scope the scope value to require - * @param the secure object - * @return an {@link AuthorizationManager} that requires a {@code "SCOPE_scope"} - * authority - */ - public static AuthorizationManager hasScope(String scope) { - assertScope(scope); - return AuthorityAuthorizationManager.hasAuthority("SCOPE_" + scope); - } - - /** - * Create an {@link AuthorizationManager} that requires an {@link Authentication} to - * have at least one authority among {@code SCOPE_scope1}, {@code SCOPE_scope2}, ... - * {@code SCOPE_scopeN}. - * - *

    - * For example, if you call {@code hasAnyScope("read", "write")}, then this will - * require that each authentication have at least a - * {@link org.springframework.security.core.GrantedAuthority} whose value is either - * {@code SCOPE_read} or {@code SCOPE_write}. - * - *

    - * This would equivalent to calling - * {@code AuthorityAuthorizationManager#hasAnyAuthority("SCOPE_read", "SCOPE_write")}. - * @param scopes the scope values to allow - * @param the secure object - * @return an {@link AuthorizationManager} that requires at least one authority among - * {@code "SCOPE_scope1"}, {@code SCOPE_scope2}, ... {@code SCOPE_scopeN}. - * - */ - public static AuthorizationManager hasAnyScope(String... scopes) { - String[] mappedScopes = new String[scopes.length]; - for (int i = 0; i < scopes.length; i++) { - assertScope(scopes[i]); - mappedScopes[i] = "SCOPE_" + scopes[i]; - } - return AuthorityAuthorizationManager.hasAnyAuthority(mappedScopes); - } - - private static void assertScope(String scope) { - Assert.isTrue(!scope.startsWith("SCOPE_"), - () -> scope + " should not start with SCOPE_ since SCOPE_" - + " is automatically prepended when using hasScope and hasAnyScope. Consider using " - + " AuthorityAuthorizationManager#hasAuthority or #hasAnyAuthority instead."); - } - -} diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/authorization/OAuth2ReactiveAuthorizationManagers.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/authorization/OAuth2ReactiveAuthorizationManagers.java deleted file mode 100644 index 753cf1aabd..0000000000 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/authorization/OAuth2ReactiveAuthorizationManagers.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.core.authorization; - -import org.springframework.security.authorization.AuthorityReactiveAuthorizationManager; -import org.springframework.security.authorization.AuthorizationManager; -import org.springframework.security.authorization.ReactiveAuthorizationManager; -import org.springframework.security.core.Authentication; -import org.springframework.util.Assert; - -/** - * A convenience class for creating OAuth 2.0-specific {@link AuthorizationManager}s. - * - * @author Josh Cummings - * @since 6.2 - * @see AuthorityReactiveAuthorizationManager - */ -public final class OAuth2ReactiveAuthorizationManagers { - - private OAuth2ReactiveAuthorizationManagers() { - } - - /** - * Create a {@link ReactiveAuthorizationManager} that requires an - * {@link Authentication} to have a {@code SCOPE_scope} authority. - * - *

    - * For example, if you call {@code hasScope("read")}, then this will require that each - * authentication have a {@link org.springframework.security.core.GrantedAuthority} - * whose value is {@code SCOPE_read}. - * - *

    - * This would equivalent to calling - * {@code AuthorityReactiveAuthorizationManager#hasAuthority("SCOPE_read")}. - * @param scope the scope value to require - * @param the secure object - * @return an {@link ReactiveAuthorizationManager} that requires a - * {@code "SCOPE_scope"} authority - */ - public static ReactiveAuthorizationManager hasScope(String scope) { - assertScope(scope); - return AuthorityReactiveAuthorizationManager.hasAuthority("SCOPE_" + scope); - } - - /** - * Create a {@link ReactiveAuthorizationManager} that requires an - * {@link Authentication} to have at least one authority among {@code SCOPE_scope1}, - * {@code SCOPE_scope2}, ... {@code SCOPE_scopeN}. - * - *

    - * For example, if you call {@code hasAnyScope("read", "write")}, then this will - * require that each authentication have at least a - * {@link org.springframework.security.core.GrantedAuthority} whose value is either - * {@code SCOPE_read} or {@code SCOPE_write}. - * - *

    - * This would equivalent to calling - * {@code AuthorityReactiveAuthorizationManager#hasAnyAuthority("SCOPE_read", "SCOPE_write")}. - * @param scopes the scope values to allow - * @param the secure object - * @return an {@link ReactiveAuthorizationManager} that requires at least one - * authority among {@code "SCOPE_scope1"}, {@code SCOPE_scope2}, ... - * {@code SCOPE_scopeN}. - */ - public static ReactiveAuthorizationManager hasAnyScope(String... scopes) { - String[] mappedScopes = new String[scopes.length]; - for (int i = 0; i < scopes.length; i++) { - assertScope(scopes[i]); - mappedScopes[i] = "SCOPE_" + scopes[i]; - } - return AuthorityReactiveAuthorizationManager.hasAnyAuthority(mappedScopes); - } - - private static void assertScope(String scope) { - Assert.isTrue(!scope.startsWith("SCOPE_"), - () -> scope + " should not start with SCOPE_ since SCOPE_" - + " is automatically prepended when using hasScope and hasAnyScope. Consider using " - + " AuthorityReactiveAuthorizationManager#hasAuthority or #hasAnyAuthority instead."); - } - -} diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/authorization/OAuth2AuthorizationManagersTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/authorization/OAuth2AuthorizationManagersTests.java deleted file mode 100644 index e8e39d77c4..0000000000 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/authorization/OAuth2AuthorizationManagersTests.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.core.authorization; - -import org.junit.jupiter.api.Test; - -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.authorization.AuthorizationManager; -import org.springframework.security.core.Authentication; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -/** - * Tests for {@link OAuth2AuthorizationManagers} - * - * @author Mario Petrovski - * @author Josh Cummings - */ -public class OAuth2AuthorizationManagersTests { - - @Test - void hasScopeWhenInvalidScopeThenThrowIllegalArgument() { - String scope = "SCOPE_invalid"; - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> OAuth2AuthorizationManagers.hasScope(scope)) - .withMessageContaining("SCOPE_invalid should not start with SCOPE_"); - } - - @Test - void hasAnyScopeWhenInvalidScopeThenThrowIllegalArgument() { - String[] scopes = { "read", "write", "SCOPE_invalid" }; - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> OAuth2AuthorizationManagers.hasAnyScope(scopes)) - .withMessageContaining("SCOPE_invalid should not start with SCOPE_"); - } - - @Test - void hasScopeWhenValidScopeThenAuthorizationManager() { - String scope = "read"; - AuthorizationManager authorizationManager = OAuth2AuthorizationManagers.hasScope(scope); - authorizationManager.verify(() -> hasScope(scope), new Object()); - assertThatExceptionOfType(AccessDeniedException.class) - .isThrownBy(() -> authorizationManager.verify(() -> hasScope("wrong"), new Object())); - } - - @Test - void hasAnyScopeWhenValidScopesThenAuthorizationManager() { - String[] scopes = { "read", "write" }; - AuthorizationManager authorizationManager = OAuth2AuthorizationManagers.hasAnyScope(scopes); - for (String scope : scopes) { - authorizationManager.verify(() -> hasScope(scope), new Object()); - } - assertThatExceptionOfType(AccessDeniedException.class) - .isThrownBy(() -> authorizationManager.verify(() -> hasScope("wrong"), new Object())); - } - - Authentication hasScope(String scope) { - return new TestingAuthenticationToken("user", "pass", "SCOPE_" + scope); - } - -} diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/authorization/OAuth2ReactiveAuthorizationManagersTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/authorization/OAuth2ReactiveAuthorizationManagersTests.java deleted file mode 100644 index e2dd11c3c0..0000000000 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/authorization/OAuth2ReactiveAuthorizationManagersTests.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2002-2023 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.oauth2.core.authorization; - -import org.junit.jupiter.api.Test; -import reactor.core.publisher.Mono; - -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.authorization.ReactiveAuthorizationManager; -import org.springframework.security.core.Authentication; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -/** - * Tests for {@link OAuth2ReactiveAuthorizationManagers} - * - * @author Josh Cummings - */ -public class OAuth2ReactiveAuthorizationManagersTests { - - @Test - void hasScopeWhenInvalidScopeThenThrowIllegalArgument() { - String scope = "SCOPE_invalid"; - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> OAuth2ReactiveAuthorizationManagers.hasScope(scope)) - .withMessageContaining("SCOPE_invalid should not start with SCOPE_"); - } - - @Test - void hasAnyScopeWhenInvalidScopeThenThrowIllegalArgument() { - String[] scopes = { "read", "write", "SCOPE_invalid" }; - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> OAuth2ReactiveAuthorizationManagers.hasAnyScope(scopes)) - .withMessageContaining("SCOPE_invalid should not start with SCOPE_"); - } - - @Test - void hasScopeWhenValidScopeThenAuthorizationManager() { - String scope = "read"; - ReactiveAuthorizationManager authorizationManager = OAuth2ReactiveAuthorizationManagers.hasScope(scope); - authorizationManager.verify(hasScope(scope), new Object()).block(); - assertThatExceptionOfType(AccessDeniedException.class) - .isThrownBy(() -> authorizationManager.verify(hasScope("wrong"), new Object()).block()); - } - - @Test - void hasAnyScopeWhenValidScopesThenAuthorizationManager() { - String[] scopes = { "read", "write" }; - ReactiveAuthorizationManager authorizationManager = OAuth2ReactiveAuthorizationManagers - .hasAnyScope(scopes); - for (String scope : scopes) { - authorizationManager.verify(hasScope(scope), new Object()).block(); - } - assertThatExceptionOfType(AccessDeniedException.class) - .isThrownBy(() -> authorizationManager.verify(hasScope("wrong"), new Object()).block()); - } - - Mono hasScope(String scope) { - return Mono.just(new TestingAuthenticationToken("user", "pass", "SCOPE_" + scope)); - } - -} diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/converter/ClaimTypeConverterTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/converter/ClaimTypeConverterTests.java index 19bb064980..3b327a856b 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/converter/ClaimTypeConverterTests.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/converter/ClaimTypeConverterTests.java @@ -139,15 +139,15 @@ public class ClaimTypeConverterTests { claims.put(JSON_ARRAY_CLAIM, jsonArray); claims.put(JSON_OBJECT_CLAIM, jsonObject); claims = this.claimTypeConverter.convert(claims); - assertThat(claims).containsEntry(STRING_CLAIM, "true"); - assertThat(claims).containsEntry(BOOLEAN_CLAIM, Boolean.TRUE); - assertThat(claims).containsEntry(INSTANT_CLAIM, instant); - assertThat(claims).containsEntry(URL_CLAIM, url); - assertThat(claims).containsEntry(COLLECTION_STRING_CLAIM, listString); - assertThat(claims).containsEntry(LIST_STRING_CLAIM, listString); - assertThat(claims).containsEntry(MAP_STRING_OBJECT_CLAIM, mapStringObject); - assertThat(claims).containsEntry(JSON_ARRAY_CLAIM, jsonArrayListString); - assertThat(claims).containsEntry(JSON_OBJECT_CLAIM, jsonObjectMap); + assertThat(claims.get(STRING_CLAIM)).isEqualTo("true"); + assertThat(claims.get(BOOLEAN_CLAIM)).isEqualTo(Boolean.TRUE); + assertThat(claims.get(INSTANT_CLAIM)).isEqualTo(instant); + assertThat(claims.get(URL_CLAIM)).isEqualTo(url); + assertThat(claims.get(COLLECTION_STRING_CLAIM)).isEqualTo(listString); + assertThat(claims.get(LIST_STRING_CLAIM)).isEqualTo(listString); + assertThat(claims.get(MAP_STRING_OBJECT_CLAIM)).isEqualTo(mapStringObject); + assertThat(claims.get(JSON_ARRAY_CLAIM)).isEqualTo(jsonArrayListString); + assertThat(claims.get(JSON_OBJECT_CLAIM)).isEqualTo(jsonObjectMap); } @Test diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/DefaultMapOAuth2AccessTokenResponseConverterTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/DefaultMapOAuth2AccessTokenResponseConverterTests.java index 0b7433ffc0..7b07619d58 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/DefaultMapOAuth2AccessTokenResponseConverterTests.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/DefaultMapOAuth2AccessTokenResponseConverterTests.java @@ -22,6 +22,7 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -29,8 +30,6 @@ import org.springframework.core.convert.converter.Converter; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.security.oauth2.core.OAuth2RefreshToken; -import static org.assertj.core.api.Assertions.assertThat; - /** * Tests for {@link DefaultMapOAuth2AccessTokenResponseConverter}. * @@ -57,24 +56,24 @@ public class DefaultMapOAuth2AccessTokenResponseConverterTests { map.put("custom_parameter_2", "custom-value-2"); OAuth2AccessTokenResponse converted = this.messageConverter.convert(map); OAuth2AccessToken accessToken = converted.getAccessToken(); - assertThat(accessToken).isNotNull(); - assertThat(accessToken.getTokenValue()).isEqualTo("access-token-1234"); - assertThat(accessToken.getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER); + Assertions.assertNotNull(accessToken); + Assertions.assertEquals("access-token-1234", accessToken.getTokenValue()); + Assertions.assertEquals(OAuth2AccessToken.TokenType.BEARER, accessToken.getTokenType()); Set scopes = accessToken.getScopes(); - assertThat(scopes).isNotNull(); - assertThat(scopes).hasSize(2); - assertThat(scopes).contains("read"); - assertThat(scopes).contains("write"); - assertThat(Duration.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()).getSeconds()) - .isEqualTo(3600); + Assertions.assertNotNull(scopes); + Assertions.assertEquals(2, scopes.size()); + Assertions.assertTrue(scopes.contains("read")); + Assertions.assertTrue(scopes.contains("write")); + Assertions.assertEquals(3600, + Duration.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()).getSeconds()); OAuth2RefreshToken refreshToken = converted.getRefreshToken(); - assertThat(refreshToken).isNotNull(); - assertThat(refreshToken.getTokenValue()).isEqualTo("refresh-token-1234"); + Assertions.assertNotNull(refreshToken); + Assertions.assertEquals("refresh-token-1234", refreshToken.getTokenValue()); Map additionalParameters = converted.getAdditionalParameters(); - assertThat(additionalParameters).isNotNull(); - assertThat(additionalParameters).hasSize(2); - assertThat(additionalParameters).containsEntry("custom_parameter_1", "custom-value-1"); - assertThat(additionalParameters).containsEntry("custom_parameter_2", "custom-value-2"); + Assertions.assertNotNull(additionalParameters); + Assertions.assertEquals(2, additionalParameters.size()); + Assertions.assertEquals("custom-value-1", additionalParameters.get("custom_parameter_1")); + Assertions.assertEquals("custom-value-2", additionalParameters.get("custom_parameter_2")); } @Test @@ -84,18 +83,19 @@ public class DefaultMapOAuth2AccessTokenResponseConverterTests { map.put("token_type", "bearer"); OAuth2AccessTokenResponse converted = this.messageConverter.convert(map); OAuth2AccessToken accessToken = converted.getAccessToken(); - assertThat(accessToken).isNotNull(); - assertThat(accessToken.getTokenValue()).isEqualTo("access-token-1234"); - assertThat(accessToken.getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER); + Assertions.assertNotNull(accessToken); + Assertions.assertEquals("access-token-1234", accessToken.getTokenValue()); + Assertions.assertEquals(OAuth2AccessToken.TokenType.BEARER, accessToken.getTokenType()); Set scopes = accessToken.getScopes(); - assertThat(scopes).isNotNull(); - assertThat(scopes).isEmpty(); - assertThat(Duration.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()).getSeconds()).isEqualTo(1); + Assertions.assertNotNull(scopes); + Assertions.assertEquals(0, scopes.size()); + Assertions.assertEquals(1, + Duration.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()).getSeconds()); OAuth2RefreshToken refreshToken = converted.getRefreshToken(); - assertThat(refreshToken).isNull(); + Assertions.assertNull(refreshToken); Map additionalParameters = converted.getAdditionalParameters(); - assertThat(additionalParameters).isNotNull(); - assertThat(additionalParameters).isEmpty(); + Assertions.assertNotNull(additionalParameters); + Assertions.assertEquals(0, additionalParameters.size()); } @Test @@ -106,18 +106,19 @@ public class DefaultMapOAuth2AccessTokenResponseConverterTests { map.put("expires_in", "2100-01-01-abc"); OAuth2AccessTokenResponse converted = this.messageConverter.convert(map); OAuth2AccessToken accessToken = converted.getAccessToken(); - assertThat(accessToken).isNotNull(); - assertThat(accessToken.getTokenValue()).isEqualTo("access-token-1234"); - assertThat(accessToken.getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER); + Assertions.assertNotNull(accessToken); + Assertions.assertEquals("access-token-1234", accessToken.getTokenValue()); + Assertions.assertEquals(OAuth2AccessToken.TokenType.BEARER, accessToken.getTokenType()); Set scopes = accessToken.getScopes(); - assertThat(scopes).isNotNull(); - assertThat(scopes).isEmpty(); - assertThat(Duration.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()).getSeconds()).isEqualTo(1); + Assertions.assertNotNull(scopes); + Assertions.assertEquals(0, scopes.size()); + Assertions.assertEquals(1, + Duration.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()).getSeconds()); OAuth2RefreshToken refreshToken = converted.getRefreshToken(); - assertThat(refreshToken).isNull(); + Assertions.assertNull(refreshToken); Map additionalParameters = converted.getAdditionalParameters(); - assertThat(additionalParameters).isNotNull(); - assertThat(additionalParameters).isEmpty(); + Assertions.assertNotNull(additionalParameters); + Assertions.assertEquals(0, additionalParameters.size()); } // gh-9685 @@ -129,11 +130,11 @@ public class DefaultMapOAuth2AccessTokenResponseConverterTests { map.put("expires_in", 3600); OAuth2AccessTokenResponse converted = this.messageConverter.convert(map); OAuth2AccessToken accessToken = converted.getAccessToken(); - assertThat(accessToken).isNotNull(); - assertThat(accessToken.getTokenValue()).isEqualTo("access-token-1234"); - assertThat(accessToken.getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER); - assertThat(Duration.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()).getSeconds()) - .isEqualTo(3600); + Assertions.assertNotNull(accessToken); + Assertions.assertEquals("access-token-1234", accessToken.getTokenValue()); + Assertions.assertEquals(OAuth2AccessToken.TokenType.BEARER, accessToken.getTokenType()); + Assertions.assertEquals(3600, + Duration.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()).getSeconds()); } // gh-9685 @@ -152,24 +153,24 @@ public class DefaultMapOAuth2AccessTokenResponseConverterTests { map.put("custom_parameter_2", "custom-value-2"); OAuth2AccessTokenResponse converted = this.messageConverter.convert(map); OAuth2AccessToken accessToken = converted.getAccessToken(); - assertThat(accessToken).isNotNull(); - assertThat(accessToken.getTokenValue()).isEqualTo("access-token-1234"); - assertThat(accessToken.getTokenType()).isEqualTo(OAuth2AccessToken.TokenType.BEARER); + Assertions.assertNotNull(accessToken); + Assertions.assertEquals("access-token-1234", accessToken.getTokenValue()); + Assertions.assertEquals(OAuth2AccessToken.TokenType.BEARER, accessToken.getTokenType()); Set scopes = accessToken.getScopes(); - assertThat(scopes).isNotNull(); - assertThat(scopes).hasSize(2); - assertThat(scopes).contains("read"); - assertThat(scopes).contains("write"); - assertThat(Duration.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()).getSeconds()) - .isEqualTo(3600); + Assertions.assertNotNull(scopes); + Assertions.assertEquals(2, scopes.size()); + Assertions.assertTrue(scopes.contains("read")); + Assertions.assertTrue(scopes.contains("write")); + Assertions.assertEquals(3600, + Duration.between(accessToken.getIssuedAt(), accessToken.getExpiresAt()).getSeconds()); OAuth2RefreshToken refreshToken = converted.getRefreshToken(); - assertThat(refreshToken).isNotNull(); - assertThat(refreshToken.getTokenValue()).isEqualTo("refresh-token-1234"); + Assertions.assertNotNull(refreshToken); + Assertions.assertEquals("refresh-token-1234", refreshToken.getTokenValue()); Map additionalParameters = converted.getAdditionalParameters(); - assertThat(additionalParameters).isNotNull(); - assertThat(additionalParameters).hasSize(2); - assertThat(additionalParameters).containsEntry("custom_parameter_1", nestedObject); - assertThat(additionalParameters).containsEntry("custom_parameter_2", "custom-value-2"); + Assertions.assertNotNull(additionalParameters); + Assertions.assertEquals(2, additionalParameters.size()); + Assertions.assertEquals(nestedObject, additionalParameters.get("custom_parameter_1")); + Assertions.assertEquals("custom-value-2", additionalParameters.get("custom_parameter_2")); } } diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/DefaultOAuth2AccessTokenResponseMapConverterTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/DefaultOAuth2AccessTokenResponseMapConverterTests.java index 69a9ab3d95..0d58ddaea4 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/DefaultOAuth2AccessTokenResponseMapConverterTests.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/endpoint/DefaultOAuth2AccessTokenResponseMapConverterTests.java @@ -22,14 +22,13 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.core.convert.converter.Converter; import org.springframework.security.oauth2.core.OAuth2AccessToken; -import static org.assertj.core.api.Assertions.assertThat; - /** * Tests for {@link DefaultOAuth2AccessTokenResponseMapConverter}. * @@ -62,14 +61,14 @@ public class DefaultOAuth2AccessTokenResponseMapConverterTests { .build(); // @formatter:on Map result = this.messageConverter.convert(build); - assertThat(result).hasSize(7); - assertThat(result).containsEntry("access_token", "access-token-value-1234"); - assertThat(result).containsEntry("refresh_token", "refresh-token-value-1234"); - assertThat(result).containsEntry("scope", "read write"); - assertThat(result).containsEntry("token_type", "Bearer"); - assertThat(result.get("expires_in")).isNotNull(); - assertThat(result).containsEntry("custom_parameter_1", "custom-value-1"); - assertThat(result).containsEntry("custom_parameter_2", "custom-value-2"); + Assertions.assertEquals(7, result.size()); + Assertions.assertEquals("access-token-value-1234", result.get("access_token")); + Assertions.assertEquals("refresh-token-value-1234", result.get("refresh_token")); + Assertions.assertEquals("read write", result.get("scope")); + Assertions.assertEquals("Bearer", result.get("token_type")); + Assertions.assertNotNull(result.get("expires_in")); + Assertions.assertEquals("custom-value-1", result.get("custom_parameter_1")); + Assertions.assertEquals("custom-value-2", result.get("custom_parameter_2")); } @Test @@ -80,10 +79,10 @@ public class DefaultOAuth2AccessTokenResponseMapConverterTests { .build(); // @formatter:on Map result = this.messageConverter.convert(build); - assertThat(result).hasSize(3); - assertThat(result).containsEntry("access_token", "access-token-value-1234"); - assertThat(result).containsEntry("token_type", "Bearer"); - assertThat(result.get("expires_in")).isNotNull(); + Assertions.assertEquals(3, result.size()); + Assertions.assertEquals("access-token-value-1234", result.get("access_token")); + Assertions.assertEquals("Bearer", result.get("token_type")); + Assertions.assertNotNull(result.get("expires_in")); } // gh-9685 @@ -108,14 +107,14 @@ public class DefaultOAuth2AccessTokenResponseMapConverterTests { .build(); // @formatter:on Map result = this.messageConverter.convert(build); - assertThat(result).hasSize(7); - assertThat(result).containsEntry("access_token", "access-token-value-1234"); - assertThat(result).containsEntry("refresh_token", "refresh-token-value-1234"); - assertThat(result).containsEntry("scope", "read write"); - assertThat(result).containsEntry("token_type", "Bearer"); - assertThat(result.get("expires_in")).isNotNull(); - assertThat(result).containsEntry("custom_parameter_1", nestedObject); - assertThat(result).containsEntry("custom_parameter_2", "custom-value-2"); + Assertions.assertEquals(7, result.size()); + Assertions.assertEquals("access-token-value-1234", result.get("access_token")); + Assertions.assertEquals("refresh-token-value-1234", result.get("refresh_token")); + Assertions.assertEquals("read write", result.get("scope")); + Assertions.assertEquals("Bearer", result.get("token_type")); + Assertions.assertNotNull(result.get("expires_in")); + Assertions.assertEquals(nestedObject, result.get("custom_parameter_1")); + Assertions.assertEquals("custom-value-2", result.get("custom_parameter_2")); } } diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/OidcIdTokenBuilderTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/OidcIdTokenBuilderTests.java index d76edc05a4..50c02c7098 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/OidcIdTokenBuilderTests.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/OidcIdTokenBuilderTests.java @@ -41,11 +41,11 @@ public class OidcIdTokenBuilderTests { .build(); // @formatter:on assertThat(first.getClaims()).hasSize(1); - assertThat(first.getClaims()).containsEntry("TEST_CLAIM_1", "C1"); + assertThat(first.getClaims().get("TEST_CLAIM_1")).isEqualTo("C1"); assertThat(first.getTokenValue()).isEqualTo("V1"); assertThat(second.getClaims()).hasSize(2); - assertThat(second.getClaims()).containsEntry("TEST_CLAIM_1", "C2"); - assertThat(second.getClaims()).containsEntry("TEST_CLAIM_2", "C3"); + assertThat(second.getClaims().get("TEST_CLAIM_1")).isEqualTo("C2"); + assertThat(second.getClaims().get("TEST_CLAIM_2")).isEqualTo("C3"); assertThat(second.getTokenValue()).isEqualTo("V2"); } diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/OidcUserInfoBuilderTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/OidcUserInfoBuilderTests.java index c41c9e03a5..50d7f56b1c 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/OidcUserInfoBuilderTests.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/OidcUserInfoBuilderTests.java @@ -38,10 +38,10 @@ public class OidcUserInfoBuilderTests { .build(); // @formatter:on assertThat(first.getClaims()).hasSize(1); - assertThat(first.getClaims()).containsEntry("TEST_CLAIM_1", "C1"); + assertThat(first.getClaims().get("TEST_CLAIM_1")).isEqualTo("C1"); assertThat(second.getClaims()).hasSize(2); - assertThat(second.getClaims()).containsEntry("TEST_CLAIM_1", "C2"); - assertThat(second.getClaims()).containsEntry("TEST_CLAIM_2", "C3"); + assertThat(second.getClaims().get("TEST_CLAIM_1")).isEqualTo("C2"); + assertThat(second.getClaims().get("TEST_CLAIM_2")).isEqualTo("C3"); } @Test diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/TestOidcIdTokens.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/TestOidcIdTokens.java index 2271a52e00..ca859473d1 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/TestOidcIdTokens.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/TestOidcIdTokens.java @@ -17,7 +17,6 @@ package org.springframework.security.oauth2.core.oidc; import java.time.Instant; -import java.util.List; /** * Test {@link OidcIdToken}s @@ -33,7 +32,6 @@ public final class TestOidcIdTokens { // @formatter:off return OidcIdToken.withTokenValue("id-token") .issuer("https://example.com") - .audience(List.of("client-id")) .subject("subject") .issuedAt(Instant.now()) .expiresAt(Instant.now() diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/user/TestOidcUsers.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/user/TestOidcUsers.java index ca2c37abf7..3bda7ec32d 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/user/TestOidcUsers.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/user/TestOidcUsers.java @@ -50,7 +50,7 @@ public final class TestOidcUsers { .expiresAt(expiresAt) .subject("subject") .issuer("http://localhost/issuer") - .audience(Collections.unmodifiableSet(new LinkedHashSet<>(Collections.singletonList("client-id")))) + .audience(Collections.unmodifiableSet(new LinkedHashSet<>(Collections.singletonList("client")))) .authorizedParty("client") .build(); // @formatter:on diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JoseHeader.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JoseHeader.java index 3b749fe395..f40fb09a55 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JoseHeader.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/JoseHeader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 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. @@ -353,7 +353,7 @@ class JoseHeader { private static URL convertAsURL(String header, String value) { URL convertedValue = ClaimConversionService.getSharedInstance().convert(value, URL.class); - Assert.notNull(convertedValue, + Assert.isTrue(convertedValue != null, () -> "Unable to convert header '" + header + "' of type '" + value.getClass() + "' to URL."); return convertedValue; } diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java index 01fa5b43e5..5690c9d7fb 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java @@ -194,7 +194,7 @@ public final class NimbusJwtDecoder implements JwtDecoder { private String getJwtValidationExceptionMessage(Collection errors) { for (OAuth2Error oAuth2Error : errors) { - if (StringUtils.hasLength(oAuth2Error.getDescription())) { + if (!StringUtils.isEmpty(oAuth2Error.getDescription())) { return String.format(DECODING_ERROR_MESSAGE_TEMPLATE, oAuth2Error.getDescription()); } } @@ -473,7 +473,7 @@ public final class NimbusJwtDecoder implements JwtDecoder { HttpHeaders headers = new HttpHeaders(); headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON, APPLICATION_JWK_SET_JSON)); ResponseEntity response = getResponse(url, headers); - if (response.getStatusCode().value() != 200) { + if (response.getStatusCodeValue() != 200) { throw new IOException(response.toString()); } return new Resource(response.getBody(), "UTF-8"); diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoder.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoder.java index 2da97ab96b..4be03e5e44 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoder.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoder.java @@ -205,7 +205,7 @@ public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder { private String getJwtValidationExceptionMessage(Collection errors) { for (OAuth2Error oAuth2Error : errors) { - if (StringUtils.hasLength(oAuth2Error.getDescription())) { + if (!StringUtils.isEmpty(oAuth2Error.getDescription())) { return oAuth2Error.getDescription(); } } diff --git a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtils.java b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtils.java index ad82339c2d..b4c712012f 100644 --- a/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtils.java +++ b/oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/ReactiveJwtDecoderProviderConfigurationUtils.java @@ -89,7 +89,7 @@ final class ReactiveJwtDecoderProviderConfigurationUtils { } Assert.notEmpty(jwsAlgorithms, "Failed to find any algorithms from the JWK set"); return jwsAlgorithms; - }).onErrorMap(KeySourceException.class, IllegalStateException::new); + }).onErrorMap(KeySourceException.class, (ex) -> new IllegalStateException(ex)); } static Mono> getConfigurationForIssuerLocation(String issuer, WebClient web) { diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtBuilderTests.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtBuilderTests.java index 2d99766254..09d13a6e7a 100644 --- a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtBuilderTests.java +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtBuilderTests.java @@ -47,16 +47,16 @@ public class JwtBuilderTests { .build(); // @formatter:on assertThat(first.getHeaders()).hasSize(1); - assertThat(first.getHeaders()).containsEntry("TEST_HEADER_1", "H1"); + assertThat(first.getHeaders().get("TEST_HEADER_1")).isEqualTo("H1"); assertThat(first.getClaims()).hasSize(1); - assertThat(first.getClaims()).containsEntry("TEST_CLAIM_1", "C1"); + assertThat(first.getClaims().get("TEST_CLAIM_1")).isEqualTo("C1"); assertThat(first.getTokenValue()).isEqualTo("V1"); assertThat(second.getHeaders()).hasSize(2); - assertThat(second.getHeaders()).containsEntry("TEST_HEADER_1", "H2"); - assertThat(second.getHeaders()).containsEntry("TEST_HEADER_2", "H3"); + assertThat(second.getHeaders().get("TEST_HEADER_1")).isEqualTo("H2"); + assertThat(second.getHeaders().get("TEST_HEADER_2")).isEqualTo("H3"); assertThat(second.getClaims()).hasSize(2); - assertThat(second.getClaims()).containsEntry("TEST_CLAIM_1", "C2"); - assertThat(second.getClaims()).containsEntry("TEST_CLAIM_2", "C3"); + assertThat(second.getClaims().get("TEST_CLAIM_1")).isEqualTo("C2"); + assertThat(second.getClaims().get("TEST_CLAIM_2")).isEqualTo("C3"); assertThat(second.getTokenValue()).isEqualTo("V2"); } diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtClaimValidatorTests.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtClaimValidatorTests.java index 9da90cfd0d..a43989c868 100644 --- a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtClaimValidatorTests.java +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/JwtClaimValidatorTests.java @@ -50,7 +50,7 @@ public class JwtClaimValidatorTests { public void validateWhenClaimFailsTheTestThenReturnsFailure() { Jwt jwt = TestJwts.jwt().claim(JwtClaimNames.ISS, "http://abc").build(); Collection details = this.validator.validate(jwt).getErrors(); - assertThat(this.validator.validate(jwt).getErrors()).isNotEmpty(); + assertThat(this.validator.validate(jwt).getErrors().isEmpty()).isFalse(); assertThat(details).allMatch((error) -> Objects.equals(error.getErrorCode(), OAuth2ErrorCodes.INVALID_TOKEN)); } diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverterTests.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverterTests.java index f07bbd9df1..9c365411b8 100644 --- a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverterTests.java +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/MappedJwtClaimSetConverterTests.java @@ -53,7 +53,7 @@ public class MappedJwtClaimSetConverterTests { .withDefaults(Collections.singletonMap(JwtClaimNames.EXP, expiresAtConverter)); Map source = new HashMap<>(); Map target = converter.convert(source); - assertThat(target).containsEntry(JwtClaimNames.IAT, Instant.ofEpochMilli(at.toEpochMilli()).minusSeconds(1)); + assertThat(target.get(JwtClaimNames.IAT)).isEqualTo(Instant.ofEpochMilli(at.toEpochMilli()).minusSeconds(1)); } @Test @@ -61,8 +61,8 @@ public class MappedJwtClaimSetConverterTests { MappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap()); Map source = Collections.singletonMap(JwtClaimNames.EXP, 1000000000L); Map target = converter.convert(source); - assertThat(target).containsEntry(JwtClaimNames.EXP, Instant.ofEpochSecond(1000000000L)); - assertThat(target).containsEntry(JwtClaimNames.IAT, Instant.ofEpochSecond(1000000000L).minusSeconds(1)); + assertThat(target.get(JwtClaimNames.EXP)).isEqualTo(Instant.ofEpochSecond(1000000000L)); + assertThat(target.get(JwtClaimNames.IAT)).isEqualTo(Instant.ofEpochSecond(1000000000L).minusSeconds(1)); } @Test @@ -71,11 +71,11 @@ public class MappedJwtClaimSetConverterTests { Map source = Collections.singletonMap(JwtClaimNames.AUD, "audience"); Map target = converter.convert(source); assertThat(target.get(JwtClaimNames.AUD)).isInstanceOf(Collection.class); - assertThat(target).containsEntry(JwtClaimNames.AUD, Arrays.asList("audience")); + assertThat(target.get(JwtClaimNames.AUD)).isEqualTo(Arrays.asList("audience")); source = Collections.singletonMap(JwtClaimNames.AUD, Arrays.asList("one", "two")); target = converter.convert(source); assertThat(target.get(JwtClaimNames.AUD)).isInstanceOf(Collection.class); - assertThat(target).containsEntry(JwtClaimNames.AUD, Arrays.asList("one", "two")); + assertThat(target.get(JwtClaimNames.AUD)).isEqualTo(Arrays.asList("one", "two")); } @Test @@ -90,13 +90,13 @@ public class MappedJwtClaimSetConverterTests { source.put(JwtClaimNames.NBF, 1000000000); source.put(JwtClaimNames.SUB, 1234); Map target = converter.convert(source); - assertThat(target).containsEntry(JwtClaimNames.JTI, "1"); - assertThat(target).containsEntry(JwtClaimNames.AUD, Arrays.asList("audience")); - assertThat(target).containsEntry(JwtClaimNames.EXP, Instant.ofEpochSecond(2000000000L)); - assertThat(target).containsEntry(JwtClaimNames.IAT, Instant.ofEpochSecond(1000000000L)); - assertThat(target).containsEntry(JwtClaimNames.ISS, "https://any.url"); - assertThat(target).containsEntry(JwtClaimNames.NBF, Instant.ofEpochSecond(1000000000L)); - assertThat(target).containsEntry(JwtClaimNames.SUB, "1234"); + assertThat(target.get(JwtClaimNames.JTI)).isEqualTo("1"); + assertThat(target.get(JwtClaimNames.AUD)).isEqualTo(Arrays.asList("audience")); + assertThat(target.get(JwtClaimNames.EXP)).isEqualTo(Instant.ofEpochSecond(2000000000L)); + assertThat(target.get(JwtClaimNames.IAT)).isEqualTo(Instant.ofEpochSecond(1000000000L)); + assertThat(target.get(JwtClaimNames.ISS)).isEqualTo("https://any.url"); + assertThat(target.get(JwtClaimNames.NBF)).isEqualTo(Instant.ofEpochSecond(1000000000L)); + assertThat(target.get(JwtClaimNames.SUB)).isEqualTo("1234"); } @Test @@ -114,13 +114,13 @@ public class MappedJwtClaimSetConverterTests { source.put(JwtClaimNames.NBF, "1000000000"); source.put(JwtClaimNames.SUB, 2345); Map target = converter.convert(source); - assertThat(target).containsEntry(JwtClaimNames.JTI, "1"); - assertThat(target).containsEntry(JwtClaimNames.AUD, Arrays.asList("audience")); - assertThat(target).containsEntry(JwtClaimNames.EXP, Instant.ofEpochSecond(2000000000L)); - assertThat(target).containsEntry(JwtClaimNames.IAT, Instant.ofEpochSecond(1000000000L)); - assertThat(target).containsEntry(JwtClaimNames.ISS, "https://any.url"); - assertThat(target).containsEntry(JwtClaimNames.NBF, Instant.ofEpochSecond(1000000000L)); - assertThat(target).containsEntry(JwtClaimNames.SUB, "1234"); + assertThat(target.get(JwtClaimNames.JTI)).isEqualTo("1"); + assertThat(target.get(JwtClaimNames.AUD)).isEqualTo(Arrays.asList("audience")); + assertThat(target.get(JwtClaimNames.EXP)).isEqualTo(Instant.ofEpochSecond(2000000000L)); + assertThat(target.get(JwtClaimNames.IAT)).isEqualTo(Instant.ofEpochSecond(1000000000L)); + assertThat(target.get(JwtClaimNames.ISS)).isEqualTo("https://any.url"); + assertThat(target.get(JwtClaimNames.NBF)).isEqualTo(Instant.ofEpochSecond(1000000000L)); + assertThat(target.get(JwtClaimNames.SUB)).isEqualTo("1234"); } // gh-10135 @@ -149,7 +149,7 @@ public class MappedJwtClaimSetConverterTests { given(claimConverter.convert(any())).willReturn("custom-value"); Map source = new HashMap<>(); Map target = converter.convert(source); - assertThat(target).containsEntry("custom-claim", "custom-value"); + assertThat(target.get("custom-claim")).isEqualTo("custom-value"); } @Test @@ -167,13 +167,13 @@ public class MappedJwtClaimSetConverterTests { source.put(JwtClaimNames.NBF, new Object()); source.put(JwtClaimNames.SUB, new Object()); Map target = converter.convert(source); - assertThat(target).containsEntry(JwtClaimNames.JTI, source.get(JwtClaimNames.JTI)); - assertThat(target).containsEntry(JwtClaimNames.AUD, source.get(JwtClaimNames.AUD)); - assertThat(target).containsEntry(JwtClaimNames.EXP, source.get(JwtClaimNames.EXP)); - assertThat(target).containsEntry(JwtClaimNames.IAT, source.get(JwtClaimNames.IAT)); - assertThat(target).containsEntry(JwtClaimNames.ISS, source.get(JwtClaimNames.ISS)); - assertThat(target).containsEntry(JwtClaimNames.NBF, source.get(JwtClaimNames.NBF)); - assertThat(target).containsEntry(JwtClaimNames.SUB, "1234"); + assertThat(target.get(JwtClaimNames.JTI)).isEqualTo(source.get(JwtClaimNames.JTI)); + assertThat(target.get(JwtClaimNames.AUD)).isEqualTo(source.get(JwtClaimNames.AUD)); + assertThat(target.get(JwtClaimNames.EXP)).isEqualTo(source.get(JwtClaimNames.EXP)); + assertThat(target.get(JwtClaimNames.IAT)).isEqualTo(source.get(JwtClaimNames.IAT)); + assertThat(target.get(JwtClaimNames.ISS)).isEqualTo(source.get(JwtClaimNames.ISS)); + assertThat(target.get(JwtClaimNames.NBF)).isEqualTo(source.get(JwtClaimNames.NBF)); + assertThat(target.get(JwtClaimNames.SUB)).isEqualTo("1234"); } @Test @@ -195,7 +195,7 @@ public class MappedJwtClaimSetConverterTests { MappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap()); Map nonUriIssuer = Collections.singletonMap(JwtClaimNames.ISS, "issuer"); Map target = converter.convert(nonUriIssuer); - assertThat(target).containsEntry(JwtClaimNames.ISS, "issuer"); + assertThat(target.get(JwtClaimNames.ISS)).isEqualTo("issuer"); } // gh-6073 @@ -204,7 +204,7 @@ public class MappedJwtClaimSetConverterTests { MappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap()); Map issuer = Collections.singletonMap(JwtClaimNames.ISS, new URL("https://issuer")); Map target = converter.convert(issuer); - assertThat(target).containsEntry(JwtClaimNames.ISS, "https://issuer"); + assertThat(target.get(JwtClaimNames.ISS)).isEqualTo("https://issuer"); } @Test diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJweEncoderTests.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJweEncoderTests.java index 5aae9a8f22..2bc95d6495 100644 --- a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJweEncoderTests.java +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJweEncoderTests.java @@ -100,11 +100,11 @@ public class NimbusJweEncoderTests { // @formatter:on Jwt encodedJwe = this.jweEncoder.encode(JwtEncoderParameters.from(jwtClaimsSet)); - assertThat(encodedJwe.getHeaders()).containsEntry(JoseHeaderNames.ALG, DEFAULT_JWE_HEADER.getAlgorithm()); - assertThat(encodedJwe.getHeaders()).containsEntry("enc", DEFAULT_JWE_HEADER.getHeader("enc")); + assertThat(encodedJwe.getHeaders().get(JoseHeaderNames.ALG)).isEqualTo(DEFAULT_JWE_HEADER.getAlgorithm()); + assertThat(encodedJwe.getHeaders().get("enc")).isEqualTo(DEFAULT_JWE_HEADER.getHeader("enc")); assertThat(encodedJwe.getHeaders().get(JoseHeaderNames.JKU)).isNull(); assertThat(encodedJwe.getHeaders().get(JoseHeaderNames.JWK)).isNull(); - assertThat(encodedJwe.getHeaders()).containsEntry(JoseHeaderNames.KID, rsaJwk.getKeyID()); + assertThat(encodedJwe.getHeaders().get(JoseHeaderNames.KID)).isEqualTo(rsaJwk.getKeyID()); assertThat(encodedJwe.getHeaders().get(JoseHeaderNames.X5U)).isNull(); assertThat(encodedJwe.getHeaders().get(JoseHeaderNames.X5C)).isNull(); assertThat(encodedJwe.getHeaders().get(JoseHeaderNames.X5T)).isNull(); @@ -144,18 +144,18 @@ public class NimbusJweEncoderTests { // @formatter:on Jwt encodedJweNestedJws = this.jweEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet)); - assertThat(encodedJweNestedJws.getHeaders()).containsEntry(JoseHeaderNames.ALG, - DEFAULT_JWE_HEADER.getAlgorithm()); - assertThat(encodedJweNestedJws.getHeaders()).containsEntry("enc", DEFAULT_JWE_HEADER.getHeader("enc")); + assertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.ALG)) + .isEqualTo(DEFAULT_JWE_HEADER.getAlgorithm()); + assertThat(encodedJweNestedJws.getHeaders().get("enc")).isEqualTo(DEFAULT_JWE_HEADER.getHeader("enc")); assertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.JKU)).isNull(); assertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.JWK)).isNull(); - assertThat(encodedJweNestedJws.getHeaders()).containsEntry(JoseHeaderNames.KID, rsaJwk.getKeyID()); + assertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.KID)).isEqualTo(rsaJwk.getKeyID()); assertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.X5U)).isNull(); assertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.X5C)).isNull(); assertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.X5T)).isNull(); assertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.X5T_S256)).isNull(); assertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.TYP)).isNull(); - assertThat(encodedJweNestedJws.getHeaders()).containsEntry(JoseHeaderNames.CTY, "JWT"); + assertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.CTY)).isEqualTo("JWT"); assertThat(encodedJweNestedJws.getHeaders().get(JoseHeaderNames.CRIT)).isNull(); assertThat(encodedJweNestedJws.getIssuer()).isEqualTo(jwtClaimsSet.getIssuer()); diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderTests.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderTests.java index 4a709e09c1..ccae57a874 100644 --- a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderTests.java +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoderTests.java @@ -248,8 +248,8 @@ public class NimbusJwtDecoderTests { given(claimSetConverter.convert(any(Map.class))).willReturn(Collections.singletonMap("custom", "value")); this.jwtDecoder.setClaimSetConverter(claimSetConverter); Jwt jwt = this.jwtDecoder.decode(SIGNED_JWT); - assertThat(jwt.getClaims()).hasSize(1); - assertThat(jwt.getClaims()).containsEntry("custom", "value"); + assertThat(jwt.getClaims().size()).isEqualTo(1); + assertThat(jwt.getClaims().get("custom")).isEqualTo("value"); } // gh-7885 diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtEncoderTests.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtEncoderTests.java index e9825f0a35..526bc511a8 100644 --- a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtEncoderTests.java +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusJwtEncoderTests.java @@ -144,7 +144,7 @@ public class NimbusJwtEncoderTests { Jwt encodedJws = this.jwtEncoder.encode(JwtEncoderParameters.from(jwtClaimsSet)); - assertThat(encodedJws.getHeaders()).containsEntry(JoseHeaderNames.ALG, SignatureAlgorithm.RS256); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.ALG)).isEqualTo(SignatureAlgorithm.RS256); } @Test @@ -165,7 +165,7 @@ public class NimbusJwtEncoderTests { Jwt encodedJws = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet)); - assertThat(encodedJws.getHeaders()).containsEntry(JoseHeaderNames.KID, rsaJwk2.getKeyID()); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.KID)).isEqualTo(rsaJwk2.getKeyID()); } @Test @@ -190,8 +190,8 @@ public class NimbusJwtEncoderTests { Jwt encodedJws = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet)); - assertThat(encodedJws.getHeaders()).containsEntry(JoseHeaderNames.X5T_S256, - rsaJwk1.getX509CertSHA256Thumbprint().toString()); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.X5T_S256)) + .isEqualTo(rsaJwk1.getX509CertSHA256Thumbprint().toString()); assertThat(encodedJws.getHeaders().get(JoseHeaderNames.KID)).isNull(); } @@ -231,15 +231,15 @@ public class NimbusJwtEncoderTests { Jwt encodedJws = this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet)); - assertThat(encodedJws.getHeaders()).containsEntry(JoseHeaderNames.ALG, jwsHeader.getAlgorithm()); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.ALG)).isEqualTo(jwsHeader.getAlgorithm()); assertThat(encodedJws.getHeaders().get(JoseHeaderNames.JKU)).isNull(); assertThat(encodedJws.getHeaders().get(JoseHeaderNames.JWK)).isNull(); - assertThat(encodedJws.getHeaders()).containsEntry(JoseHeaderNames.KID, rsaJwk.getKeyID()); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.KID)).isEqualTo(rsaJwk.getKeyID()); assertThat(encodedJws.getHeaders().get(JoseHeaderNames.X5U)).isNull(); assertThat(encodedJws.getHeaders().get(JoseHeaderNames.X5C)).isNull(); assertThat(encodedJws.getHeaders().get(JoseHeaderNames.X5T)).isNull(); - assertThat(encodedJws.getHeaders()).containsEntry(JoseHeaderNames.X5T_S256, - rsaJwk.getX509CertSHA256Thumbprint().toString()); + assertThat(encodedJws.getHeaders().get(JoseHeaderNames.X5T_S256)) + .isEqualTo(rsaJwk.getX509CertSHA256Thumbprint().toString()); assertThat(encodedJws.getHeaders().get(JoseHeaderNames.TYP)).isNull(); assertThat(encodedJws.getHeaders().get(JoseHeaderNames.CTY)).isNull(); assertThat(encodedJws.getHeaders().get(JoseHeaderNames.CRIT)).isNull(); diff --git a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoderTests.java b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoderTests.java index d5f2ca9b2c..242937b93d 100644 --- a/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoderTests.java +++ b/oauth2/oauth2-jose/src/test/java/org/springframework/security/oauth2/jwt/NimbusReactiveJwtDecoderTests.java @@ -149,7 +149,7 @@ public class NimbusReactiveJwtDecoderTests { @Test public void decodeWhenMessageReadScopeThenSuccess() { Jwt jwt = this.decoder.decode(this.messageReadToken).block(); - assertThat(jwt.getClaims()).containsEntry("scope", "message:read"); + assertThat(jwt.getClaims().get("scope")).isEqualTo("message:read"); } @Test @@ -167,7 +167,7 @@ public class NimbusReactiveJwtDecoderTests { public void decodeWhenIssuedAtThenSuccess() { String withIssuedAt = "eyJraWQiOiJrZXktaWQtMSIsImFsZyI6IlJTMjU2In0.eyJzY29wZSI6IiIsImV4cCI6OTIyMzM3MjAwNjA5NjM3NSwiaWF0IjoxNTI5OTQyNDQ4fQ.LBzAJO-FR-uJDHST61oX4kimuQjz6QMJPW_mvEXRB6A-fMQWpfTQ089eboipAqsb33XnwWth9ELju9HMWLk0FjlWVVzwObh9FcoKelmPNR8mZIlFG-pAYGgSwi8HufyLabXHntFavBiFtqwp_z9clSOFK1RxWvt3lywEbGgtCKve0BXOjfKWiH1qe4QKGixH-NFxidvz8Qd5WbJwyb9tChC6ZKoKPv7Jp-N5KpxkY-O2iUtINvn4xOSactUsvKHgF8ZzZjvJGzG57r606OZXaNtoElQzjAPU5xDGg5liuEJzfBhvqiWCLRmSuZ33qwp3aoBnFgEw0B85gsNe3ggABg"; Jwt jwt = this.decoder.decode(withIssuedAt).block(); - assertThat(jwt.getClaims()).containsEntry(JwtClaimNames.IAT, Instant.ofEpochSecond(1529942448L)); + assertThat(jwt.getClaims().get(JwtClaimNames.IAT)).isEqualTo(Instant.ofEpochSecond(1529942448L)); } @Test @@ -268,8 +268,8 @@ public class NimbusReactiveJwtDecoderTests { this.decoder.setClaimSetConverter(claimSetConverter); given(claimSetConverter.convert(any(Map.class))).willReturn(Collections.singletonMap("custom", "value")); Jwt jwt = this.decoder.decode(this.messageReadToken).block(); - assertThat(jwt.getClaims()).hasSize(1); - assertThat(jwt.getClaims()).containsEntry("custom", "value"); + assertThat(jwt.getClaims().size()).isEqualTo(1); + assertThat(jwt.getClaims().get("custom")).isEqualTo("value"); verify(claimSetConverter).convert(any(Map.class)); } diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolver.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolver.java index 5d80e981bb..b1216e3681 100644 --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolver.java +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolver.java @@ -16,9 +16,10 @@ package org.springframework.security.oauth2.server.resource.authentication; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; @@ -46,10 +47,10 @@ import org.springframework.util.Assert; * "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer in * a signed JWT (JWS). * - * To use, this class must be able to determine whether the `iss` claim is trusted. Recall - * that anyone can stand up an authorization server and issue valid tokens to a resource - * server. The simplest way to achieve this is to supply a set of trusted issuers in the - * constructor. + * To use, this class must be able to determine whether or not the `iss` claim is trusted. + * Recall that anyone can stand up an authorization server and issue valid tokens to a + * resource server. The simplest way to achieve this is to supply a list of trusted + * issuers in the constructor. * * This class derives the Issuer from the `iss` claim found in the * {@link HttpServletRequest}'s @@ -66,58 +67,22 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat /** * Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided * parameters - * @param trustedIssuers an array of trusted issuers - * @deprecated use {@link #fromTrustedIssuers(String...)} + * @param trustedIssuers a list of trusted issuers */ - @Deprecated(since = "6.2", forRemoval = true) public JwtIssuerAuthenticationManagerResolver(String... trustedIssuers) { - this(Set.of(trustedIssuers)); + this(Arrays.asList(trustedIssuers)); } /** * Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided * parameters - * @param trustedIssuers a collection of trusted issuers - * @deprecated use {@link #fromTrustedIssuers(Collection)} + * @param trustedIssuers a list of trusted issuers */ - @Deprecated(since = "6.2", forRemoval = true) public JwtIssuerAuthenticationManagerResolver(Collection trustedIssuers) { Assert.notEmpty(trustedIssuers, "trustedIssuers cannot be empty"); this.authenticationManager = new ResolvingAuthenticationManager( - new TrustedIssuerJwtAuthenticationManagerResolver(Set.copyOf(trustedIssuers)::contains)); - } - - /** - * Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided - * parameters - * @param trustedIssuers an array of trusted issuers - * @since 6.2 - */ - public static JwtIssuerAuthenticationManagerResolver fromTrustedIssuers(String... trustedIssuers) { - return fromTrustedIssuers(Set.of(trustedIssuers)); - } - - /** - * Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided - * parameters - * @param trustedIssuers a collection of trusted issuers - * @since 6.2 - */ - public static JwtIssuerAuthenticationManagerResolver fromTrustedIssuers(Collection trustedIssuers) { - Assert.notEmpty(trustedIssuers, "trustedIssuers cannot be empty"); - return fromTrustedIssuers(Set.copyOf(trustedIssuers)::contains); - } - - /** - * Construct a {@link JwtIssuerAuthenticationManagerResolver} using the provided - * parameters - * @param trustedIssuers a predicate to validate issuers - * @since 6.2 - */ - public static JwtIssuerAuthenticationManagerResolver fromTrustedIssuers(Predicate trustedIssuers) { - Assert.notNull(trustedIssuers, "trustedIssuers cannot be null"); - return new JwtIssuerAuthenticationManagerResolver( - new TrustedIssuerJwtAuthenticationManagerResolver(trustedIssuers)); + new TrustedIssuerJwtAuthenticationManagerResolver( + Collections.unmodifiableCollection(trustedIssuers)::contains)); } /** @@ -125,8 +90,8 @@ public final class JwtIssuerAuthenticationManagerResolver implements Authenticat * parameters * * Note that the {@link AuthenticationManagerResolver} provided in this constructor - * will need to verify that the issuer is trusted. This should be done via an allowed - * set of issuers. + * will need to verify that the issuer is trusted. This should be done via an + * allowlist. * * One way to achieve this is with a {@link Map} where the keys are the known issuers: *
    diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolver.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolver.java
    index 2e81d3b3d8..f31949d937 100644
    --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolver.java
    +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolver.java
    @@ -17,20 +17,18 @@
     package org.springframework.security.oauth2.server.resource.authentication;
     
     import java.time.Duration;
    +import java.util.ArrayList;
    +import java.util.Arrays;
     import java.util.Collection;
     import java.util.Map;
    -import java.util.Set;
     import java.util.concurrent.ConcurrentHashMap;
     import java.util.function.Predicate;
     
     import com.nimbusds.jwt.JWTParser;
    -import org.apache.commons.logging.Log;
    -import org.apache.commons.logging.LogFactory;
     import reactor.core.publisher.Mono;
     import reactor.core.scheduler.Schedulers;
     
     import org.springframework.core.convert.converter.Converter;
    -import org.springframework.core.log.LogMessage;
     import org.springframework.lang.NonNull;
     import org.springframework.security.authentication.AuthenticationManager;
     import org.springframework.security.authentication.ReactiveAuthenticationManager;
    @@ -48,10 +46,10 @@ import org.springframework.web.server.ServerWebExchange;
      * "https://openid.net/specs/openid-connect-core-1_0.html#IssuerIdentifier">Issuer in
      * a signed JWT (JWS).
      *
    - * To use, this class must be able to determine whether the `iss` claim is trusted. Recall
    - * that anyone can stand up an authorization server and issue valid tokens to a resource
    - * server. The simplest way to achieve this is to supply a set of trusted issuers in the
    - * constructor.
    + * To use, this class must be able to determine whether or not the `iss` claim is trusted.
    + * Recall that anyone can stand up an authorization server and issue valid tokens to a
    + * resource server. The simplest way to achieve this is to supply a list of trusted
    + * issuers in the constructor.
      *
      * This class derives the Issuer from the `iss` claim found in the
      * {@link ServerWebExchange}'s
    @@ -70,58 +68,21 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
     	/**
     	 * Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the
     	 * provided parameters
    -	 * @param trustedIssuers an array of trusted issuers
    -	 * @deprecated use {@link #fromTrustedIssuers(String...)}
    +	 * @param trustedIssuers a list of trusted issuers
     	 */
    -	@Deprecated(since = "6.2", forRemoval = true)
     	public JwtIssuerReactiveAuthenticationManagerResolver(String... trustedIssuers) {
    -		this(Set.of(trustedIssuers));
    +		this(Arrays.asList(trustedIssuers));
     	}
     
     	/**
     	 * Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the
     	 * provided parameters
     	 * @param trustedIssuers a collection of trusted issuers
    -	 * @deprecated use {@link #fromTrustedIssuers(Collection)}
     	 */
    -	@Deprecated(since = "6.2", forRemoval = true)
     	public JwtIssuerReactiveAuthenticationManagerResolver(Collection trustedIssuers) {
     		Assert.notEmpty(trustedIssuers, "trustedIssuers cannot be empty");
     		this.authenticationManager = new ResolvingAuthenticationManager(
    -				new TrustedIssuerJwtAuthenticationManagerResolver(Set.copyOf(trustedIssuers)::contains));
    -	}
    -
    -	/**
    -	 * Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the
    -	 * provided parameters
    -	 * @param trustedIssuers an array of trusted issuers
    -	 * @since 6.2
    -	 */
    -	public static JwtIssuerReactiveAuthenticationManagerResolver fromTrustedIssuers(String... trustedIssuers) {
    -		return fromTrustedIssuers(Set.of(trustedIssuers));
    -	}
    -
    -	/**
    -	 * Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the
    -	 * provided parameters
    -	 * @param trustedIssuers a collection of trusted issuers
    -	 * @since 6.2
    -	 */
    -	public static JwtIssuerReactiveAuthenticationManagerResolver fromTrustedIssuers(Collection trustedIssuers) {
    -		Assert.notEmpty(trustedIssuers, "trustedIssuers cannot be empty");
    -		return fromTrustedIssuers(Set.copyOf(trustedIssuers)::contains);
    -	}
    -
    -	/**
    -	 * Construct a {@link JwtIssuerReactiveAuthenticationManagerResolver} using the
    -	 * provided parameters
    -	 * @param trustedIssuers a predicate to validate issuers
    -	 * @since 6.2
    -	 */
    -	public static JwtIssuerReactiveAuthenticationManagerResolver fromTrustedIssuers(Predicate trustedIssuers) {
    -		Assert.notNull(trustedIssuers, "trustedIssuers cannot be null");
    -		return new JwtIssuerReactiveAuthenticationManagerResolver(
    -				new TrustedIssuerJwtAuthenticationManagerResolver(trustedIssuers));
    +				new TrustedIssuerJwtAuthenticationManagerResolver(new ArrayList<>(trustedIssuers)::contains));
     	}
     
     	/**
    @@ -130,7 +91,7 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
     	 *
     	 * Note that the {@link ReactiveAuthenticationManagerResolver} provided in this
     	 * constructor will need to verify that the issuer is trusted. This should be done via
    -	 * an allowed set of issuers.
    +	 * an allowed list of issuers.
     	 *
     	 * One way to achieve this is with a {@link Map} where the keys are the known issuers:
     	 * 
    @@ -208,8 +169,6 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
     	static class TrustedIssuerJwtAuthenticationManagerResolver
     			implements ReactiveAuthenticationManagerResolver {
     
    -		private final Log logger = LogFactory.getLog(getClass());
    -
     		private final Map> authenticationManagers = new ConcurrentHashMap<>();
     
     		private final Predicate trustedIssuer;
    @@ -221,13 +180,11 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
     		@Override
     		public Mono resolve(String issuer) {
     			if (!this.trustedIssuer.test(issuer)) {
    -				this.logger.debug("Did not resolve AuthenticationManager since issuer is not trusted");
     				return Mono.empty();
     			}
     			// @formatter:off
     			return this.authenticationManagers.computeIfAbsent(issuer,
     					(k) -> Mono.fromCallable(() -> new JwtReactiveAuthenticationManager(ReactiveJwtDecoders.fromIssuerLocation(k)))
    -							.doOnNext((manager) -> this.logger.debug(LogMessage.format("Resolved AuthenticationManager for issuer '%s'", issuer)))
     							.subscribeOn(Schedulers.boundedElastic())
     							.cache((manager) -> Duration.ofMillis(Long.MAX_VALUE), (ex) -> Duration.ZERO, () -> Duration.ZERO)
     			);
    diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.java
    index 96d862c102..eb1d63ff8f 100644
    --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.java
    +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/authentication/OpaqueTokenAuthenticationProvider.java
    @@ -101,9 +101,10 @@ public final class OpaqueTokenAuthenticationProvider implements AuthenticationPr
     	 */
     	@Override
     	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    -		if (!(authentication instanceof BearerTokenAuthenticationToken bearer)) {
    +		if (!(authentication instanceof BearerTokenAuthenticationToken)) {
     			return null;
     		}
    +		BearerTokenAuthenticationToken bearer = (BearerTokenAuthenticationToken) authentication;
     		OAuth2AuthenticatedPrincipal principal = getOAuth2AuthenticatedPrincipal(bearer);
     		Authentication result = this.authenticationConverter.convert(bearer.getToken(), principal);
     		if (result == null) {
    diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusOpaqueTokenIntrospector.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusOpaqueTokenIntrospector.java
    index c284668604..ffb70c483e 100644
    --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusOpaqueTokenIntrospector.java
    +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusOpaqueTokenIntrospector.java
    @@ -177,7 +177,7 @@ public class NimbusOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
     					+ contentType + "' is not compatible with JSON");
     		}
     
    -		HTTPResponse response = new HTTPResponse(responseEntity.getStatusCode().value());
    +		HTTPResponse response = new HTTPResponse(responseEntity.getStatusCodeValue());
     		response.setHeader(HttpHeaders.CONTENT_TYPE, contentType.toString());
     		response.setContent(responseEntity.getBody());
     
    diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospector.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospector.java
    index da183f28bc..4e3756db7b 100644
    --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospector.java
    +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospector.java
    @@ -133,7 +133,7 @@ public class NimbusReactiveOpaqueTokenIntrospector implements ReactiveOpaqueToke
     					+ contentType + "' is not compatible with JSON");
     		}
     
    -		HTTPResponse response = new HTTPResponse(responseEntity.statusCode().value());
    +		HTTPResponse response = new HTTPResponse(responseEntity.rawStatusCode());
     		response.setHeader(HttpHeaders.CONTENT_TYPE, contentType.toString());
     		if (response.getStatusCode() != HTTPResponse.SC_OK) {
     			this.logger.trace("Introspection endpoint returned non-OK status code");
    diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.java
    index 19361ecdd3..9db76d0de7 100644
    --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.java
    +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/BearerTokenAuthenticationEntryPoint.java
    @@ -74,7 +74,8 @@ public final class BearerTokenAuthenticationEntryPoint implements Authentication
     			if (StringUtils.hasText(error.getUri())) {
     				parameters.put("error_uri", error.getUri());
     			}
    -			if (error instanceof BearerTokenError bearerTokenError) {
    +			if (error instanceof BearerTokenError) {
    +				BearerTokenError bearerTokenError = (BearerTokenError) error;
     				if (StringUtils.hasText(bearerTokenError.getScope())) {
     					parameters.put("scope", bearerTokenError.getScope());
     				}
    diff --git a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/BearerTokenServerAuthenticationEntryPoint.java b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/BearerTokenServerAuthenticationEntryPoint.java
    index a2bc58a50d..7c7ee2aa44 100644
    --- a/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/BearerTokenServerAuthenticationEntryPoint.java
    +++ b/oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/server/BearerTokenServerAuthenticationEntryPoint.java
    @@ -82,7 +82,8 @@ public final class BearerTokenServerAuthenticationEntryPoint implements ServerAu
     			if (StringUtils.hasText(error.getUri())) {
     				parameters.put("error_uri", error.getUri());
     			}
    -			if (error instanceof BearerTokenError bearerTokenError) {
    +			if (error instanceof BearerTokenError) {
    +				BearerTokenError bearerTokenError = (BearerTokenError) error;
     				if (StringUtils.hasText(bearerTokenError.getScope())) {
     					parameters.put("scope", bearerTokenError.getScope());
     				}
    diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolverDeprecatedTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolverDeprecatedTests.java
    deleted file mode 100644
    index 9301ed104b..0000000000
    --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolverDeprecatedTests.java
    +++ /dev/null
    @@ -1,257 +0,0 @@
    -/*
    - * Copyright 2002-2020 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.oauth2.server.resource.authentication;
    -
    -import java.util.Collection;
    -import java.util.Collections;
    -import java.util.HashMap;
    -import java.util.Map;
    -
    -import com.nimbusds.jose.JWSAlgorithm;
    -import com.nimbusds.jose.JWSHeader;
    -import com.nimbusds.jose.JWSObject;
    -import com.nimbusds.jose.Payload;
    -import com.nimbusds.jose.crypto.RSASSASigner;
    -import com.nimbusds.jwt.JWTClaimsSet;
    -import com.nimbusds.jwt.PlainJWT;
    -import net.minidev.json.JSONObject;
    -import okhttp3.mockwebserver.MockResponse;
    -import okhttp3.mockwebserver.MockWebServer;
    -import org.junit.jupiter.api.Test;
    -
    -import org.springframework.security.authentication.AuthenticationManager;
    -import org.springframework.security.authentication.AuthenticationManagerResolver;
    -import org.springframework.security.core.Authentication;
    -import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
    -import org.springframework.security.oauth2.jose.TestKeys;
    -import org.springframework.security.oauth2.jwt.JwtClaimNames;
    -import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerAuthenticationManagerResolver.TrustedIssuerJwtAuthenticationManagerResolver;
    -
    -import static org.assertj.core.api.Assertions.assertThat;
    -import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
    -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
    -import static org.mockito.BDDMockito.mock;
    -import static org.mockito.BDDMockito.verify;
    -
    -/**
    - * Tests for {@link JwtIssuerAuthenticationManagerResolver}
    - */
    -@Deprecated
    -public class JwtIssuerAuthenticationManagerResolverDeprecatedTests {
    -
    -	private static final String DEFAULT_RESPONSE_TEMPLATE = "{\n" + "    \"issuer\": \"%s\", \n"
    -			+ "    \"jwks_uri\": \"%s/.well-known/jwks.json\" \n" + "}";
    -
    -	private static final String JWK_SET = "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"n\":\"3FlqJr5TRskIQIgdE3Dd7D9lboWdcTUT8a-fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRvc5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4_1tfRgG6ii4Uhxh6iI8qNMJQX-fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2kJdJ_ZIV-WW4noDdzpKqHcwmB8FsrumlVY_DNVvUSDIipiq9PbP4H99TXN1o746oRaNa07rq1hoCgMSSy-85SagCoxlmyE-D-of9SsMY8Ol9t0rdzpobBuhyJ_o5dfvjKw\"}]}";
    -
    -	private String jwt = jwt("iss", "trusted");
    -
    -	private String evil = jwt("iss", "\"");
    -
    -	private String noIssuer = jwt("sub", "sub");
    -
    -	@Test
    -	public void resolveWhenUsingTrustedIssuerThenReturnsAuthenticationManager() throws Exception {
    -		try (MockWebServer server = new MockWebServer()) {
    -			server.start();
    -			String issuer = server.url("").toString();
    -			// @formatter:off
    -			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();
    -			Authentication authentication = authenticationManager.authenticate(token);
    -			assertThat(authentication.isAuthenticated()).isTrue();
    -		}
    -	}
    -
    -	@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)
    -			);
    -			// @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()) {
    -			String issuer = server.url("").toString();
    -			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));
    -			TrustedIssuerJwtAuthenticationManagerResolver resolver = new TrustedIssuerJwtAuthenticationManagerResolver(
    -					(iss) -> iss.equals(issuer));
    -			AuthenticationManager authenticationManager = resolver.resolve(issuer);
    -			AuthenticationManager cachedAuthenticationManager = resolver.resolve(issuer);
    -			assertThat(authenticationManager).isSameAs(cachedAuthenticationManager);
    -		}
    -	}
    -
    -	@Test
    -	public void resolveWhenUsingUntrustedIssuerThenException() {
    -		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
    -				"other", "issuers");
    -		Authentication token = withBearerToken(this.jwt);
    -		// @formatter:off
    -		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    -				.isThrownBy(() -> authenticationManagerResolver.resolve(null).authenticate(token))
    -				.withMessageContaining("Invalid issuer");
    -		// @formatter:on
    -	}
    -
    -	@Test
    -	public void resolveWhenUsingCustomIssuerAuthenticationManagerResolverThenUses() {
    -		AuthenticationManager authenticationManager = mock(AuthenticationManager.class);
    -		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
    -				(issuer) -> authenticationManager);
    -		Authentication token = withBearerToken(this.jwt);
    -		authenticationManagerResolver.resolve(null).authenticate(token);
    -		verify(authenticationManager).authenticate(token);
    -	}
    -
    -	@Test
    -	public void resolveWhenUsingExternalSourceThenRespondsToChanges() {
    -		Authentication token = withBearerToken(this.jwt);
    -		Map authenticationManagers = new HashMap<>();
    -		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
    -				authenticationManagers::get);
    -		// @formatter:off
    -		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    -				.isThrownBy(() -> authenticationManagerResolver.resolve(null).authenticate(token))
    -				.withMessageContaining("Invalid issuer");
    -		// @formatter:on
    -		AuthenticationManager authenticationManager = mock(AuthenticationManager.class);
    -		authenticationManagers.put("trusted", authenticationManager);
    -		authenticationManagerResolver.resolve(null).authenticate(token);
    -		verify(authenticationManager).authenticate(token);
    -		authenticationManagers.clear();
    -		// @formatter:off
    -		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    -				.isThrownBy(() -> authenticationManagerResolver.resolve(null).authenticate(token))
    -				.withMessageContaining("Invalid issuer");
    -		// @formatter:on
    -	}
    -
    -	@Test
    -	public void resolveWhenBearerTokenMalformedThenException() {
    -		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
    -				"trusted");
    -		Authentication token = withBearerToken("jwt");
    -		// @formatter:off
    -		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    -				.isThrownBy(() -> authenticationManagerResolver.resolve(null).authenticate(token))
    -				.withMessageNotContaining("Invalid issuer");
    -		// @formatter:on
    -	}
    -
    -	@Test
    -	public void resolveWhenBearerTokenNoIssuerThenException() {
    -		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
    -				"trusted");
    -		Authentication token = withBearerToken(this.noIssuer);
    -		// @formatter:off
    -		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    -				.isThrownBy(() -> authenticationManagerResolver.resolve(null).authenticate(token))
    -				.withMessageContaining("Missing issuer");
    -		// @formatter:on
    -	}
    -
    -	@Test
    -	public void resolveWhenBearerTokenEvilThenGenericException() {
    -		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
    -				"trusted");
    -		Authentication token = withBearerToken(this.evil);
    -		// @formatter:off
    -		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    -				.isThrownBy(() -> authenticationManagerResolver
    -						.resolve(null).authenticate(token)
    -				)
    -				.withMessage("Invalid issuer");
    -		// @formatter:on
    -	}
    -
    -	@Test
    -	public void constructorWhenNullOrEmptyIssuersThenException() {
    -		assertThatIllegalArgumentException()
    -			.isThrownBy(() -> new JwtIssuerAuthenticationManagerResolver((Collection) null));
    -		assertThatIllegalArgumentException()
    -			.isThrownBy(() -> new JwtIssuerAuthenticationManagerResolver(Collections.emptyList()));
    -	}
    -
    -	@Test
    -	public void constructorWhenNullAuthenticationManagerResolverThenException() {
    -		assertThatIllegalArgumentException()
    -			.isThrownBy(() -> new JwtIssuerAuthenticationManagerResolver((AuthenticationManagerResolver) null));
    -	}
    -
    -	private Authentication withBearerToken(String token) {
    -		return new BearerTokenAuthenticationToken(token);
    -	}
    -
    -	private String jwt(String claim, String value) {
    -		PlainJWT jwt = new PlainJWT(new JWTClaimsSet.Builder().claim(claim, value).build());
    -		return jwt.serialize();
    -	}
    -
    -}
    diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolverTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolverTests.java
    index 8a50dab153..fc9fed19b8 100644
    --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolverTests.java
    +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerAuthenticationManagerResolverTests.java
    @@ -20,7 +20,6 @@ import java.util.Collection;
     import java.util.Collections;
     import java.util.HashMap;
     import java.util.Map;
    -import java.util.function.Predicate;
     
     import com.nimbusds.jose.JWSAlgorithm;
     import com.nimbusds.jose.JWSHeader;
    @@ -65,7 +64,7 @@ public class JwtIssuerAuthenticationManagerResolverTests {
     	private String noIssuer = jwt("sub", "sub");
     
     	@Test
    -	public void resolveWhenUsingFromTrustedIssuersThenReturnsAuthenticationManager() throws Exception {
    +	public void resolveWhenUsingTrustedIssuerThenReturnsAuthenticationManager() throws Exception {
     		try (MockWebServer server = new MockWebServer()) {
     			server.start();
     			String issuer = server.url("").toString();
    @@ -73,7 +72,7 @@ public class JwtIssuerAuthenticationManagerResolverTests {
     			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)
    @@ -86,40 +85,8 @@ public class JwtIssuerAuthenticationManagerResolverTests {
     			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 = JwtIssuerAuthenticationManagerResolver
    -				.fromTrustedIssuers(issuer);
    -			Authentication token = withBearerToken(jws.serialize());
    -			AuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null);
    -			assertThat(authenticationManager).isNotNull();
    -			Authentication authentication = authenticationManager.authenticate(token);
    -			assertThat(authentication.isAuthenticated()).isTrue();
    -		}
    -	}
    -
    -	@Test
    -	public void resolveWhenUsingFromTrustedIssuersPredicateThenReturnsAuthenticationManager() throws Exception {
    -		try (MockWebServer server = new MockWebServer()) {
    -			server.start();
    -			String issuer = server.url("").toString();
    -			// @formatter:off
    -			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 = JwtIssuerAuthenticationManagerResolver
    -				.fromTrustedIssuers(issuer::equals);
    +			JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
    +					issuer);
     			Authentication token = withBearerToken(jws.serialize());
     			AuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null);
     			assertThat(authenticationManager).isNotNull();
    @@ -150,8 +117,8 @@ public class JwtIssuerAuthenticationManagerResolverTests {
     			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 = JwtIssuerAuthenticationManagerResolver
    -				.fromTrustedIssuers(issuer);
    +			JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
    +					issuer);
     			Authentication token = withBearerToken(jws.serialize());
     			AuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null);
     			assertThat(authenticationManager).isNotNull();
    @@ -182,8 +149,8 @@ public class JwtIssuerAuthenticationManagerResolverTests {
     
     	@Test
     	public void resolveWhenUsingUntrustedIssuerThenException() {
    -		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver
    -			.fromTrustedIssuers("other", "issuers");
    +		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
    +				"other", "issuers");
     		Authentication token = withBearerToken(this.jwt);
     		// @formatter:off
     		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    @@ -227,8 +194,8 @@ public class JwtIssuerAuthenticationManagerResolverTests {
     
     	@Test
     	public void resolveWhenBearerTokenMalformedThenException() {
    -		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver
    -			.fromTrustedIssuers("trusted");
    +		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
    +				"trusted");
     		Authentication token = withBearerToken("jwt");
     		// @formatter:off
     		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    @@ -239,8 +206,8 @@ public class JwtIssuerAuthenticationManagerResolverTests {
     
     	@Test
     	public void resolveWhenBearerTokenNoIssuerThenException() {
    -		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver
    -			.fromTrustedIssuers("trusted");
    +		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
    +				"trusted");
     		Authentication token = withBearerToken(this.noIssuer);
     		// @formatter:off
     		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    @@ -251,8 +218,8 @@ public class JwtIssuerAuthenticationManagerResolverTests {
     
     	@Test
     	public void resolveWhenBearerTokenEvilThenGenericException() {
    -		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerAuthenticationManagerResolver
    -			.fromTrustedIssuers("trusted");
    +		JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
    +				"trusted");
     		Authentication token = withBearerToken(this.evil);
     		// @formatter:off
     		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    @@ -264,13 +231,11 @@ public class JwtIssuerAuthenticationManagerResolverTests {
     	}
     
     	@Test
    -	public void factoryWhenNullOrEmptyIssuersThenException() {
    +	public void constructorWhenNullOrEmptyIssuersThenException() {
     		assertThatIllegalArgumentException()
    -			.isThrownBy(() -> JwtIssuerAuthenticationManagerResolver.fromTrustedIssuers((Predicate) null));
    +			.isThrownBy(() -> new JwtIssuerAuthenticationManagerResolver((Collection) null));
     		assertThatIllegalArgumentException()
    -			.isThrownBy(() -> JwtIssuerAuthenticationManagerResolver.fromTrustedIssuers((Collection) null));
    -		assertThatIllegalArgumentException()
    -			.isThrownBy(() -> JwtIssuerAuthenticationManagerResolver.fromTrustedIssuers(Collections.emptyList()));
    +			.isThrownBy(() -> new JwtIssuerAuthenticationManagerResolver(Collections.emptyList()));
     	}
     
     	@Test
    diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverDeprecatedTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverDeprecatedTests.java
    deleted file mode 100644
    index 9e0c08f1a6..0000000000
    --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverDeprecatedTests.java
    +++ /dev/null
    @@ -1,261 +0,0 @@
    -/*
    - * Copyright 2002-2020 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.oauth2.server.resource.authentication;
    -
    -import java.util.Collection;
    -import java.util.Collections;
    -import java.util.HashMap;
    -import java.util.Map;
    -
    -import com.nimbusds.jose.JWSAlgorithm;
    -import com.nimbusds.jose.JWSHeader;
    -import com.nimbusds.jose.JWSObject;
    -import com.nimbusds.jose.Payload;
    -import com.nimbusds.jose.crypto.RSASSASigner;
    -import com.nimbusds.jwt.JWTClaimsSet;
    -import com.nimbusds.jwt.PlainJWT;
    -import net.minidev.json.JSONObject;
    -import okhttp3.mockwebserver.MockResponse;
    -import okhttp3.mockwebserver.MockWebServer;
    -import org.junit.jupiter.api.Test;
    -import reactor.core.publisher.Mono;
    -
    -import org.springframework.security.authentication.ReactiveAuthenticationManager;
    -import org.springframework.security.authentication.ReactiveAuthenticationManagerResolver;
    -import org.springframework.security.core.Authentication;
    -import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
    -import org.springframework.security.oauth2.jose.TestKeys;
    -import org.springframework.security.oauth2.jwt.JwtClaimNames;
    -import org.springframework.security.oauth2.server.resource.authentication.JwtIssuerReactiveAuthenticationManagerResolver.TrustedIssuerJwtAuthenticationManagerResolver;
    -
    -import static org.assertj.core.api.Assertions.assertThat;
    -import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
    -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
    -import static org.mockito.BDDMockito.any;
    -import static org.mockito.BDDMockito.given;
    -import static org.mockito.BDDMockito.mock;
    -import static org.mockito.BDDMockito.verify;
    -
    -/**
    - * Tests for {@link JwtIssuerReactiveAuthenticationManagerResolver}
    - */
    -@Deprecated
    -public class JwtIssuerReactiveAuthenticationManagerResolverDeprecatedTests {
    -
    -	// @formatter:off
    -	private static final String DEFAULT_RESPONSE_TEMPLATE = "{\n"
    -			+ "    \"issuer\": \"%s\", \n"
    -			+ "    \"jwks_uri\": \"%s/.well-known/jwks.json\" \n"
    -			+ "}";
    -	// @formatter:on
    -
    -	private static final String JWK_SET = "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\",\"kid\":\"one\",\"n\":\"3FlqJr5TRskIQIgdE3Dd7D9lboWdcTUT8a-fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRvc5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4_1tfRgG6ii4Uhxh6iI8qNMJQX-fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2kJdJ_ZIV-WW4noDdzpKqHcwmB8FsrumlVY_DNVvUSDIipiq9PbP4H99TXN1o746oRaNa07rq1hoCgMSSy-85SagCoxlmyE-D-of9SsMY8Ol9t0rdzpobBuhyJ_o5dfvjKw\"}]}";
    -
    -	private String jwt = jwt("iss", "trusted");
    -
    -	private String evil = jwt("iss", "\"");
    -
    -	private String noIssuer = jwt("sub", "sub");
    -
    -	@Test
    -	public void resolveWhenUsingTrustedIssuerThenReturnsAuthenticationManager() throws Exception {
    -		try (MockWebServer server = new MockWebServer()) {
    -			String issuer = server.url("").toString();
    -			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));
    -			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();
    -			BearerTokenAuthenticationToken token = withBearerToken(jws.serialize());
    -			Authentication authentication = authenticationManager.authenticate(token).block();
    -			assertThat(authentication).isNotNull();
    -			assertThat(authentication.isAuthenticated()).isTrue();
    -		}
    -	}
    -
    -	// 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));
    -			// @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()) {
    -			String issuer = server.url("").toString();
    -			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));
    -			TrustedIssuerJwtAuthenticationManagerResolver resolver = new TrustedIssuerJwtAuthenticationManagerResolver(
    -					(iss) -> iss.equals(issuer));
    -			ReactiveAuthenticationManager authenticationManager = resolver.resolve(issuer).block();
    -			ReactiveAuthenticationManager cachedAuthenticationManager = resolver.resolve(issuer).block();
    -			assertThat(authenticationManager).isSameAs(cachedAuthenticationManager);
    -		}
    -	}
    -
    -	@Test
    -	public void resolveWhenUsingUntrustedIssuerThenException() {
    -		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
    -				"other", "issuers");
    -		Authentication token = withBearerToken(this.jwt);
    -		// @formatter:off
    -		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    -				.isThrownBy(() -> authenticationManagerResolver.resolve(null)
    -						.flatMap((authenticationManager) -> authenticationManager.authenticate(token))
    -						.block())
    -				.withMessageContaining("Invalid issuer");
    -		// @formatter:on
    -	}
    -
    -	@Test
    -	public void resolveWhenUsingCustomIssuerAuthenticationManagerResolverThenUses() {
    -		Authentication token = withBearerToken(this.jwt);
    -		ReactiveAuthenticationManager authenticationManager = mock(ReactiveAuthenticationManager.class);
    -		given(authenticationManager.authenticate(token)).willReturn(Mono.empty());
    -		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
    -				(issuer) -> Mono.just(authenticationManager));
    -		authenticationManagerResolver.resolve(null).flatMap((manager) -> manager.authenticate(token)).block();
    -		verify(authenticationManager).authenticate(any());
    -	}
    -
    -	@Test
    -	public void resolveWhenUsingExternalSourceThenRespondsToChanges() {
    -		Authentication token = withBearerToken(this.jwt);
    -		Map authenticationManagers = new HashMap<>();
    -		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
    -				(issuer) -> Mono.justOrEmpty(authenticationManagers.get(issuer)));
    -		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    -			.isThrownBy(() -> authenticationManagerResolver.resolve(null)
    -				.flatMap((manager) -> manager.authenticate(token))
    -				.block())
    -			.withMessageContaining("Invalid issuer");
    -		ReactiveAuthenticationManager authenticationManager = mock(ReactiveAuthenticationManager.class);
    -		given(authenticationManager.authenticate(token)).willReturn(Mono.empty());
    -		authenticationManagers.put("trusted", authenticationManager);
    -		authenticationManagerResolver.resolve(null).flatMap((manager) -> manager.authenticate(token)).block();
    -		verify(authenticationManager).authenticate(token);
    -		authenticationManagers.clear();
    -		// @formatter:off
    -		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    -				.isThrownBy(() -> authenticationManagerResolver.resolve(null)
    -						.flatMap((manager) -> manager.authenticate(token))
    -						.block())
    -				.withMessageContaining("Invalid issuer");
    -		// @formatter:on
    -	}
    -
    -	@Test
    -	public void resolveWhenBearerTokenMalformedThenException() {
    -		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
    -				"trusted");
    -		Authentication token = withBearerToken("jwt");
    -		// @formatter:off
    -		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    -				.isThrownBy(() -> authenticationManagerResolver.resolve(null)
    -						.flatMap((manager) -> manager.authenticate(token))
    -						.block())
    -				.withMessageNotContaining("Invalid issuer");
    -		// @formatter:on
    -	}
    -
    -	@Test
    -	public void resolveWhenBearerTokenNoIssuerThenException() {
    -		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
    -				"trusted");
    -		Authentication token = withBearerToken(this.noIssuer);
    -		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    -			.isThrownBy(() -> authenticationManagerResolver.resolve(null)
    -				.flatMap((manager) -> manager.authenticate(token))
    -				.block())
    -			.withMessageContaining("Missing issuer");
    -	}
    -
    -	@Test
    -	public void resolveWhenBearerTokenEvilThenGenericException() {
    -		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
    -				"trusted");
    -		Authentication token = withBearerToken(this.evil);
    -		// @formatter:off
    -		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    -				.isThrownBy(() -> authenticationManagerResolver.resolve(null)
    -						.flatMap((manager) -> manager.authenticate(token))
    -						.block())
    -				.withMessage("Invalid token");
    -		// @formatter:on
    -	}
    -
    -	@Test
    -	public void constructorWhenNullOrEmptyIssuersThenException() {
    -		assertThatIllegalArgumentException()
    -			.isThrownBy(() -> new JwtIssuerReactiveAuthenticationManagerResolver((Collection) null));
    -		assertThatIllegalArgumentException()
    -			.isThrownBy(() -> new JwtIssuerReactiveAuthenticationManagerResolver(Collections.emptyList()));
    -	}
    -
    -	@Test
    -	public void constructorWhenNullAuthenticationManagerResolverThenException() {
    -		assertThatIllegalArgumentException().isThrownBy(
    -				() -> new JwtIssuerReactiveAuthenticationManagerResolver((ReactiveAuthenticationManagerResolver) null));
    -	}
    -
    -	private String jwt(String claim, String value) {
    -		PlainJWT jwt = new PlainJWT(new JWTClaimsSet.Builder().claim(claim, value).build());
    -		return jwt.serialize();
    -	}
    -
    -	private BearerTokenAuthenticationToken withBearerToken(String token) {
    -		return new BearerTokenAuthenticationToken(token);
    -	}
    -
    -}
    diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverTests.java
    index f12c6d65be..a830cbdfd8 100644
    --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverTests.java
    +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/authentication/JwtIssuerReactiveAuthenticationManagerResolverTests.java
    @@ -20,7 +20,6 @@ import java.util.Collection;
     import java.util.Collections;
     import java.util.HashMap;
     import java.util.Map;
    -import java.util.function.Predicate;
     
     import com.nimbusds.jose.JWSAlgorithm;
     import com.nimbusds.jose.JWSHeader;
    @@ -72,7 +71,7 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
     	private String noIssuer = jwt("sub", "sub");
     
     	@Test
    -	public void resolveWhenUsingFromTrustedIssuersThenReturnsAuthenticationManager() throws Exception {
    +	public void resolveWhenUsingTrustedIssuerThenReturnsAuthenticationManager() throws Exception {
     		try (MockWebServer server = new MockWebServer()) {
     			String issuer = server.url("").toString();
     			server.enqueue(new MockResponse().setResponseCode(200)
    @@ -87,35 +86,8 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
     			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 = JwtIssuerReactiveAuthenticationManagerResolver
    -				.fromTrustedIssuers(issuer);
    -			ReactiveAuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null).block();
    -			assertThat(authenticationManager).isNotNull();
    -			BearerTokenAuthenticationToken token = withBearerToken(jws.serialize());
    -			Authentication authentication = authenticationManager.authenticate(token).block();
    -			assertThat(authentication).isNotNull();
    -			assertThat(authentication.isAuthenticated()).isTrue();
    -		}
    -	}
    -
    -	@Test
    -	public void resolveWhenUsingFromTrustedIssuersPredicateThenReturnsAuthenticationManager() throws Exception {
    -		try (MockWebServer server = new MockWebServer()) {
    -			String issuer = server.url("").toString();
    -			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));
    -			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 = JwtIssuerReactiveAuthenticationManagerResolver
    -				.fromTrustedIssuers(issuer::equals);
    +			JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
    +					issuer);
     			ReactiveAuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null).block();
     			assertThat(authenticationManager).isNotNull();
     			BearerTokenAuthenticationToken token = withBearerToken(jws.serialize());
    @@ -141,8 +113,8 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
     			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 = JwtIssuerReactiveAuthenticationManagerResolver
    -				.fromTrustedIssuers(issuer);
    +			JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
    +					issuer);
     			ReactiveAuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null).block();
     			assertThat(authenticationManager).isNotNull();
     			Authentication token = withBearerToken(jws.serialize());
    @@ -173,8 +145,8 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
     
     	@Test
     	public void resolveWhenUsingUntrustedIssuerThenException() {
    -		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver
    -			.fromTrustedIssuers("other", "issuers");
    +		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
    +				"other", "issuers");
     		Authentication token = withBearerToken(this.jwt);
     		// @formatter:off
     		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    @@ -224,8 +196,8 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
     
     	@Test
     	public void resolveWhenBearerTokenMalformedThenException() {
    -		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver
    -			.fromTrustedIssuers("trusted");
    +		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
    +				"trusted");
     		Authentication token = withBearerToken("jwt");
     		// @formatter:off
     		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    @@ -238,8 +210,8 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
     
     	@Test
     	public void resolveWhenBearerTokenNoIssuerThenException() {
    -		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver
    -			.fromTrustedIssuers("trusted");
    +		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
    +				"trusted");
     		Authentication token = withBearerToken(this.noIssuer);
     		assertThatExceptionOfType(OAuth2AuthenticationException.class)
     			.isThrownBy(() -> authenticationManagerResolver.resolve(null)
    @@ -250,8 +222,8 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
     
     	@Test
     	public void resolveWhenBearerTokenEvilThenGenericException() {
    -		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = JwtIssuerReactiveAuthenticationManagerResolver
    -			.fromTrustedIssuers("trusted");
    +		JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
    +				"trusted");
     		Authentication token = withBearerToken(this.evil);
     		// @formatter:off
     		assertThatExceptionOfType(OAuth2AuthenticationException.class)
    @@ -263,13 +235,11 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
     	}
     
     	@Test
    -	public void factoryWhenNullOrEmptyIssuersThenException() {
    -		assertThatIllegalArgumentException().isThrownBy(
    -				() -> JwtIssuerReactiveAuthenticationManagerResolver.fromTrustedIssuers((Predicate) null));
    -		assertThatIllegalArgumentException().isThrownBy(
    -				() -> JwtIssuerReactiveAuthenticationManagerResolver.fromTrustedIssuers((Collection) null));
    -		assertThatIllegalArgumentException().isThrownBy(
    -				() -> JwtIssuerReactiveAuthenticationManagerResolver.fromTrustedIssuers(Collections.emptyList()));
    +	public void constructorWhenNullOrEmptyIssuersThenException() {
    +		assertThatIllegalArgumentException()
    +			.isThrownBy(() -> new JwtIssuerReactiveAuthenticationManagerResolver((Collection) null));
    +		assertThatIllegalArgumentException()
    +			.isThrownBy(() -> new JwtIssuerReactiveAuthenticationManagerResolver(Collections.emptyList()));
     	}
     
     	@Test
    diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospectorTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospectorTests.java
    index 68ff78a2ac..d4ae7111c6 100644
    --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospectorTests.java
    +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/NimbusReactiveOpaqueTokenIntrospectorTests.java
    @@ -270,6 +270,7 @@ public class NimbusReactiveOpaqueTokenIntrospectorTests {
     		WebClient webClient = spy(WebClient.class);
     		given(webClient.post()).willReturn(spec);
     		ClientResponse clientResponse = mock(ClientResponse.class);
    +		given(clientResponse.rawStatusCode()).willReturn(200);
     		given(clientResponse.statusCode()).willReturn(HttpStatus.OK);
     		given(clientResponse.bodyToMono(String.class)).willReturn(Mono.just(response));
     		ClientResponse.Headers headers = mock(ClientResponse.Headers.class);
    diff --git a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/SpringReactiveOpaqueTokenIntrospectorTests.java b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/SpringReactiveOpaqueTokenIntrospectorTests.java
    index f395fd6326..974565affd 100644
    --- a/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/SpringReactiveOpaqueTokenIntrospectorTests.java
    +++ b/oauth2/oauth2-resource-server/src/test/java/org/springframework/security/oauth2/server/resource/introspection/SpringReactiveOpaqueTokenIntrospectorTests.java
    @@ -227,6 +227,7 @@ public class SpringReactiveOpaqueTokenIntrospectorTests {
     		WebClient webClient = spy(WebClient.class);
     		given(webClient.post()).willReturn(spec);
     		ClientResponse clientResponse = mock(ClientResponse.class);
    +		given(clientResponse.rawStatusCode()).willReturn(200);
     		given(clientResponse.statusCode()).willReturn(HttpStatus.OK);
     		given(clientResponse.bodyToMono(STRING_OBJECT_MAP)).willReturn(Mono.just(response));
     		ClientResponse.Headers headers = mock(ClientResponse.Headers.class);
    diff --git a/rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadInterceptorRSocket.java b/rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadInterceptorRSocket.java
    index cb9efc01fb..6bd1c674f7 100644
    --- a/rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadInterceptorRSocket.java
    +++ b/rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadInterceptorRSocket.java
    @@ -94,7 +94,7 @@ class PayloadInterceptorRSocket extends RSocketProxy {
     			return intercept(PayloadExchangeType.REQUEST_CHANNEL, firstPayload)
     				.flatMapMany((context) -> innerFlux.index()
     					.concatMap((tuple) -> justOrIntercept(tuple.getT1(), tuple.getT2()))
    -					.transform(this.source::requestChannel)
    +					.transform((securedPayloads) -> this.source.requestChannel(securedPayloads))
     					.contextWrite(context));
     		});
     	}
    @@ -115,7 +115,7 @@ class PayloadInterceptorRSocket extends RSocketProxy {
     			DefaultPayloadExchange exchange = new DefaultPayloadExchange(type, payload, this.metadataMimeType,
     					this.dataMimeType);
     			return chain.next(exchange)
    -				.then(Mono.fromCallable(chain::getContext))
    +				.then(Mono.fromCallable(() -> chain.getContext()))
     				.defaultIfEmpty(Context.empty())
     				.contextWrite(this.context);
     		});
    diff --git a/rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadSocketAcceptor.java b/rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadSocketAcceptor.java
    index 2270964f1c..ccc9b5776b 100644
    --- a/rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadSocketAcceptor.java
    +++ b/rsocket/src/main/java/org/springframework/security/rsocket/core/PayloadSocketAcceptor.java
    @@ -81,7 +81,9 @@ class PayloadSocketAcceptor implements SocketAcceptor {
     			ContextPayloadInterceptorChain chain = new ContextPayloadInterceptorChain(this.interceptors);
     			DefaultPayloadExchange exchange = new DefaultPayloadExchange(PayloadExchangeType.SETUP, payload,
     					metadataMimeType, dataMimeType);
    -			return chain.next(exchange).then(Mono.fromCallable(chain::getContext)).defaultIfEmpty(Context.empty());
    +			return chain.next(exchange)
    +				.then(Mono.fromCallable(() -> chain.getContext()))
    +				.defaultIfEmpty(Context.empty());
     		});
     	}
     
    diff --git a/rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/PayloadExchangeMatchers.java b/rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/PayloadExchangeMatchers.java
    index d47cd44d86..ef96a1b1c9 100644
    --- a/rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/PayloadExchangeMatchers.java
    +++ b/rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/PayloadExchangeMatchers.java
    @@ -16,6 +16,9 @@
     
     package org.springframework.security.rsocket.util.matcher;
     
    +import reactor.core.publisher.Mono;
    +
    +import org.springframework.security.rsocket.api.PayloadExchange;
     import org.springframework.security.rsocket.api.PayloadExchangeType;
     
     /**
    @@ -27,17 +30,37 @@ public final class PayloadExchangeMatchers {
     	}
     
     	public static PayloadExchangeMatcher setup() {
    -		return (exchange) -> PayloadExchangeType.SETUP.equals(exchange.getType())
    -				? PayloadExchangeMatcher.MatchResult.match() : PayloadExchangeMatcher.MatchResult.notMatch();
    +		return new PayloadExchangeMatcher() {
    +
    +			@Override
    +			public Mono matches(PayloadExchange exchange) {
    +				return PayloadExchangeType.SETUP.equals(exchange.getType()) ? MatchResult.match()
    +						: MatchResult.notMatch();
    +			}
    +
    +		};
     	}
     
     	public static PayloadExchangeMatcher anyRequest() {
    -		return (exchange) -> exchange.getType().isRequest() ? PayloadExchangeMatcher.MatchResult.match()
    -				: PayloadExchangeMatcher.MatchResult.notMatch();
    +		return new PayloadExchangeMatcher() {
    +
    +			@Override
    +			public Mono matches(PayloadExchange exchange) {
    +				return exchange.getType().isRequest() ? MatchResult.match() : MatchResult.notMatch();
    +			}
    +
    +		};
     	}
     
     	public static PayloadExchangeMatcher anyExchange() {
    -		return (exchange) -> PayloadExchangeMatcher.MatchResult.match();
    +		return new PayloadExchangeMatcher() {
    +
    +			@Override
    +			public Mono matches(PayloadExchange exchange) {
    +				return MatchResult.match();
    +			}
    +
    +		};
     	}
     
     }
    diff --git a/rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/RoutePayloadExchangeMatcher.java b/rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/RoutePayloadExchangeMatcher.java
    index 45f4e7b303..aad1522ddd 100644
    --- a/rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/RoutePayloadExchangeMatcher.java
    +++ b/rsocket/src/main/java/org/springframework/security/rsocket/util/matcher/RoutePayloadExchangeMatcher.java
    @@ -52,9 +52,9 @@ public class RoutePayloadExchangeMatcher implements PayloadExchangeMatcher {
     		Map metadata = this.metadataExtractor.extract(exchange.getPayload(),
     				exchange.getMetadataMimeType());
     		return Optional.ofNullable((String) metadata.get(MetadataExtractor.ROUTE_KEY))
    -			.map(this.routeMatcher::parseRoute)
    +			.map((routeValue) -> this.routeMatcher.parseRoute(routeValue))
     			.map((route) -> this.routeMatcher.matchAndExtract(this.pattern, route))
    -			.map(MatchResult::match)
    +			.map((v) -> MatchResult.match(v))
     			.orElse(MatchResult.notMatch());
     	}
     
    diff --git a/rsocket/src/test/java/org/springframework/security/rsocket/authentication/AuthenticationPayloadInterceptorTests.java b/rsocket/src/test/java/org/springframework/security/rsocket/authentication/AuthenticationPayloadInterceptorTests.java
    index 070c400a84..e41455f2f7 100644
    --- a/rsocket/src/test/java/org/springframework/security/rsocket/authentication/AuthenticationPayloadInterceptorTests.java
    +++ b/rsocket/src/test/java/org/springframework/security/rsocket/authentication/AuthenticationPayloadInterceptorTests.java
    @@ -89,8 +89,8 @@ public class AuthenticationPayloadInterceptorTests {
     		interceptor.intercept(exchange, authenticationPayloadChain).block();
     		Authentication authentication = authenticationPayloadChain.getAuthentication();
     		verify(this.authenticationManager).authenticate(this.authenticationArg.capture());
    -		assertThat(this.authenticationArg.getValue()).usingRecursiveComparison()
    -			.isEqualTo(UsernamePasswordAuthenticationToken.unauthenticated("user", "password"));
    +		assertThat(this.authenticationArg.getValue())
    +			.isEqualToComparingFieldByField(UsernamePasswordAuthenticationToken.unauthenticated("user", "password"));
     		assertThat(authentication).isEqualTo(expectedAuthentication);
     	}
     
    diff --git a/rsocket/src/test/java/org/springframework/security/rsocket/metadata/BasicAuthenticationDecoderTests.java b/rsocket/src/test/java/org/springframework/security/rsocket/metadata/BasicAuthenticationDecoderTests.java
    index 1ae60bb652..7c38154f79 100644
    --- a/rsocket/src/test/java/org/springframework/security/rsocket/metadata/BasicAuthenticationDecoderTests.java
    +++ b/rsocket/src/test/java/org/springframework/security/rsocket/metadata/BasicAuthenticationDecoderTests.java
    @@ -46,7 +46,7 @@ public class BasicAuthenticationDecoderTests {
     		UsernamePasswordMetadata actualCredentials = decoder
     			.decodeToMono(Mono.just(dataBuffer), elementType, mimeType, hints)
     			.block();
    -		assertThat(actualCredentials).usingRecursiveComparison().isEqualTo(expectedCredentials);
    +		assertThat(actualCredentials).isEqualToComparingFieldByField(expectedCredentials);
     	}
     
     }
    diff --git a/saml2/saml2-service-provider/spring-security-saml2-service-provider.gradle b/saml2/saml2-service-provider/spring-security-saml2-service-provider.gradle
    index bc6fedd828..e00593c149 100644
    --- a/saml2/saml2-service-provider/spring-security-saml2-service-provider.gradle
    +++ b/saml2/saml2-service-provider/spring-security-saml2-service-provider.gradle
    @@ -22,6 +22,7 @@ dependencies {
     	testImplementation "org.junit.jupiter:junit-jupiter-params"
     	testImplementation "org.junit.jupiter:junit-jupiter-engine"
     	testImplementation "org.mockito:mockito-core"
    +	testImplementation "org.mockito:mockito-inline"
     	testImplementation "org.mockito:mockito-junit-jupiter"
     	testImplementation "org.springframework:spring-test"
     }
    diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlMetadataResolver.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlMetadataResolver.java
    index e4b23b5000..a8137fb93f 100644
    --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlMetadataResolver.java
    +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlMetadataResolver.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2002-2023 the original author or authors.
    + * Copyright 2002-2022 the original author or authors.
      *
      * Licensed under the Apache License, Version 2.0 (the "License");
      * you may not use this file except in compliance with the License.
    @@ -72,8 +72,6 @@ public final class OpenSamlMetadataResolver implements Saml2MetadataResolver {
     	private Consumer entityDescriptorCustomizer = (parameters) -> {
     	};
     
    -	private boolean usePrettyPrint = true;
    -
     	public OpenSamlMetadataResolver() {
     		this.entityDescriptorMarshaller = (EntityDescriptorMarshaller) XMLObjectProviderRegistrySupport
     			.getMarshallerFactory()
    @@ -125,15 +123,6 @@ public final class OpenSamlMetadataResolver implements Saml2MetadataResolver {
     		this.entityDescriptorCustomizer = entityDescriptorCustomizer;
     	}
     
    -	/**
    -	 * Configure whether to pretty-print the metadata XML. This can be helpful when
    -	 * signing the metadata payload.
    -	 * @since 6.2
    -	 **/
    -	public void setUsePrettyPrint(boolean usePrettyPrint) {
    -		this.usePrettyPrint = usePrettyPrint;
    -	}
    -
     	private SPSSODescriptor buildSpSsoDescriptor(RelyingPartyRegistration registration) {
     		SPSSODescriptor spSsoDescriptor = build(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
     		spSsoDescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS);
    @@ -215,10 +204,7 @@ public final class OpenSamlMetadataResolver implements Saml2MetadataResolver {
     	private String serialize(EntityDescriptor entityDescriptor) {
     		try {
     			Element element = this.entityDescriptorMarshaller.marshall(entityDescriptor);
    -			if (this.usePrettyPrint) {
    -				return SerializeSupport.prettyPrintXML(element);
    -			}
    -			return SerializeSupport.nodeToString(element);
    +			return SerializeSupport.prettyPrintXML(element);
     		}
     		catch (Exception ex) {
     			throw new Saml2Exception(ex);
    @@ -228,10 +214,7 @@ public final class OpenSamlMetadataResolver implements Saml2MetadataResolver {
     	private String serialize(EntitiesDescriptor entities) {
     		try {
     			Element element = this.entitiesDescriptorMarshaller.marshall(entities);
    -			if (this.usePrettyPrint) {
    -				return SerializeSupport.prettyPrintXML(element);
    -			}
    -			return SerializeSupport.nodeToString(element);
    +			return SerializeSupport.prettyPrintXML(element);
     		}
     		catch (Exception ex) {
     			throw new Saml2Exception(ex);
    diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlAuthenticationRequestResolver.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlAuthenticationRequestResolver.java
    index 39cd8bafb2..b49aacb287 100644
    --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlAuthenticationRequestResolver.java
    +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSamlAuthenticationRequestResolver.java
    @@ -92,7 +92,7 @@ class OpenSamlAuthenticationRequestResolver {
     		XMLObjectProviderRegistry registry = ConfigurationService.get(XMLObjectProviderRegistry.class);
     		this.marshaller = (AuthnRequestMarshaller) registry.getMarshallerFactory()
     			.getMarshaller(AuthnRequest.DEFAULT_ELEMENT_NAME);
    -		Assert.notNull(this.marshaller, "authnRequestMarshaller must be configured in OpenSAML");
    +		Assert.notNull(this.marshaller, "logoutRequestMarshaller must be configured in OpenSAML");
     		this.authnRequestBuilder = (AuthnRequestBuilder) XMLObjectProviderRegistrySupport.getBuilderFactory()
     			.getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME);
     		Assert.notNull(this.authnRequestBuilder, "authnRequestBuilder must be configured in OpenSAML");
    diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2WebSsoAuthenticationFilter.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2WebSsoAuthenticationFilter.java
    index 334e8a51a8..4affc10049 100644
    --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2WebSsoAuthenticationFilter.java
    +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2WebSsoAuthenticationFilter.java
    @@ -35,9 +35,6 @@ import org.springframework.security.saml2.provider.service.web.Saml2Authenticati
     import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
     import org.springframework.security.web.authentication.AuthenticationConverter;
     import org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy;
    -import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
    -import org.springframework.security.web.util.matcher.OrRequestMatcher;
    -import org.springframework.security.web.util.matcher.RequestMatcher;
     import org.springframework.util.Assert;
     
     /**
    @@ -47,9 +44,6 @@ public class Saml2WebSsoAuthenticationFilter extends AbstractAuthenticationProce
     
     	public static final String DEFAULT_FILTER_PROCESSES_URI = "/login/saml2/sso/{registrationId}";
     
    -	private static final RequestMatcher DEFAULT_REQUEST_MATCHER = new OrRequestMatcher(
    -			new AntPathRequestMatcher(DEFAULT_FILTER_PROCESSES_URI), new AntPathRequestMatcher("/login/saml2/sso"));
    -
     	private final AuthenticationConverter authenticationConverter;
     
     	private Saml2AuthenticationRequestRepository authenticationRequestRepository = new HttpSessionSaml2AuthenticationRequestRepository();
    @@ -81,21 +75,6 @@ public class Saml2WebSsoAuthenticationFilter extends AbstractAuthenticationProce
     				"filterProcessesUrl must contain a {registrationId} match variable");
     	}
     
    -	/**
    -	 * Creates a {@link Saml2WebSsoAuthenticationFilter} that is configured to use the
    -	 * {@link #DEFAULT_FILTER_PROCESSES_URI} processing URL
    -	 * @param authenticationConverter the strategy for converting an
    -	 * {@link HttpServletRequest} into an {@link Authentication}
    -	 * @since 6.2
    -	 */
    -	public Saml2WebSsoAuthenticationFilter(AuthenticationConverter authenticationConverter) {
    -		super(DEFAULT_REQUEST_MATCHER);
    -		Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
    -		this.authenticationConverter = authenticationConverter;
    -		setAllowSessionCreation(true);
    -		setSessionAuthenticationStrategy(new ChangeSessionIdAuthenticationStrategy());
    -	}
    -
     	/**
     	 * Creates a {@link Saml2WebSsoAuthenticationFilter} given the provided parameters
     	 * @param authenticationConverter the strategy for converting an
    @@ -150,7 +129,8 @@ public class Saml2WebSsoAuthenticationFilter extends AbstractAuthenticationProce
     
     	private void setAuthenticationRequestRepositoryIntoAuthenticationConverter(
     			Saml2AuthenticationRequestRepository authenticationRequestRepository) {
    -		if (this.authenticationConverter instanceof Saml2AuthenticationTokenConverter authenticationTokenConverter) {
    +		if (this.authenticationConverter instanceof Saml2AuthenticationTokenConverter) {
    +			Saml2AuthenticationTokenConverter authenticationTokenConverter = (Saml2AuthenticationTokenConverter) this.authenticationConverter;
     			authenticationTokenConverter.setAuthenticationRequestRepository(authenticationRequestRepository);
     		}
     	}
    diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java
    index 0c5657e200..89e9a9454a 100644
    --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java
    +++ b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/TestOpenSamlObjects.java
    @@ -17,7 +17,6 @@
     package org.springframework.security.saml2.provider.service.authentication;
     
     import java.security.cert.X509Certificate;
    -import java.time.Instant;
     import java.util.ArrayList;
     import java.util.Base64;
     import java.util.List;
    @@ -118,7 +117,7 @@ public final class TestOpenSamlObjects {
     		return response(DESTINATION, ASSERTING_PARTY_ENTITY_ID);
     	}
     
    -	public static Response response(String destination, String issuerEntityId) {
    +	static Response response(String destination, String issuerEntityId) {
     		Response response = build(Response.DEFAULT_ELEMENT_NAME);
     		response.setID("R" + UUID.randomUUID().toString());
     		response.setVersion(SAMLVersion.VERSION_20);
    @@ -144,15 +143,13 @@ public final class TestOpenSamlObjects {
     		return assertion(USERNAME, ASSERTING_PARTY_ENTITY_ID, RELYING_PARTY_ENTITY_ID, DESTINATION);
     	}
     
    -	public static Assertion assertion(String username, String issuerEntityId, String recipientEntityId,
    -			String recipientUri) {
    +	static Assertion assertion(String username, String issuerEntityId, String recipientEntityId, String recipientUri) {
     		Assertion assertion = build(Assertion.DEFAULT_ELEMENT_NAME);
     		assertion.setID("A" + UUID.randomUUID().toString());
     		assertion.setVersion(SAMLVersion.VERSION_20);
     		assertion.setIssuer(issuer(issuerEntityId));
     		assertion.setSubject(subject(username));
     		assertion.setConditions(conditions());
    -		assertion.setIssueInstant(Instant.now());
     		SubjectConfirmation subjectConfirmation = subjectConfirmation();
     		subjectConfirmation.setMethod(SubjectConfirmation.METHOD_BEARER);
     		SubjectConfirmationData confirmationData = subjectConfirmationData(recipientEntityId);
    diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationsTests.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationsTests.java
    index 7d89c99810..2bf8b1fb03 100644
    --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationsTests.java
    +++ b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationsTests.java
    @@ -264,7 +264,7 @@ public class RelyingPartyRegistrationsTests {
     	public void collectionFromMetadataLocationCanHandleFederationMetadata() {
     		Collection federationMetadataWithSkippedSPEntries = RelyingPartyRegistrations
     			.collectionFromMetadataLocation("classpath:test-federated-metadata.xml");
    -		assertThat(federationMetadataWithSkippedSPEntries).hasSize(1);
    +		assertThat(federationMetadataWithSkippedSPEntries.size()).isEqualTo(1);
     	}
     
     	@Test
    diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2WebSsoAuthenticationFilterTests.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2WebSsoAuthenticationFilterTests.java
    index 7b3abddf95..29537b3b6f 100644
    --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2WebSsoAuthenticationFilterTests.java
    +++ b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2WebSsoAuthenticationFilterTests.java
    @@ -17,6 +17,7 @@
     package org.springframework.security.saml2.provider.service.web.authentication;
     
     import jakarta.servlet.http.HttpServletResponse;
    +import org.junit.jupiter.api.Assertions;
     import org.junit.jupiter.api.BeforeEach;
     import org.junit.jupiter.api.Test;
     
    @@ -44,7 +45,6 @@ import org.springframework.security.web.authentication.WebAuthenticationDetails;
     import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
     import org.springframework.security.web.util.matcher.RequestMatcher;
     
    -import static org.assertj.core.api.Assertions.assertThat;
     import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
     import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
     import static org.assertj.core.api.Assertions.assertThatNoException;
    @@ -93,7 +93,7 @@ public class Saml2WebSsoAuthenticationFilterTests {
     
     	@Test
     	public void requiresAuthenticationWhenHappyPathThenReturnsTrue() {
    -		assertThat(this.filter.requiresAuthentication(this.request, this.response)).isTrue();
    +		Assertions.assertTrue(this.filter.requiresAuthentication(this.request, this.response));
     	}
     
     	@Test
    @@ -101,7 +101,7 @@ public class Saml2WebSsoAuthenticationFilterTests {
     		this.filter = new Saml2WebSsoAuthenticationFilter(this.repository, "/some/other/path/{registrationId}");
     		this.request.setPathInfo("/some/other/path/idp-registration-id");
     		this.request.setParameter(Saml2ParameterNames.SAML_RESPONSE, "xml-data-goes-here");
    -		assertThat(this.filter.requiresAuthentication(this.request, this.response)).isTrue();
    +		Assertions.assertTrue(this.filter.requiresAuthentication(this.request, this.response));
     	}
     
     	@Test
    @@ -142,7 +142,7 @@ public class Saml2WebSsoAuthenticationFilterTests {
     		this.filter.setAuthenticationDetailsSource(authenticationDetailsSource);
     		this.request.setPathInfo("/some/other/path/idp-registration-id");
     		this.filter.attemptAuthentication(this.request, this.response);
    -		assertThat(token.getDetails()).isEqualTo(details);
    +		Assertions.assertEquals(details, token.getDetails());
     	}
     
     	@Test
    diff --git a/settings.gradle b/settings.gradle
    index cb9c8bb8a0..af46967b84 100644
    --- a/settings.gradle
    +++ b/settings.gradle
    @@ -12,7 +12,6 @@ plugins {
     dependencyResolutionManagement {
     	repositories {
     		mavenCentral()
    -		maven { url "https://repo.spring.io/milestone" }
     	}
     }
     
    diff --git a/taglibs/src/main/resources/META-INF/security.tld b/taglibs/src/main/resources/META-INF/security.tld
    index 5aed63096f..0b609513d1 100644
    --- a/taglibs/src/main/resources/META-INF/security.tld
    +++ b/taglibs/src/main/resources/META-INF/security.tld
    @@ -20,7 +20,7 @@
             version="2.0">
         Spring Security Authorization Tag Library
     
    -    6.2
    +    6.1
         security
         http://www.springframework.org/security/tags
     
    diff --git a/test/spring-security-test.gradle b/test/spring-security-test.gradle
    index 15d07dd367..92b3868438 100644
    --- a/test/spring-security-test.gradle
    +++ b/test/spring-security-test.gradle
    @@ -27,6 +27,7 @@ dependencies {
     	testImplementation "org.junit.jupiter:junit-jupiter-params"
     	testImplementation "org.junit.jupiter:junit-jupiter-engine"
     	testImplementation "org.mockito:mockito-core"
    +	testImplementation 'org.mockito:mockito-inline'
     	testImplementation "org.mockito:mockito-junit-jupiter"
     	testImplementation "org.springframework:spring-test"
     	testImplementation 'org.skyscreamer:jsonassert'
    diff --git a/test/src/main/java/org/springframework/security/test/context/support/WithAnonymousUserSecurityContextFactory.java b/test/src/main/java/org/springframework/security/test/context/support/WithAnonymousUserSecurityContextFactory.java
    index f366a89cbd..28cc347410 100644
    --- a/test/src/main/java/org/springframework/security/test/context/support/WithAnonymousUserSecurityContextFactory.java
    +++ b/test/src/main/java/org/springframework/security/test/context/support/WithAnonymousUserSecurityContextFactory.java
    @@ -28,7 +28,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
     import org.springframework.security.core.context.SecurityContextHolderStrategy;
     
     /**
    - * A {@link WithSecurityContextFactory} that runs with an
    + * A {@link WithAnonymousUserSecurityContextFactory} that runs with an
      * {@link AnonymousAuthenticationToken}. .
      *
      * @author Rob Winch
    diff --git a/test/src/main/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactory.java b/test/src/main/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactory.java
    index f4717a72a3..1296cca9e5 100644
    --- a/test/src/main/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactory.java
    +++ b/test/src/main/java/org/springframework/security/test/context/support/WithMockUserSecurityContextFactory.java
    @@ -33,7 +33,7 @@ import org.springframework.util.Assert;
     import org.springframework.util.StringUtils;
     
     /**
    - * A {@link WithSecurityContextFactory} that works with {@link WithMockUser}.
    + * A {@link WithUserDetailsSecurityContextFactory} that works with {@link WithMockUser}.
      *
      * @author Rob Winch
      * @since 4.0
    diff --git a/test/src/main/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactory.java b/test/src/main/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactory.java
    index 7948b9e87b..395dd53bb1 100644
    --- a/test/src/main/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactory.java
    +++ b/test/src/main/java/org/springframework/security/test/context/support/WithUserDetailsSecurityContextFactory.java
    @@ -34,7 +34,8 @@ import org.springframework.util.ClassUtils;
     import org.springframework.util.StringUtils;
     
     /**
    - * A {@link WithSecurityContextFactory} that works with {@link WithUserDetails} .
    + * A {@link WithUserDetailsSecurityContextFactory} that works with {@link WithUserDetails}
    + * .
      *
      * @author Rob Winch
      * @since 4.0
    diff --git a/test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurerOpaqueTokenTests.java b/test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurerOpaqueTokenTests.java
    index 8fb1168c30..b0c921462b 100644
    --- a/test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurerOpaqueTokenTests.java
    +++ b/test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurerOpaqueTokenTests.java
    @@ -66,7 +66,7 @@ public class SecurityMockServerConfigurerOpaqueTokenTests extends AbstractMockSe
     		BearerTokenAuthentication token = (BearerTokenAuthentication) context.getAuthentication();
     		assertThat(token.getAuthorities()).isNotEmpty();
     		assertThat(token.getToken()).isNotNull();
    -		assertThat(token.getTokenAttributes()).containsEntry(OAuth2TokenIntrospectionClaimNames.SUB, "user");
    +		assertThat(token.getTokenAttributes().get(OAuth2TokenIntrospectionClaimNames.SUB)).isEqualTo("user");
     	}
     
     	@Test
    diff --git a/test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurersJwtTests.java b/test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurersJwtTests.java
    index ad43313e00..b8d1d2ddd5 100644
    --- a/test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurersJwtTests.java
    +++ b/test/src/test/java/org/springframework/security/test/web/reactive/server/SecurityMockServerConfigurersJwtTests.java
    @@ -65,7 +65,7 @@ public class SecurityMockServerConfigurersJwtTests extends AbstractMockServerCon
     		assertThat(token.getAuthorities()).isNotEmpty();
     		assertThat(token.getToken()).isNotNull();
     		assertThat(token.getToken().getSubject()).isEqualTo("user");
    -		assertThat(token.getToken().getHeaders()).containsEntry("alg", "none");
    +		assertThat(token.getToken().getHeaders().get("alg")).isEqualTo("none");
     	}
     
     	@Test
    @@ -137,7 +137,7 @@ public class SecurityMockServerConfigurersJwtTests extends AbstractMockServerCon
     		JwtAuthenticationToken retrievedToken = (JwtAuthenticationToken) context.getAuthentication();
     		assertThat(retrievedToken.getToken().getSubject()).isEqualTo("some_user");
     		assertThat(retrievedToken.getToken().getTokenValue()).isEqualTo("token");
    -		assertThat(retrievedToken.getToken().getHeaders()).containsEntry("header1", "value1");
    +		assertThat(retrievedToken.getToken().getHeaders().get("header1")).isEqualTo("value1");
     	}
     
     }
    diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsJwtTests.java b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsJwtTests.java
    index 03192ac4ec..b6726b0b66 100644
    --- a/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsJwtTests.java
    +++ b/test/src/test/java/org/springframework/security/test/web/servlet/request/SecurityMockMvcRequestPostProcessorsJwtTests.java
    @@ -102,7 +102,7 @@ public class SecurityMockMvcRequestPostProcessorsJwtTests {
     		assertThat(token.getAuthorities()).isNotEmpty();
     		assertThat(token.getToken()).isNotNull();
     		assertThat(token.getToken().getSubject()).isEqualTo("user");
    -		assertThat(token.getToken().getHeaders()).containsEntry("alg", "none");
    +		assertThat(token.getToken().getHeaders().get("alg")).isEqualTo("none");
     	}
     
     	@Test
    @@ -160,7 +160,7 @@ public class SecurityMockMvcRequestPostProcessorsJwtTests {
     		JwtAuthenticationToken retrievedToken = (JwtAuthenticationToken) context.getAuthentication();
     		assertThat(retrievedToken.getToken().getSubject()).isEqualTo("some_user");
     		assertThat(retrievedToken.getToken().getTokenValue()).isEqualTo("token");
    -		assertThat(retrievedToken.getToken().getHeaders()).containsEntry("header1", "value1");
    +		assertThat(retrievedToken.getToken().getHeaders().get("header1")).isEqualTo("value1");
     	}
     
     }
    diff --git a/test/src/test/java/org/springframework/security/test/web/servlet/response/Gh3409Tests.java b/test/src/test/java/org/springframework/security/test/web/servlet/response/Gh3409Tests.java
    index 1e40bf04d2..b8e193e88e 100644
    --- a/test/src/test/java/org/springframework/security/test/web/servlet/response/Gh3409Tests.java
    +++ b/test/src/test/java/org/springframework/security/test/web/servlet/response/Gh3409Tests.java
    @@ -78,7 +78,7 @@ public class Gh3409Tests {
     	}
     
     	@Test
    -	public void unauthenticatedNullAuthentication() throws Exception {
    +	public void unauthenticatedNullAuthenitcation() throws Exception {
     		// @formatter:off
     		this.mockMvc
     			.perform(get("/")
    diff --git a/web/spring-security-web.gradle b/web/spring-security-web.gradle
    index cf405549c2..c0ee717cf0 100644
    --- a/web/spring-security-web.gradle
    +++ b/web/spring-security-web.gradle
    @@ -26,6 +26,7 @@ dependencies {
     	testImplementation 'jakarta.websocket:jakarta.websocket-client-api'
     	testImplementation 'org.hamcrest:hamcrest'
     	testImplementation 'org.mockito:mockito-core'
    +	testImplementation 'org.mockito:mockito-inline'
     	testImplementation 'org.skyscreamer:jsonassert'
     	testImplementation 'org.springframework:spring-webflux'
     	testImplementation 'org.synchronoss.cloud:nio-multipart-parser'
    diff --git a/web/src/main/java/org/springframework/security/web/DefaultRedirectStrategy.java b/web/src/main/java/org/springframework/security/web/DefaultRedirectStrategy.java
    index ff74b38346..8e9f0def76 100644
    --- a/web/src/main/java/org/springframework/security/web/DefaultRedirectStrategy.java
    +++ b/web/src/main/java/org/springframework/security/web/DefaultRedirectStrategy.java
    @@ -1,5 +1,5 @@
     /*
    - * Copyright 2002-2023 the original author or authors.
    + * Copyright 2002-2016 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,8 +24,6 @@ import org.apache.commons.logging.Log;
     import org.apache.commons.logging.LogFactory;
     
     import org.springframework.core.log.LogMessage;
    -import org.springframework.http.HttpHeaders;
    -import org.springframework.http.HttpStatus;
     import org.springframework.security.web.util.UrlUtils;
     import org.springframework.util.Assert;
     
    @@ -34,7 +32,6 @@ import org.springframework.util.Assert;
      * the framework.
      *
      * @author Luke Taylor
    - * @author Mark Chesney
      * @since 3.0
      */
     public class DefaultRedirectStrategy implements RedirectStrategy {
    @@ -43,8 +40,6 @@ public class DefaultRedirectStrategy implements RedirectStrategy {
     
     	private boolean contextRelative;
     
    -	private HttpStatus statusCode = HttpStatus.FOUND;
    -
     	/**
     	 * Redirects the response to the supplied URL.
     	 * 

    @@ -60,14 +55,7 @@ public class DefaultRedirectStrategy implements RedirectStrategy { if (this.logger.isDebugEnabled()) { this.logger.debug(LogMessage.format("Redirecting to %s", redirectUrl)); } - if (this.statusCode == HttpStatus.FOUND) { - response.sendRedirect(redirectUrl); - } - else { - response.setHeader(HttpHeaders.LOCATION, redirectUrl); - response.setStatus(this.statusCode.value()); - response.getWriter().flush(); - } + response.sendRedirect(redirectUrl); } protected String calculateRedirectUrl(String contextPath, String url) { @@ -108,18 +96,4 @@ public class DefaultRedirectStrategy implements RedirectStrategy { return this.contextRelative; } - /** - * Sets the HTTP status code to use. The default is {@link HttpStatus#FOUND}. - *

    - * Note that according to RFC 7231, with {@link HttpStatus#FOUND}, a user agent MAY - * change the request method from POST to GET for the subsequent request. If this - * behavior is undesired, {@link HttpStatus#TEMPORARY_REDIRECT} can be used instead. - * @param statusCode the HTTP status code to use. - * @since 6.2 - */ - public void setStatusCode(HttpStatus statusCode) { - Assert.notNull(statusCode, "statusCode cannot be null"); - this.statusCode = statusCode; - } - } diff --git a/web/src/main/java/org/springframework/security/web/FilterInvocation.java b/web/src/main/java/org/springframework/security/web/FilterInvocation.java index f9f86476c8..a73f441674 100644 --- a/web/src/main/java/org/springframework/security/web/FilterInvocation.java +++ b/web/src/main/java/org/springframework/security/web/FilterInvocation.java @@ -146,7 +146,7 @@ public class FilterInvocation { @Override public String toString() { - if (!StringUtils.hasLength(this.request.getMethod())) { + if (StringUtils.isEmpty(this.request.getMethod())) { return "filter invocation [" + getRequestUrl() + "]"; } else { diff --git a/web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java b/web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java index 9a38b1d231..8b669c2ef2 100644 --- a/web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java +++ b/web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java @@ -94,7 +94,7 @@ public class ExceptionTranslationFilter extends GenericFilterBean implements Mes private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer(); - private final RequestCache requestCache; + private RequestCache requestCache = new HttpSessionRequestCache(); protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); diff --git a/web/src/main/java/org/springframework/security/web/access/NoOpAccessDeniedHandler.java b/web/src/main/java/org/springframework/security/web/access/NoOpAccessDeniedHandler.java deleted file mode 100644 index 329a41ecf8..0000000000 --- a/web/src/main/java/org/springframework/security/web/access/NoOpAccessDeniedHandler.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.web.access; - -import java.io.IOException; - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import org.springframework.security.access.AccessDeniedException; - -/** - * An {@link AccessDeniedHandler} implementation that does nothing. - * - * @author Marcus da Coregio - * @since 6.2 - */ -public class NoOpAccessDeniedHandler implements AccessDeniedHandler { - - @Override - public void handle(HttpServletRequest request, HttpServletResponse response, - AccessDeniedException accessDeniedException) throws IOException, ServletException { - - } - -} diff --git a/web/src/main/java/org/springframework/security/web/access/intercept/RequestKey.java b/web/src/main/java/org/springframework/security/web/access/intercept/RequestKey.java index 43722a7a7f..b608e4f8b3 100644 --- a/web/src/main/java/org/springframework/security/web/access/intercept/RequestKey.java +++ b/web/src/main/java/org/springframework/security/web/access/intercept/RequestKey.java @@ -48,9 +48,10 @@ public class RequestKey { @Override public boolean equals(Object obj) { - if (!(obj instanceof RequestKey key)) { + if (!(obj instanceof RequestKey)) { return false; } + RequestKey key = (RequestKey) obj; if (!this.url.equals(key.url)) { return false; } diff --git a/web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java b/web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java index 7f00779c4e..6192426ac3 100644 --- a/web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java +++ b/web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,12 +26,9 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.core.log.LogMessage; -import org.springframework.security.authorization.AuthenticatedAuthorizationManager; -import org.springframework.security.authorization.AuthorityAuthorizationManager; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.core.Authentication; -import org.springframework.security.web.util.matcher.AnyRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher.MatchResult; import org.springframework.security.web.util.matcher.RequestMatcherEntry; @@ -105,8 +102,6 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho */ public static final class Builder { - private boolean anyRequestConfigured; - private final List>> mappings = new ArrayList<>(); /** @@ -116,7 +111,6 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho * @return the {@link Builder} for further customizations */ public Builder add(RequestMatcher matcher, AuthorizationManager manager) { - Assert.state(!this.anyRequestConfigured, "Can't add mappings after anyRequest"); Assert.notNull(matcher, "matcher cannot be null"); Assert.notNull(manager, "manager cannot be null"); this.mappings.add(new RequestMatcherEntry<>(matcher, manager)); @@ -133,34 +127,11 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho */ public Builder mappings( Consumer>>> mappingsConsumer) { - Assert.state(!this.anyRequestConfigured, "Can't configure mappings after anyRequest"); Assert.notNull(mappingsConsumer, "mappingsConsumer cannot be null"); mappingsConsumer.accept(this.mappings); return this; } - /** - * Maps any request. - * @return the {@link AuthorizedUrl} for further customizations - * @since 6.2 - */ - public AuthorizedUrl anyRequest() { - Assert.state(!this.anyRequestConfigured, "Can't configure anyRequest after itself"); - this.anyRequestConfigured = true; - return new AuthorizedUrl(AnyRequestMatcher.INSTANCE); - } - - /** - * Maps {@link RequestMatcher}s to {@link AuthorizationManager}. - * @param matchers the {@link RequestMatcher}s to map - * @return the {@link AuthorizedUrl} for further customizations - * @since 6.2 - */ - public AuthorizedUrl requestMatchers(RequestMatcher... matchers) { - Assert.state(!this.anyRequestConfigured, "Can't configure requestMatchers after anyRequest"); - return new AuthorizedUrl(matchers); - } - /** * Creates a {@link RequestMatcherDelegatingAuthorizationManager} instance. * @return the {@link RequestMatcherDelegatingAuthorizationManager} instance @@ -169,123 +140,6 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho return new RequestMatcherDelegatingAuthorizationManager(this.mappings); } - /** - * An object that allows configuring the {@link AuthorizationManager} for - * {@link RequestMatcher}s. - * - * @author Evgeniy Cheban - * @since 6.2 - */ - public final class AuthorizedUrl { - - private final List matchers; - - private AuthorizedUrl(RequestMatcher... matchers) { - this(List.of(matchers)); - } - - private AuthorizedUrl(List matchers) { - this.matchers = matchers; - } - - /** - * Specify that URLs are allowed by anyone. - * @return the {@link Builder} for further customizations - */ - public Builder permitAll() { - return access((a, o) -> new AuthorizationDecision(true)); - } - - /** - * Specify that URLs are not allowed by anyone. - * @return the {@link Builder} for further customizations - */ - public Builder denyAll() { - return access((a, o) -> new AuthorizationDecision(false)); - } - - /** - * Specify that URLs are allowed by any authenticated user. - * @return the {@link Builder} for further customizations - */ - public Builder authenticated() { - return access(AuthenticatedAuthorizationManager.authenticated()); - } - - /** - * Specify that URLs are allowed by users who have authenticated and were not - * "remembered". - * @return the {@link Builder} for further customization - */ - public Builder fullyAuthenticated() { - return access(AuthenticatedAuthorizationManager.fullyAuthenticated()); - } - - /** - * Specify that URLs are allowed by users that have been remembered. - * @return the {@link Builder} for further customization - */ - public Builder rememberMe() { - return access(AuthenticatedAuthorizationManager.rememberMe()); - } - - /** - * Specify that URLs are allowed by anonymous users. - * @return the {@link Builder} for further customization - */ - public Builder anonymous() { - return access(AuthenticatedAuthorizationManager.anonymous()); - } - - /** - * Specifies a user requires a role. - * @param role the role that should be required which is prepended with ROLE_ - * automatically (i.e. USER, ADMIN, etc). It should not start with ROLE_ - * @return {@link Builder} for further customizations - */ - public Builder hasRole(String role) { - return access(AuthorityAuthorizationManager.hasRole(role)); - } - - /** - * Specifies that a user requires one of many roles. - * @param roles the roles that the user should have at least one of (i.e. - * ADMIN, USER, etc). Each role should not start with ROLE_ since it is - * automatically prepended already - * @return the {@link Builder} for further customizations - */ - public Builder hasAnyRole(String... roles) { - return access(AuthorityAuthorizationManager.hasAnyRole(roles)); - } - - /** - * Specifies a user requires an authority. - * @param authority the authority that should be required - * @return the {@link Builder} for further customizations - */ - public Builder hasAuthority(String authority) { - return access(AuthorityAuthorizationManager.hasAuthority(authority)); - } - - /** - * Specifies that a user requires one of many authorities. - * @param authorities the authorities that the user should have at least one - * of (i.e. ROLE_USER, ROLE_ADMIN, etc) - * @return the {@link Builder} for further customizations - */ - public Builder hasAnyAuthority(String... authorities) { - return access(AuthorityAuthorizationManager.hasAnyAuthority(authorities)); - } - - private Builder access(AuthorizationManager manager) { - for (RequestMatcher matcher : this.matchers) { - Builder.this.mappings.add(new RequestMatcherEntry<>(matcher, manager)); - } - return Builder.this; - } - - } - } } diff --git a/web/src/main/java/org/springframework/security/web/authentication/NoOpAuthenticationEntryPoint.java b/web/src/main/java/org/springframework/security/web/authentication/NoOpAuthenticationEntryPoint.java deleted file mode 100644 index 30da537610..0000000000 --- a/web/src/main/java/org/springframework/security/web/authentication/NoOpAuthenticationEntryPoint.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2002-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.web.authentication; - -import java.io.IOException; - -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; - -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; - -/** - * An {@link AuthenticationEntryPoint} implementation that does nothing. - * - * @author Marcus da Coregio - * @since 6.2 - */ -public class NoOpAuthenticationEntryPoint implements AuthenticationEntryPoint { - - @Override - public void commence(HttpServletRequest request, HttpServletResponse response, - AuthenticationException authException) throws IOException, ServletException { - - } - -} diff --git a/web/src/main/java/org/springframework/security/web/authentication/rememberme/TokenBasedRememberMeServices.java b/web/src/main/java/org/springframework/security/web/authentication/rememberme/TokenBasedRememberMeServices.java index f9c72df806..46eada5192 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/rememberme/TokenBasedRememberMeServices.java +++ b/web/src/main/java/org/springframework/security/web/authentication/rememberme/TokenBasedRememberMeServices.java @@ -167,7 +167,7 @@ public class TokenBasedRememberMeServices extends AbstractRememberMeServices { private long getTokenExpiryTime(String[] cookieTokens) { try { - return Long.valueOf(cookieTokens[1]); + return new Long(cookieTokens[1]); } catch (NumberFormatException nfe) { throw new InvalidCookieException( diff --git a/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLoginPageGeneratingFilter.java b/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLoginPageGeneratingFilter.java index b70649a8e3..ce0f4f0056 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLoginPageGeneratingFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLoginPageGeneratingFilter.java @@ -203,7 +203,7 @@ public class DefaultLoginPageGeneratingFilter extends GenericFilterBean { sb.append(" \n"); sb.append(" \n"); + + "rel=\"stylesheet\" crossorigin=\"anonymous\"/>\n"); sb.append(" \n"); sb.append(" \n"); sb.append("

    \n"); diff --git a/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLogoutPageGeneratingFilter.java b/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLogoutPageGeneratingFilter.java index 29f4b3d5d5..40a1ab84fc 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLogoutPageGeneratingFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/ui/DefaultLogoutPageGeneratingFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2018 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. @@ -73,7 +73,7 @@ public class DefaultLogoutPageGeneratingFilter extends OncePerRequestFilter { + "rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" " + "crossorigin=\"anonymous\">\n"); sb.append(" \n"); + + "rel=\"stylesheet\" crossorigin=\"anonymous\"/>\n"); sb.append(" \n"); sb.append(" \n"); sb.append("
    \n"); diff --git a/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java b/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java index 8841937c51..c91099e4bf 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java @@ -28,16 +28,15 @@ import org.springframework.core.log.LogMessage; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.security.web.authentication.NullRememberMeServices; import org.springframework.security.web.authentication.RememberMeServices; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.security.web.context.RequestAttributeSecurityContextRepository; import org.springframework.security.web.context.SecurityContextRepository; import org.springframework.util.Assert; @@ -106,7 +105,7 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter { private String credentialsCharset = "UTF-8"; - private AuthenticationConverter authenticationConverter = new BasicAuthenticationConverter(); + private BasicAuthenticationConverter authenticationConverter = new BasicAuthenticationConverter(); private SecurityContextRepository securityContextRepository = new RequestAttributeSecurityContextRepository(); @@ -150,18 +149,6 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter { this.securityContextRepository = securityContextRepository; } - /** - * Sets the - * {@link org.springframework.security.web.authentication.AuthenticationConverter} to - * use. Defaults to {@link BasicAuthenticationConverter} - * @param authenticationConverter the converter to use - * @since 6.2 - */ - public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { - Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); - this.authenticationConverter = authenticationConverter; - } - @Override public void afterPropertiesSet() { Assert.notNull(this.authenticationManager, "An AuthenticationManager is required"); @@ -174,7 +161,7 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { try { - Authentication authRequest = this.authenticationConverter.convert(request); + UsernamePasswordAuthenticationToken authRequest = this.authenticationConverter.convert(request); if (authRequest == null) { this.logger.trace("Did not process authentication request since failed to find " + "username and password in Basic Authorization header"); @@ -263,19 +250,9 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter { this.securityContextHolderStrategy = securityContextHolderStrategy; } - /** - * Sets the {@link AuthenticationDetailsSource} to use. By default, it is set to use - * the {@link WebAuthenticationDetailsSource}. Note that this configuration applies - * exclusively when the {@link #authenticationConverter} is set to - * {@link BasicAuthenticationConverter}. If you are utilizing a different - * implementation, you will need to manually specify the authentication details on it. - * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} to use. - */ public void setAuthenticationDetailsSource( AuthenticationDetailsSource authenticationDetailsSource) { - if (this.authenticationConverter instanceof BasicAuthenticationConverter basicAuthenticationConverter) { - basicAuthenticationConverter.setAuthenticationDetailsSource(authenticationDetailsSource); - } + this.authenticationConverter.setAuthenticationDetailsSource(authenticationDetailsSource); } public void setRememberMeServices(RememberMeServices rememberMeServices) { @@ -283,20 +260,10 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter { this.rememberMeServices = rememberMeServices; } - /** - * Sets the charset to use when decoding credentials to {@link String}s. By default, - * it is set to {@code UTF-8}. Note that this configuration applies exclusively when - * the {@link #authenticationConverter} is set to - * {@link BasicAuthenticationConverter}. If you are utilizing a different - * implementation, you will need to manually specify the charset on it. - * @param credentialsCharset the charset to use. - */ public void setCredentialsCharset(String credentialsCharset) { Assert.hasText(credentialsCharset, "credentialsCharset cannot be null or empty"); this.credentialsCharset = credentialsCharset; - if (this.authenticationConverter instanceof BasicAuthenticationConverter basicAuthenticationConverter) { - basicAuthenticationConverter.setCredentialsCharset(Charset.forName(credentialsCharset)); - } + this.authenticationConverter.setCredentialsCharset(Charset.forName(credentialsCharset)); } protected String getCredentialsCharset(HttpServletRequest httpRequest) { diff --git a/web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilter.java b/web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilter.java index bf23561280..49242172f9 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilter.java @@ -385,7 +385,7 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes } // Extract expiry time from nonce try { - this.nonceExpiryTime = Long.valueOf(nonceTokens[0]); + this.nonceExpiryTime = new Long(nonceTokens[0]); } catch (NumberFormatException nfe) { throw new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage( diff --git a/web/src/main/java/org/springframework/security/web/csrf/CsrfFilter.java b/web/src/main/java/org/springframework/security/web/csrf/CsrfFilter.java index e6034b6bfb..988f094ea6 100644 --- a/web/src/main/java/org/springframework/security/web/csrf/CsrfFilter.java +++ b/web/src/main/java/org/springframework/security/web/csrf/CsrfFilter.java @@ -51,9 +51,9 @@ import org.springframework.web.filter.OncePerRequestFilter; * *

    * Typically the {@link CsrfTokenRepository} implementation chooses to store the - * {@link CsrfToken} in {@link HttpSession} with {@link HttpSessionCsrfTokenRepository}. - * This is preferred to storing the token in a cookie which can be modified by a client - * application. + * {@link CsrfToken} in {@link HttpSession} with {@link HttpSessionCsrfTokenRepository} + * wrapped by a {@link LazyCsrfTokenRepository}. This is preferred to storing the token in + * a cookie which can be modified by a client application. *

    * * @author Rob Winch @@ -72,7 +72,7 @@ public final class CsrfFilter extends OncePerRequestFilter { /** * The attribute name to use when marking a given request as one that should not be * filtered. - *

    + * * To use, set the attribute on your {@link HttpServletRequest}:

     	 * 	CsrfFilter.skipRequest(request);
     	 * 
    diff --git a/web/src/main/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandler.java b/web/src/main/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandler.java index 8d966331ae..0a1a42d3a3 100644 --- a/web/src/main/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandler.java +++ b/web/src/main/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -97,7 +97,7 @@ public final class XorCsrfTokenRequestAttributeHandler extends CsrfTokenRequestA System.arraycopy(actualBytes, randomBytesSize, xoredCsrf, 0, tokenSize); byte[] csrfBytes = xorCsrf(randomBytes, xoredCsrf); - return (csrfBytes != null) ? Utf8.decode(csrfBytes) : null; + return Utf8.decode(csrfBytes); } private static String createXoredCsrfToken(SecureRandom secureRandom, String token) { @@ -114,9 +114,6 @@ public final class XorCsrfTokenRequestAttributeHandler extends CsrfTokenRequestA } private static byte[] xorCsrf(byte[] randomBytes, byte[] csrfBytes) { - if (csrfBytes.length < randomBytes.length) { - return null; - } int len = Math.min(randomBytes.length, csrfBytes.length); byte[] xoredCsrf = new byte[len]; System.arraycopy(csrfBytes, 0, xoredCsrf, 0, csrfBytes.length); diff --git a/web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java b/web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java index 585ecd503f..9f3cd7ee33 100644 --- a/web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java +++ b/web/src/main/java/org/springframework/security/web/firewall/StrictHttpFirewall.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-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. @@ -713,7 +713,7 @@ public class StrictHttpFirewall implements HttpFirewall { } String value = super.getHeader(name); if (value != null) { - validateAllowedHeaderValue(name, value); + validateAllowedHeaderValue(value); } return value; } @@ -734,7 +734,7 @@ public class StrictHttpFirewall implements HttpFirewall { @Override public String nextElement() { String value = headers.nextElement(); - validateAllowedHeaderValue(name, value); + validateAllowedHeaderValue(value); return value; } @@ -768,7 +768,7 @@ public class StrictHttpFirewall implements HttpFirewall { } String value = super.getParameter(name); if (value != null) { - validateAllowedParameterValue(name, value); + validateAllowedParameterValue(value); } return value; } @@ -781,7 +781,7 @@ public class StrictHttpFirewall implements HttpFirewall { String[] values = entry.getValue(); validateAllowedParameterName(name); for (String value : values) { - validateAllowedParameterValue(name, value); + validateAllowedParameterValue(value); } } return parameterMap; @@ -815,7 +815,7 @@ public class StrictHttpFirewall implements HttpFirewall { String[] values = super.getParameterValues(name); if (values != null) { for (String value : values) { - validateAllowedParameterValue(name, value); + validateAllowedParameterValue(value); } } return values; @@ -828,10 +828,10 @@ public class StrictHttpFirewall implements HttpFirewall { } } - private void validateAllowedHeaderValue(String name, String value) { + private void validateAllowedHeaderValue(String value) { if (!StrictHttpFirewall.this.allowedHeaderValues.test(value)) { - throw new RequestRejectedException("The request was rejected because the header: \"" + name - + " \" has a value \"" + value + "\" that is not allowed."); + throw new RequestRejectedException( + "The request was rejected because the header value \"" + value + "\" is not allowed."); } } @@ -842,10 +842,10 @@ public class StrictHttpFirewall implements HttpFirewall { } } - private void validateAllowedParameterValue(String name, String value) { + private void validateAllowedParameterValue(String value) { if (!StrictHttpFirewall.this.allowedParameterValues.test(value)) { - throw new RequestRejectedException("The request was rejected because the parameter: \"" + name - + " \" has a value \"" + value + "\" that is not allowed."); + throw new RequestRejectedException( + "The request was rejected because the parameter value \"" + value + "\" is not allowed."); } } diff --git a/web/src/main/java/org/springframework/security/web/savedrequest/CookieRequestCache.java b/web/src/main/java/org/springframework/security/web/savedrequest/CookieRequestCache.java index 8f245eb7ea..bd1719b151 100644 --- a/web/src/main/java/org/springframework/security/web/savedrequest/CookieRequestCache.java +++ b/web/src/main/java/org/springframework/security/web/savedrequest/CookieRequestCache.java @@ -128,7 +128,7 @@ public class CookieRequestCache implements RequestCache { private static String getCookiePath(HttpServletRequest request) { String contextPath = request.getContextPath(); - return (StringUtils.hasLength(contextPath)) ? contextPath : "/"; + return (!StringUtils.isEmpty(contextPath)) ? contextPath : "/"; } private boolean matchesSavedRequest(HttpServletRequest request, SavedRequest savedRequest) { diff --git a/web/src/main/java/org/springframework/security/web/savedrequest/SavedRequestAwareWrapper.java b/web/src/main/java/org/springframework/security/web/savedrequest/SavedRequestAwareWrapper.java index 77c5207cd8..200578d595 100644 --- a/web/src/main/java/org/springframework/security/web/savedrequest/SavedRequestAwareWrapper.java +++ b/web/src/main/java/org/springframework/security/web/savedrequest/SavedRequestAwareWrapper.java @@ -33,8 +33,6 @@ import jakarta.servlet.http.HttpServletRequestWrapper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.http.HttpHeaders; - /** * Provides request parameters, headers and cookies from either an original request or a * saved request. @@ -59,7 +57,10 @@ class SavedRequestAwareWrapper extends HttpServletRequestWrapper { protected static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT"); - protected SavedRequest savedRequest; + /** The default Locale if none are specified. */ + protected static Locale defaultLocale = Locale.getDefault(); + + protected SavedRequest savedRequest = null; /** * The set of SimpleDateFormat formats to use in getDateHeader(). Notice that because @@ -100,12 +101,14 @@ class SavedRequestAwareWrapper extends HttpServletRequestWrapper { } @Override - public Enumeration getHeaderNames() { + @SuppressWarnings("unchecked") + public Enumeration getHeaderNames() { return new Enumerator<>(this.savedRequest.getHeaderNames()); } @Override - public Enumeration getHeaders(String name) { + @SuppressWarnings("unchecked") + public Enumeration getHeaders(String name) { return new Enumerator<>(this.savedRequest.getHeaderValues(name)); } @@ -122,7 +125,8 @@ class SavedRequestAwareWrapper extends HttpServletRequestWrapper { } @Override - public Enumeration getLocales() { + @SuppressWarnings("unchecked") + public Enumeration getLocales() { List locales = this.savedRequest.getLocales(); if (locales.isEmpty()) { // Fall back to default locale @@ -137,11 +141,6 @@ class SavedRequestAwareWrapper extends HttpServletRequestWrapper { return this.savedRequest.getMethod(); } - @Override - public String getContentType() { - return getHeader(HttpHeaders.CONTENT_TYPE); - } - /** * If the parameter is available from the wrapped request then the request has been * forwarded/included to a URL with parameters, either supplementing or overriding the @@ -166,7 +165,8 @@ class SavedRequestAwareWrapper extends HttpServletRequestWrapper { } @Override - public Map getParameterMap() { + @SuppressWarnings("unchecked") + public Map getParameterMap() { Set names = getCombinedParameterNames(); Map parameterMap = new HashMap<>(names.size()); for (String name : names) { @@ -175,6 +175,7 @@ class SavedRequestAwareWrapper extends HttpServletRequestWrapper { return parameterMap; } + @SuppressWarnings("unchecked") private Set getCombinedParameterNames() { Set names = new HashSet<>(); names.addAll(super.getParameterMap().keySet()); @@ -183,8 +184,9 @@ class SavedRequestAwareWrapper extends HttpServletRequestWrapper { } @Override - public Enumeration getParameterNames() { - return new Enumerator<>(getCombinedParameterNames()); + @SuppressWarnings("unchecked") + public Enumeration getParameterNames() { + return new Enumerator(getCombinedParameterNames()); } @Override diff --git a/web/src/main/java/org/springframework/security/web/savedrequest/SimpleSavedRequest.java b/web/src/main/java/org/springframework/security/web/savedrequest/SimpleSavedRequest.java index 08165eb0cd..e1595ea6bd 100644 --- a/web/src/main/java/org/springframework/security/web/savedrequest/SimpleSavedRequest.java +++ b/web/src/main/java/org/springframework/security/web/savedrequest/SimpleSavedRequest.java @@ -126,7 +126,7 @@ public class SimpleSavedRequest implements SavedRequest { } public void setLocales(List locales) { - Assert.notNull(locales, "locales cannot be null"); + Assert.notNull("locales cannot be null"); this.locales = locales; } diff --git a/web/src/main/java/org/springframework/security/web/server/ServerFormLoginAuthenticationConverter.java b/web/src/main/java/org/springframework/security/web/server/ServerFormLoginAuthenticationConverter.java index a851af1df2..01fa28c6b0 100644 --- a/web/src/main/java/org/springframework/security/web/server/ServerFormLoginAuthenticationConverter.java +++ b/web/src/main/java/org/springframework/security/web/server/ServerFormLoginAuthenticationConverter.java @@ -46,7 +46,7 @@ public class ServerFormLoginAuthenticationConverter implements Function apply(ServerWebExchange exchange) { - return exchange.getFormData().map(this::createAuthentication); + return exchange.getFormData().map((data) -> createAuthentication(data)); } private UsernamePasswordAuthenticationToken createAuthentication(MultiValueMap data) { diff --git a/web/src/main/java/org/springframework/security/web/server/authentication/SwitchUserWebFilter.java b/web/src/main/java/org/springframework/security/web/server/authentication/SwitchUserWebFilter.java index cfc93a301a..88e8511af6 100644 --- a/web/src/main/java/org/springframework/security/web/server/authentication/SwitchUserWebFilter.java +++ b/web/src/main/java/org/springframework/security/web/server/authentication/SwitchUserWebFilter.java @@ -280,7 +280,8 @@ public class SwitchUserWebFilter implements WebFilter { private Optional extractSourceAuthentication(Authentication currentAuthentication) { // iterate over granted authorities and find the 'switch user' authority for (GrantedAuthority authority : currentAuthentication.getAuthorities()) { - if (authority instanceof SwitchUserGrantedAuthority switchAuthority) { + if (authority instanceof SwitchUserGrantedAuthority) { + SwitchUserGrantedAuthority switchAuthority = (SwitchUserGrantedAuthority) authority; return Optional.of(switchAuthority.getSource()); } } diff --git a/web/src/main/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandler.java b/web/src/main/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandler.java index de59167e8c..d361104ecb 100644 --- a/web/src/main/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandler.java +++ b/web/src/main/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -90,7 +90,7 @@ public final class XorServerCsrfTokenRequestAttributeHandler extends ServerCsrfT System.arraycopy(actualBytes, randomBytesSize, xoredCsrf, 0, tokenSize); byte[] csrfBytes = xorCsrf(randomBytes, xoredCsrf); - return (csrfBytes != null) ? Utf8.decode(csrfBytes) : null; + return Utf8.decode(csrfBytes); } private static String createXoredCsrfToken(SecureRandom secureRandom, String token) { @@ -107,9 +107,6 @@ public final class XorServerCsrfTokenRequestAttributeHandler extends ServerCsrfT } private static byte[] xorCsrf(byte[] randomBytes, byte[] csrfBytes) { - if (csrfBytes.length < randomBytes.length) { - return null; - } int len = Math.min(randomBytes.length, csrfBytes.length); byte[] xoredCsrf = new byte[len]; System.arraycopy(csrfBytes, 0, xoredCsrf, 0, csrfBytes.length); diff --git a/web/src/main/java/org/springframework/security/web/server/ui/LoginPageGeneratingWebFilter.java b/web/src/main/java/org/springframework/security/web/server/ui/LoginPageGeneratingWebFilter.java index 2b85e89732..807f96177f 100644 --- a/web/src/main/java/org/springframework/security/web/server/ui/LoginPageGeneratingWebFilter.java +++ b/web/src/main/java/org/springframework/security/web/server/ui/LoginPageGeneratingWebFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2017 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. @@ -101,7 +101,7 @@ public class LoginPageGeneratingWebFilter implements WebFilter { + "rel=\"stylesheet\" integrity=\"sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M\" " + "crossorigin=\"anonymous\">\n"); page.append(" \n"); + + "rel=\"stylesheet\" crossorigin=\"anonymous\"/>\n"); page.append(" \n"); page.append(" \n"); page.append("
    \n"); diff --git a/web/src/main/java/org/springframework/security/web/server/ui/LogoutPageGeneratingWebFilter.java b/web/src/main/java/org/springframework/security/web/server/ui/LogoutPageGeneratingWebFilter.java index b8ad98cd29..b0df82e849 100644 --- a/web/src/main/java/org/springframework/security/web/server/ui/LogoutPageGeneratingWebFilter.java +++ b/web/src/main/java/org/springframework/security/web/server/ui/LogoutPageGeneratingWebFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -81,7 +81,7 @@ public class LogoutPageGeneratingWebFilter implements WebFilter { page.append(" \n"); page.append(" \n"); + + "rel=\"stylesheet\" crossorigin=\"anonymous\"/>\n"); page.append(" \n"); page.append(" \n"); page.append("
    \n"); diff --git a/web/src/main/java/org/springframework/security/web/session/RequestedUrlRedirectInvalidSessionStrategy.java b/web/src/main/java/org/springframework/security/web/session/RequestedUrlRedirectInvalidSessionStrategy.java index da2d8cf297..2720e72937 100644 --- a/web/src/main/java/org/springframework/security/web/session/RequestedUrlRedirectInvalidSessionStrategy.java +++ b/web/src/main/java/org/springframework/security/web/session/RequestedUrlRedirectInvalidSessionStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2016 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,7 +25,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.security.web.DefaultRedirectStrategy; import org.springframework.security.web.RedirectStrategy; -import org.springframework.util.Assert; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; /** @@ -33,13 +32,12 @@ import org.springframework.web.servlet.support.ServletUriComponentsBuilder; * detected by the {@code SessionManagementFilter}. * * @author Craig Andrews - * @author Mark Chesney */ public final class RequestedUrlRedirectInvalidSessionStrategy implements InvalidSessionStrategy { private final Log logger = LogFactory.getLog(getClass()); - private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); + private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); private boolean createNewSession = true; @@ -70,14 +68,4 @@ public final class RequestedUrlRedirectInvalidSessionStrategy implements Invalid this.createNewSession = createNewSession; } - /** - * Sets the redirect strategy to use. The default is {@link DefaultRedirectStrategy}. - * @param redirectStrategy the redirect strategy to use. - * @since 6.2 - */ - public void setRedirectStrategy(RedirectStrategy redirectStrategy) { - Assert.notNull(redirectStrategy, "redirectStrategy cannot be null"); - this.redirectStrategy = redirectStrategy; - } - } diff --git a/web/src/main/java/org/springframework/security/web/util/ThrowableAnalyzer.java b/web/src/main/java/org/springframework/security/web/util/ThrowableAnalyzer.java index 6084434539..2fac52493c 100755 --- a/web/src/main/java/org/springframework/security/web/util/ThrowableAnalyzer.java +++ b/web/src/main/java/org/springframework/security/web/util/ThrowableAnalyzer.java @@ -41,7 +41,7 @@ public class ThrowableAnalyzer { * * @see Throwable#getCause() */ - public static final ThrowableCauseExtractor DEFAULT_EXTRACTOR = Throwable::getCause; + public static final ThrowableCauseExtractor DEFAULT_EXTRACTOR = (throwable) -> throwable.getCause(); /** * Default extractor for {@link InvocationTargetException} instances. diff --git a/web/src/main/java/org/springframework/security/web/util/matcher/AntPathRequestMatcher.java b/web/src/main/java/org/springframework/security/web/util/matcher/AntPathRequestMatcher.java index da9874b4d9..21b6140f70 100644 --- a/web/src/main/java/org/springframework/security/web/util/matcher/AntPathRequestMatcher.java +++ b/web/src/main/java/org/springframework/security/web/util/matcher/AntPathRequestMatcher.java @@ -226,9 +226,10 @@ public final class AntPathRequestMatcher implements RequestMatcher, RequestVaria @Override public boolean equals(Object obj) { - if (!(obj instanceof AntPathRequestMatcher other)) { + if (!(obj instanceof AntPathRequestMatcher)) { return false; } + AntPathRequestMatcher other = (AntPathRequestMatcher) obj; return this.pattern.equals(other.pattern) && this.httpMethod == other.httpMethod && this.caseSensitive == other.caseSensitive; } diff --git a/web/src/test/java/org/springframework/security/web/DefaultRedirectStrategyTests.java b/web/src/test/java/org/springframework/security/web/DefaultRedirectStrategyTests.java index ca8adbcd6e..098309fd97 100644 --- a/web/src/test/java/org/springframework/security/web/DefaultRedirectStrategyTests.java +++ b/web/src/test/java/org/springframework/security/web/DefaultRedirectStrategyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2016 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,7 +18,6 @@ package org.springframework.security.web; import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; @@ -27,7 +26,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException /** * @author Luke Taylor - * @author Mark Chesney * @since 3.0 */ public class DefaultRedirectStrategyTests { @@ -66,21 +64,4 @@ public class DefaultRedirectStrategyTests { .isThrownBy(() -> rds.sendRedirect(request, response, "https://redirectme.somewhere.else")); } - @Test - public void statusCodeIsHandledCorrectly() throws Exception { - // given - DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); - redirectStrategy.setStatusCode(HttpStatus.TEMPORARY_REDIRECT); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - - // when - redirectStrategy.sendRedirect(request, response, "/requested"); - - // then - assertThat(response.isCommitted()).isTrue(); - assertThat(response.getRedirectedUrl()).isEqualTo("/requested"); - assertThat(response.getStatus()).isEqualTo(307); - } - } diff --git a/web/src/test/java/org/springframework/security/web/access/NoOpAccessDeniedHandlerTests.java b/web/src/test/java/org/springframework/security/web/access/NoOpAccessDeniedHandlerTests.java deleted file mode 100644 index 8dab8dfb36..0000000000 --- a/web/src/test/java/org/springframework/security/web/access/NoOpAccessDeniedHandlerTests.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.web.access; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.junit.jupiter.api.Test; - -import org.springframework.security.access.AccessDeniedException; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyNoInteractions; - -class NoOpAccessDeniedHandlerTests { - - private final NoOpAccessDeniedHandler handler = new NoOpAccessDeniedHandler(); - - @Test - void handleWhenInvokedThenDoesNothing() throws Exception { - HttpServletRequest request = mock(HttpServletRequest.class); - HttpServletResponse response = mock(HttpServletResponse.class); - AccessDeniedException exception = mock(AccessDeniedException.class); - this.handler.handle(request, response, exception); - verifyNoInteractions(request, response, exception); - } - -} diff --git a/web/src/test/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManagerTests.java b/web/src/test/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManagerTests.java index aa969ce2bb..7914f4f78f 100644 --- a/web/src/test/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManagerTests.java +++ b/web/src/test/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,20 +21,16 @@ import java.util.function.Supplier; import org.junit.jupiter.api.Test; import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.security.authentication.TestAuthentication; import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.authorization.AuthenticatedAuthorizationManager; import org.springframework.security.authorization.AuthorityAuthorizationManager; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.core.Authentication; import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AnyRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcherEntry; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.Assertions.assertThatIllegalStateException; /** * Tests for {@link RequestMatcherDelegatingAuthorizationManager}. @@ -128,280 +124,4 @@ public class RequestMatcherDelegatingAuthorizationManagerTests { .withMessage("mappingsConsumer cannot be null"); } - @Test - public void mappingsWhenConfiguredAfterAnyRequestThenException() { - assertThatIllegalStateException() - .isThrownBy(() -> RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .authenticated() - .mappings((m) -> m.add(new RequestMatcherEntry<>(AnyRequestMatcher.INSTANCE, - AuthenticatedAuthorizationManager.authenticated())))) - .withMessage("Can't configure mappings after anyRequest"); - } - - @Test - public void addWhenConfiguredAfterAnyRequestThenException() { - assertThatIllegalStateException() - .isThrownBy(() -> RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .authenticated() - .add(AnyRequestMatcher.INSTANCE, AuthenticatedAuthorizationManager.authenticated())) - .withMessage("Can't add mappings after anyRequest"); - } - - @Test - public void requestMatchersWhenConfiguredAfterAnyRequestThenException() { - assertThatIllegalStateException() - .isThrownBy(() -> RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .authenticated() - .requestMatchers(new AntPathRequestMatcher("/authenticated")) - .authenticated() - .build()) - .withMessage("Can't configure requestMatchers after anyRequest"); - } - - @Test - public void anyRequestWhenConfiguredAfterAnyRequestThenException() { - assertThatIllegalStateException() - .isThrownBy(() -> RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .authenticated() - .anyRequest() - .authenticated() - .build()) - .withMessage("Can't configure anyRequest after itself"); - } - - @Test - public void anyRequestWhenPermitAllThenGrantedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .permitAll() - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::anonymousUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isTrue(); - } - - @Test - public void anyRequestWhenDenyAllThenDeniedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .denyAll() - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isFalse(); - } - - @Test - public void authenticatedWhenAuthenticatedUserThenGrantedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .authenticated() - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isTrue(); - } - - @Test - public void authenticatedWhenAnonymousUserThenDeniedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .authenticated() - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::anonymousUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isFalse(); - } - - @Test - public void fullyAuthenticatedWhenAuthenticatedUserThenGrantedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .fullyAuthenticated() - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isTrue(); - } - - @Test - public void fullyAuthenticatedWhenAnonymousUserThenDeniedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .fullyAuthenticated() - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::anonymousUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isFalse(); - } - - @Test - public void fullyAuthenticatedWhenRememberMeUserThenDeniedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .fullyAuthenticated() - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::rememberMeUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isFalse(); - } - - @Test - public void rememberMeWhenRememberMeUserThenGrantedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .rememberMe() - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::rememberMeUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isTrue(); - } - - @Test - public void rememberMeWhenAuthenticatedUserThenDeniedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .rememberMe() - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isFalse(); - } - - @Test - public void anonymousWhenAnonymousUserThenGrantedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .anonymous() - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::anonymousUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isTrue(); - } - - @Test - public void anonymousWhenAuthenticatedUserThenDeniedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .anonymous() - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isFalse(); - } - - @Test - public void hasRoleAdminWhenAuthenticatedUserThenDeniedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .hasRole("ADMIN") - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isFalse(); - } - - @Test - public void hasRoleAdminWhenAuthenticatedAdminThenGrantedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .hasRole("ADMIN") - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isTrue(); - } - - @Test - public void hasAnyRoleUserOrAdminWhenAuthenticatedUserThenGrantedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .hasAnyRole("USER", "ADMIN") - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isTrue(); - } - - @Test - public void hasAnyRoleUserOrAdminWhenAuthenticatedAdminThenGrantedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .hasAnyRole("USER", "ADMIN") - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isTrue(); - } - - @Test - public void hasAnyRoleUserOrAdminWhenAnonymousUserThenDeniedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .hasAnyRole("USER", "ADMIN") - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::anonymousUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isFalse(); - } - - @Test - public void hasAuthorityRoleAdminWhenAuthenticatedUserThenDeniedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .hasAuthority("ROLE_ADMIN") - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isFalse(); - } - - @Test - public void hasAuthorityRoleAdminWhenAuthenticatedAdminThenGrantedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .hasAuthority("ROLE_ADMIN") - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isTrue(); - } - - @Test - public void hasAnyAuthorityRoleUserOrAdminWhenAuthenticatedUserThenGrantedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .hasAnyAuthority("ROLE_USER", "ROLE_ADMIN") - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isTrue(); - } - - @Test - public void hasAnyAuthorityRoleUserOrAdminWhenAuthenticatedAdminThenGrantedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .hasAnyAuthority("ROLE_USER", "ROLE_ADMIN") - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedAdmin, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isTrue(); - } - - @Test - public void hasAnyAuthorityRoleUserOrAdminWhenAnonymousUserThenDeniedDecision() { - RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder() - .anyRequest() - .hasAnyRole("USER", "ADMIN") - .build(); - AuthorizationDecision decision = manager.check(TestAuthentication::anonymousUser, null); - assertThat(decision).isNotNull(); - assertThat(decision.isGranted()).isFalse(); - } - } diff --git a/web/src/test/java/org/springframework/security/web/authentication/NoOpAuthenticationEntryPointTests.java b/web/src/test/java/org/springframework/security/web/authentication/NoOpAuthenticationEntryPointTests.java deleted file mode 100644 index a687f2bd1b..0000000000 --- a/web/src/test/java/org/springframework/security/web/authentication/NoOpAuthenticationEntryPointTests.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2002-2023 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.security.web.authentication; - -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.junit.jupiter.api.Test; - -import org.springframework.security.core.AuthenticationException; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyNoInteractions; - -class NoOpAuthenticationEntryPointTests { - - private final NoOpAuthenticationEntryPoint authenticationEntryPoint = new NoOpAuthenticationEntryPoint(); - - @Test - void commenceWhenInvokedThenDoesNothing() throws Exception { - HttpServletRequest request = mock(HttpServletRequest.class); - HttpServletResponse response = mock(HttpServletResponse.class); - AuthenticationException exception = mock(AuthenticationException.class); - this.authenticationEntryPoint.commence(request, response, exception); - verifyNoInteractions(request, response, exception); - } - -} diff --git a/web/src/test/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedAuthenticationTokenTests.java b/web/src/test/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedAuthenticationTokenTests.java index e9eb80cc37..8007b4c4c5 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedAuthenticationTokenTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/preauth/PreAuthenticatedAuthenticationTokenTests.java @@ -42,7 +42,7 @@ public class PreAuthenticatedAuthenticationTokenTests { assertThat(token.getPrincipal()).isEqualTo(principal); assertThat(token.getCredentials()).isEqualTo(credentials); assertThat(token.getDetails()).isEqualTo(details); - assertThat(token.getAuthorities()).isEmpty(); + assertThat(token.getAuthorities().isEmpty()).isTrue(); } @Test @@ -53,7 +53,7 @@ public class PreAuthenticatedAuthenticationTokenTests { assertThat(token.getPrincipal()).isEqualTo(principal); assertThat(token.getCredentials()).isEqualTo(credentials); assertThat(token.getDetails()).isNull(); - assertThat(token.getAuthorities()).isEmpty(); + assertThat(token.getAuthorities().isEmpty()).isTrue(); } @Test diff --git a/web/src/test/java/org/springframework/security/web/authentication/rememberme/JdbcTokenRepositoryImplTests.java b/web/src/test/java/org/springframework/security/web/authentication/rememberme/JdbcTokenRepositoryImplTests.java index e95e628d1c..2d93da83d0 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/rememberme/JdbcTokenRepositoryImplTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/rememberme/JdbcTokenRepositoryImplTests.java @@ -95,10 +95,10 @@ public class JdbcTokenRepositoryImplTests { PersistentRememberMeToken token = new PersistentRememberMeToken("joeuser", "joesseries", "atoken", currentDate); this.repo.createNewToken(token); Map results = this.template.queryForMap("select * from persistent_logins"); - assertThat(results).containsEntry("last_used", currentDate); - assertThat(results).containsEntry("username", "joeuser"); - assertThat(results).containsEntry("series", "joesseries"); - assertThat(results).containsEntry("token", "atoken"); + assertThat(results.get("last_used")).isEqualTo(currentDate); + assertThat(results.get("username")).isEqualTo("joeuser"); + assertThat(results.get("series")).isEqualTo("joesseries"); + assertThat(results.get("token")).isEqualTo("atoken"); } @Test @@ -157,9 +157,9 @@ public class JdbcTokenRepositoryImplTests { this.repo.updateToken("joesseries", "newtoken", new Date()); Map results = this.template .queryForMap("select * from persistent_logins where series = 'joesseries'"); - assertThat(results).containsEntry("username", "joeuser"); - assertThat(results).containsEntry("series", "joesseries"); - assertThat(results).containsEntry("token", "newtoken"); + assertThat(results.get("username")).isEqualTo("joeuser"); + assertThat(results.get("series")).isEqualTo("joesseries"); + assertThat(results.get("token")).isEqualTo("newtoken"); Date lastUsed = (Date) results.get("last_used"); assertThat(lastUsed.getTime() > ts.getTime()).isTrue(); } diff --git a/web/src/test/java/org/springframework/security/web/authentication/rememberme/PersistentTokenBasedRememberMeServicesTests.java b/web/src/test/java/org/springframework/security/web/authentication/rememberme/PersistentTokenBasedRememberMeServicesTests.java index fbe3e245d8..adc216fb3e 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/rememberme/PersistentTokenBasedRememberMeServicesTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/rememberme/PersistentTokenBasedRememberMeServicesTests.java @@ -93,7 +93,7 @@ public class PersistentTokenBasedRememberMeServicesTests { this.services.processAutoLoginCookie(new String[] { "series", "token" }, new MockHttpServletRequest(), response); assertThat(this.repo.getStoredToken().getSeries()).isEqualTo("series"); - assertThat(this.repo.getStoredToken().getTokenValue()).hasSize(16); + assertThat(this.repo.getStoredToken().getTokenValue().length()).isEqualTo(16); String[] cookie = this.services.decodeCookie(response.getCookie("mycookiename").getValue()); assertThat(cookie[0]).isEqualTo("series"); assertThat(cookie[1]).isEqualTo(this.repo.getStoredToken().getTokenValue()); @@ -108,8 +108,8 @@ public class PersistentTokenBasedRememberMeServicesTests { MockHttpServletResponse response = new MockHttpServletResponse(); this.services.loginSuccess(new MockHttpServletRequest(), response, UsernamePasswordAuthenticationToken.unauthenticated("joe", "password")); - assertThat(this.repo.getStoredToken().getSeries()).hasSize(16); - assertThat(this.repo.getStoredToken().getTokenValue()).hasSize(16); + assertThat(this.repo.getStoredToken().getSeries().length()).isEqualTo(16); + assertThat(this.repo.getStoredToken().getTokenValue().length()).isEqualTo(16); String[] cookie = this.services.decodeCookie(response.getCookie("mycookiename").getValue()); assertThat(cookie[0]).isEqualTo(this.repo.getStoredToken().getSeries()); assertThat(cookie[1]).isEqualTo(this.repo.getStoredToken().getTokenValue()); diff --git a/web/src/test/java/org/springframework/security/web/authentication/ui/DefaultLogoutPageGeneratingFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/ui/DefaultLogoutPageGeneratingFilterTests.java index 00885411a2..91f4403d90 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/ui/DefaultLogoutPageGeneratingFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/ui/DefaultLogoutPageGeneratingFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,7 +45,7 @@ public class DefaultLogoutPageGeneratingFilterTests { + " \n" + " \n" + " Confirm Log Out?\n" + " \n" - + " \n" + + " \n" + " \n" + " \n" + "
    \n" + "
    \n" + " \n" diff --git a/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java index faf9f17db4..134a5d46f3 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java @@ -21,7 +21,6 @@ import java.nio.charset.StandardCharsets; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; -import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -42,12 +41,9 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.test.web.CodecTestUtils; -import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.security.web.authentication.WebAuthenticationDetails; import org.springframework.security.web.context.RequestAttributeSecurityContextRepository; import org.springframework.security.web.context.SecurityContextRepository; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.web.util.WebUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -492,57 +488,4 @@ public class BasicAuthenticationFilterTests { assertThat(authenticationRequest.getName()).isEqualTo("rod"); } - @Test - public void doFilterWhenCustomAuthenticationConverterThatIgnoresRequestThenIgnores() throws Exception { - this.filter.setAuthenticationConverter(new TestAuthenticationConverter()); - String token = "rod:koala"; - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Authorization", "Basic " + CodecTestUtils.encodeBase64(token)); - request.setServletPath("/ignored"); - FilterChain filterChain = mock(FilterChain.class); - MockHttpServletResponse response = new MockHttpServletResponse(); - this.filter.doFilter(request, response, filterChain); - assertThat(response.getStatus()).isEqualTo(200); - - verify(this.manager, never()).authenticate(any(Authentication.class)); - verify(filterChain).doFilter(any(ServletRequest.class), any(ServletResponse.class)); - verifyNoMoreInteractions(this.manager, filterChain); - } - - @Test - public void doFilterWhenCustomAuthenticationConverterRequestThenAuthenticate() throws Exception { - this.filter.setAuthenticationConverter(new TestAuthenticationConverter()); - String token = "rod:koala"; - MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Authorization", "Basic " + CodecTestUtils.encodeBase64(token)); - request.setServletPath("/ok"); - FilterChain filterChain = mock(FilterChain.class); - MockHttpServletResponse response = new MockHttpServletResponse(); - this.filter.doFilter(request, response, filterChain); - assertThat(response.getStatus()).isEqualTo(200); - assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull(); - assertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo("rod"); - } - - @Test - public void setAuthenticationConverterWhenNullThenException() { - assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationConverter(null)); - } - - static class TestAuthenticationConverter implements AuthenticationConverter { - - private final RequestMatcher matcher = AntPathRequestMatcher.antMatcher("/ignored"); - - private final BasicAuthenticationConverter delegate = new BasicAuthenticationConverter(); - - @Override - public Authentication convert(HttpServletRequest request) { - if (this.matcher.matches(request)) { - return null; - } - return this.delegate.convert(request); - } - - } - } diff --git a/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthUtilsTests.java b/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthUtilsTests.java index 9b9387a15e..9b5345765f 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthUtilsTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthUtilsTests.java @@ -38,16 +38,16 @@ public class DigestAuthUtilsTests { String unsplit = "username=\"rod\", invalidEntryThatHasNoEqualsSign, realm=\"Contacts Realm\", nonce=\"MTEwOTAyMzU1MTQ4NDo1YzY3OWViYWM5NDNmZWUwM2UwY2NmMDBiNDQzMTQ0OQ==\", uri=\"/spring-security-sample-contacts-filter/secure/adminPermission.htm?contactId=4\", response=\"38644211cf9ac3da63ab639807e2baff\", qop=auth, nc=00000004, cnonce=\"2b8d329a8571b99a\""; String[] headerEntries = StringUtils.commaDelimitedListToStringArray(unsplit); Map headerMap = DigestAuthUtils.splitEachArrayElementAndCreateMap(headerEntries, "=", "\""); - assertThat(headerMap).containsEntry("username", "rod"); - assertThat(headerMap).containsEntry("realm", "Contacts Realm"); - assertThat(headerMap).containsEntry("nonce", - "MTEwOTAyMzU1MTQ4NDo1YzY3OWViYWM5NDNmZWUwM2UwY2NmMDBiNDQzMTQ0OQ=="); - assertThat(headerMap).containsEntry("uri", - "/spring-security-sample-contacts-filter/secure/adminPermission.htm?contactId=4"); - assertThat(headerMap).containsEntry("response", "38644211cf9ac3da63ab639807e2baff"); - assertThat(headerMap).containsEntry("qop", "auth"); - assertThat(headerMap).containsEntry("nc", "00000004"); - assertThat(headerMap).containsEntry("cnonce", "2b8d329a8571b99a"); + assertThat(headerMap.get("username")).isEqualTo("rod"); + assertThat(headerMap.get("realm")).isEqualTo("Contacts Realm"); + assertThat(headerMap.get("nonce")) + .isEqualTo("MTEwOTAyMzU1MTQ4NDo1YzY3OWViYWM5NDNmZWUwM2UwY2NmMDBiNDQzMTQ0OQ=="); + assertThat(headerMap.get("uri")) + .isEqualTo("/spring-security-sample-contacts-filter/secure/adminPermission.htm?contactId=4"); + assertThat(headerMap.get("response")).isEqualTo("38644211cf9ac3da63ab639807e2baff"); + assertThat(headerMap.get("qop")).isEqualTo("auth"); + assertThat(headerMap.get("nc")).isEqualTo("00000004"); + assertThat(headerMap.get("cnonce")).isEqualTo("2b8d329a8571b99a"); assertThat(headerMap).hasSize(8); } @@ -56,16 +56,16 @@ public class DigestAuthUtilsTests { String unsplit = "username=\"rod\", realm=\"Contacts Realm\", nonce=\"MTEwOTAyMzU1MTQ4NDo1YzY3OWViYWM5NDNmZWUwM2UwY2NmMDBiNDQzMTQ0OQ==\", uri=\"/spring-security-sample-contacts-filter/secure/adminPermission.htm?contactId=4\", response=\"38644211cf9ac3da63ab639807e2baff\", qop=auth, nc=00000004, cnonce=\"2b8d329a8571b99a\""; String[] headerEntries = StringUtils.commaDelimitedListToStringArray(unsplit); Map headerMap = DigestAuthUtils.splitEachArrayElementAndCreateMap(headerEntries, "=", null); - assertThat(headerMap).containsEntry("username", "\"rod\""); - assertThat(headerMap).containsEntry("realm", "\"Contacts Realm\""); - assertThat(headerMap).containsEntry("nonce", - "\"MTEwOTAyMzU1MTQ4NDo1YzY3OWViYWM5NDNmZWUwM2UwY2NmMDBiNDQzMTQ0OQ==\""); - assertThat(headerMap).containsEntry("uri", - "\"/spring-security-sample-contacts-filter/secure/adminPermission.htm?contactId=4\""); - assertThat(headerMap).containsEntry("response", "\"38644211cf9ac3da63ab639807e2baff\""); - assertThat(headerMap).containsEntry("qop", "auth"); - assertThat(headerMap).containsEntry("nc", "00000004"); - assertThat(headerMap).containsEntry("cnonce", "\"2b8d329a8571b99a\""); + assertThat(headerMap.get("username")).isEqualTo("\"rod\""); + assertThat(headerMap.get("realm")).isEqualTo("\"Contacts Realm\""); + assertThat(headerMap.get("nonce")) + .isEqualTo("\"MTEwOTAyMzU1MTQ4NDo1YzY3OWViYWM5NDNmZWUwM2UwY2NmMDBiNDQzMTQ0OQ==\""); + assertThat(headerMap.get("uri")) + .isEqualTo("\"/spring-security-sample-contacts-filter/secure/adminPermission.htm?contactId=4\""); + assertThat(headerMap.get("response")).isEqualTo("\"38644211cf9ac3da63ab639807e2baff\""); + assertThat(headerMap.get("qop")).isEqualTo("auth"); + assertThat(headerMap.get("nc")).isEqualTo("00000004"); + assertThat(headerMap.get("cnonce")).isEqualTo("\"2b8d329a8571b99a\""); assertThat(headerMap).hasSize(8); } diff --git a/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthenticationEntryPointTests.java b/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthenticationEntryPointTests.java index 761aeed07c..63c74dfd95 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthenticationEntryPointTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthenticationEntryPointTests.java @@ -93,8 +93,8 @@ public class DigestAuthenticationEntryPointTests { String header = response.getHeader("WWW-Authenticate").toString().substring(7); String[] headerEntries = StringUtils.commaDelimitedListToStringArray(header); Map headerMap = DigestAuthUtils.splitEachArrayElementAndCreateMap(headerEntries, "=", "\""); - assertThat(headerMap).containsEntry("realm", "hello"); - assertThat(headerMap).containsEntry("qop", "auth"); + assertThat(headerMap.get("realm")).isEqualTo("hello"); + assertThat(headerMap.get("qop")).isEqualTo("auth"); assertThat(headerMap.get("stale")).isNull(); checkNonceValid(headerMap.get("nonce")); } @@ -116,9 +116,9 @@ public class DigestAuthenticationEntryPointTests { String header = response.getHeader("WWW-Authenticate").toString().substring(7); String[] headerEntries = StringUtils.commaDelimitedListToStringArray(header); Map headerMap = DigestAuthUtils.splitEachArrayElementAndCreateMap(headerEntries, "=", "\""); - assertThat(headerMap).containsEntry("realm", "hello"); - assertThat(headerMap).containsEntry("qop", "auth"); - assertThat(headerMap).containsEntry("stale", "true"); + assertThat(headerMap.get("realm")).isEqualTo("hello"); + assertThat(headerMap.get("qop")).isEqualTo("auth"); + assertThat(headerMap.get("stale")).isEqualTo("true"); checkNonceValid(headerMap.get("nonce")); } diff --git a/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilterTests.java index 230c554fcd..30e72ea4c1 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilterTests.java @@ -149,7 +149,7 @@ public class DigestAuthenticationFilterTests { String header = response.getHeader("WWW-Authenticate").toString().substring(7); String[] headerEntries = StringUtils.commaDelimitedListToStringArray(header); Map headerMap = DigestAuthUtils.splitEachArrayElementAndCreateMap(headerEntries, "=", "\""); - assertThat(headerMap).containsEntry("stale", "true"); + assertThat(headerMap.get("stale")).isEqualTo("true"); } @Test diff --git a/web/src/test/java/org/springframework/security/web/context/AbstractSecurityWebApplicationInitializerTests.java b/web/src/test/java/org/springframework/security/web/context/AbstractSecurityWebApplicationInitializerTests.java index 103716d119..16c3354394 100644 --- a/web/src/test/java/org/springframework/security/web/context/AbstractSecurityWebApplicationInitializerTests.java +++ b/web/src/test/java/org/springframework/security/web/context/AbstractSecurityWebApplicationInitializerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ package org.springframework.security.web.context; import java.util.Collections; import java.util.EnumSet; import java.util.EventListener; +import java.util.HashSet; import java.util.Set; import jakarta.servlet.DispatcherType; @@ -318,15 +319,14 @@ public class AbstractSecurityWebApplicationInitializerTests { ServletContext context = mock(ServletContext.class); FilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class); ArgumentCaptor proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class); - given(context.addFilter(eq("springSecurityFilterChain"), any(DelegatingFilterProxy.class))) - .willReturn(registration); - @SuppressWarnings("unchecked") - ArgumentCaptor> modesCaptor = ArgumentCaptor.forClass(Set.class); + given(context.addFilter(eq("springSecurityFilterChain"), proxyCaptor.capture())).willReturn(registration); + ArgumentCaptor> modesCaptor = ArgumentCaptor + .forClass(new HashSet() { + }.getClass()); + willDoNothing().given(context).setSessionTrackingModes(modesCaptor.capture()); new AbstractSecurityWebApplicationInitializer() { }.onStartup(context); - verify(context).addFilter(eq("springSecurityFilterChain"), proxyCaptor.capture()); assertProxyDefaults(proxyCaptor.getValue()); - verify(context).setSessionTrackingModes(modesCaptor.capture()); Set modes = modesCaptor.getValue(); assertThat(modes).hasSize(1); assertThat(modes).containsExactly(SessionTrackingMode.COOKIE); @@ -337,20 +337,18 @@ public class AbstractSecurityWebApplicationInitializerTests { ServletContext context = mock(ServletContext.class); FilterRegistration.Dynamic registration = mock(FilterRegistration.Dynamic.class); ArgumentCaptor proxyCaptor = ArgumentCaptor.forClass(DelegatingFilterProxy.class); - given(context.addFilter(eq("springSecurityFilterChain"), any(DelegatingFilterProxy.class))) - .willReturn(registration); - @SuppressWarnings("unchecked") - ArgumentCaptor> modesCaptor = ArgumentCaptor.forClass(Set.class); - willDoNothing().given(context).setSessionTrackingModes(any()); + given(context.addFilter(eq("springSecurityFilterChain"), proxyCaptor.capture())).willReturn(registration); + ArgumentCaptor> modesCaptor = ArgumentCaptor + .forClass(new HashSet() { + }.getClass()); + willDoNothing().given(context).setSessionTrackingModes(modesCaptor.capture()); new AbstractSecurityWebApplicationInitializer() { @Override public Set getSessionTrackingModes() { return Collections.singleton(SessionTrackingMode.SSL); } }.onStartup(context); - verify(context).addFilter(eq("springSecurityFilterChain"), proxyCaptor.capture()); assertProxyDefaults(proxyCaptor.getValue()); - verify(context).setSessionTrackingModes(modesCaptor.capture()); Set modes = modesCaptor.getValue(); assertThat(modes).hasSize(1); assertThat(modes).containsExactly(SessionTrackingMode.SSL); diff --git a/web/src/test/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandlerTests.java b/web/src/test/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandlerTests.java index 6f50862411..97e1d67cae 100644 --- a/web/src/test/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandlerTests.java +++ b/web/src/test/java/org/springframework/security/web/csrf/XorCsrfTokenRequestAttributeHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -208,14 +208,6 @@ public class XorCsrfTokenRequestAttributeHandlerTests { assertThat(tokenValue).isEqualTo(this.token.getToken()); } - @Test - public void resolveCsrfTokenIsInvalidThenReturnsNull() { - this.request.setParameter(this.token.getParameterName(), XOR_CSRF_TOKEN_VALUE); - CsrfToken csrfToken = new DefaultCsrfToken("headerName", "paramName", "a"); - String tokenValue = this.handler.resolveCsrfTokenValue(this.request, csrfToken); - assertThat(tokenValue).isNull(); - } - private static Answer fillByteArray() { return (invocation) -> { byte[] bytes = invocation.getArgument(0); diff --git a/web/src/test/java/org/springframework/security/web/header/writers/CacheControlHeadersWriterTests.java b/web/src/test/java/org/springframework/security/web/header/writers/CacheControlHeadersWriterTests.java index b01fd9ce12..fd7c8feae0 100644 --- a/web/src/test/java/org/springframework/security/web/header/writers/CacheControlHeadersWriterTests.java +++ b/web/src/test/java/org/springframework/security/web/header/writers/CacheControlHeadersWriterTests.java @@ -47,7 +47,7 @@ public class CacheControlHeadersWriterTests { @Test public void writeHeaders() { this.writer.writeHeaders(this.request, this.response); - assertThat(this.response.getHeaderNames()).hasSize(3); + assertThat(this.response.getHeaderNames().size()).isEqualTo(3); assertThat(this.response.getHeaderValues("Cache-Control")) .containsOnly("no-cache, no-store, max-age=0, must-revalidate"); assertThat(this.response.getHeaderValues("Pragma")).containsOnly("no-cache"); diff --git a/web/src/test/java/org/springframework/security/web/header/writers/HstsHeaderWriterTests.java b/web/src/test/java/org/springframework/security/web/header/writers/HstsHeaderWriterTests.java index 7e2c33a443..616e562827 100644 --- a/web/src/test/java/org/springframework/security/web/header/writers/HstsHeaderWriterTests.java +++ b/web/src/test/java/org/springframework/security/web/header/writers/HstsHeaderWriterTests.java @@ -112,7 +112,7 @@ public class HstsHeaderWriterTests { public void writeHeadersInsecureRequestDoesNotWriteHeader() { this.request.setSecure(false); this.writer.writeHeaders(this.request, this.response); - assertThat(this.response.getHeaderNames()).isEmpty(); + assertThat(this.response.getHeaderNames().isEmpty()).isTrue(); } @Test diff --git a/web/src/test/java/org/springframework/security/web/header/writers/frameoptions/FrameOptionsHeaderWriterTests.java b/web/src/test/java/org/springframework/security/web/header/writers/frameoptions/FrameOptionsHeaderWriterTests.java index 0358f11983..27b47758b0 100644 --- a/web/src/test/java/org/springframework/security/web/header/writers/frameoptions/FrameOptionsHeaderWriterTests.java +++ b/web/src/test/java/org/springframework/security/web/header/writers/frameoptions/FrameOptionsHeaderWriterTests.java @@ -72,7 +72,7 @@ public class FrameOptionsHeaderWriterTests { public void writeHeadersAllowFromReturnsNull() { this.writer = new XFrameOptionsHeaderWriter(this.strategy); this.writer.writeHeaders(this.request, this.response); - assertThat(this.response.getHeaderNames()).isEmpty(); + assertThat(this.response.getHeaderNames().isEmpty()).isTrue(); } @Test diff --git a/web/src/test/java/org/springframework/security/web/method/ResolvableMethod.java b/web/src/test/java/org/springframework/security/web/method/ResolvableMethod.java index 12947d5f2f..834febc6ed 100644 --- a/web/src/test/java/org/springframework/security/web/method/ResolvableMethod.java +++ b/web/src/test/java/org/springframework/security/web/method/ResolvableMethod.java @@ -40,7 +40,7 @@ import org.springframework.cglib.proxy.Callback; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.Factory; import org.springframework.cglib.proxy.MethodProxy; -import org.springframework.core.DefaultParameterNameDiscoverer; +import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.MethodIntrospector; import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; @@ -131,7 +131,7 @@ public final class ResolvableMethod { private static final SpringObjenesis objenesis = new SpringObjenesis(); - private static final ParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer(); + private static final ParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer(); private final Method method; diff --git a/web/src/test/java/org/springframework/security/web/savedrequest/DefaultSavedRequestTests.java b/web/src/test/java/org/springframework/security/web/savedrequest/DefaultSavedRequestTests.java index b38b97c680..f010a6521d 100644 --- a/web/src/test/java/org/springframework/security/web/savedrequest/DefaultSavedRequestTests.java +++ b/web/src/test/java/org/springframework/security/web/savedrequest/DefaultSavedRequestTests.java @@ -45,7 +45,7 @@ public class DefaultSavedRequestTests { MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("If-None-Match", "somehashvalue"); DefaultSavedRequest saved = new DefaultSavedRequest(request, new MockPortResolver(8080, 8443)); - assertThat(saved.getHeaderValues("if-none-match")).isEmpty(); + assertThat(saved.getHeaderValues("if-none-match").isEmpty()).isTrue(); } // SEC-3082 diff --git a/web/src/test/java/org/springframework/security/web/savedrequest/SavedRequestAwareWrapperTests.java b/web/src/test/java/org/springframework/security/web/savedrequest/SavedRequestAwareWrapperTests.java index 3b9357c878..9f340ea736 100644 --- a/web/src/test/java/org/springframework/security/web/savedrequest/SavedRequestAwareWrapperTests.java +++ b/web/src/test/java/org/springframework/security/web/savedrequest/SavedRequestAwareWrapperTests.java @@ -24,7 +24,6 @@ import java.util.Locale; import jakarta.servlet.http.Cookie; import org.junit.jupiter.api.Test; -import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.security.web.PortResolverImpl; @@ -43,21 +42,22 @@ public class SavedRequestAwareWrapperTests { @Test public void savedRequestCookiesAreIgnored() { MockHttpServletRequest newRequest = new MockHttpServletRequest(); - newRequest.setCookies(new Cookie("cookie", "fromnew")); + newRequest.setCookies(new Cookie[] { new Cookie("cookie", "fromnew") }); MockHttpServletRequest savedRequest = new MockHttpServletRequest(); - savedRequest.setCookies(new Cookie("cookie", "fromsaved")); + savedRequest.setCookies(new Cookie[] { new Cookie("cookie", "fromsaved") }); SavedRequestAwareWrapper wrapper = createWrapper(savedRequest, newRequest); assertThat(wrapper.getCookies()).hasSize(1); assertThat(wrapper.getCookies()[0].getValue()).isEqualTo("fromnew"); } @Test + @SuppressWarnings("unchecked") public void savedRequesthHeaderIsReturnedIfSavedRequestIsSet() { MockHttpServletRequest savedRequest = new MockHttpServletRequest(); savedRequest.addHeader("header", "savedheader"); SavedRequestAwareWrapper wrapper = createWrapper(savedRequest, new MockHttpServletRequest()); assertThat(wrapper.getHeader("nonexistent")).isNull(); - Enumeration headers = wrapper.getHeaders("nonexistent"); + Enumeration headers = wrapper.getHeaders("nonexistent"); assertThat(headers.hasMoreElements()).isFalse(); assertThat(wrapper.getHeader("Header")).isEqualTo("savedheader"); headers = wrapper.getHeaders("heaDer"); @@ -97,7 +97,7 @@ public class SavedRequestAwareWrapperTests { SavedRequestAwareWrapper wrapper = createWrapper(savedRequest, wrappedRequest); assertThat(wrapper.getParameterValues("action")).hasSize(1); assertThat(wrapper.getParameterMap()).hasSize(1); - assertThat(wrapper.getParameterMap().get("action")).hasSize(1); + assertThat(((String[]) wrapper.getParameterMap().get("action"))).hasSize(1); } @Test @@ -127,7 +127,7 @@ public class SavedRequestAwareWrapperTests { wrappedRequest.setParameter("action", "bar"); assertThat(wrapper.getParameterValues("action")).isEqualTo(new Object[] { "bar", "foo" }); // Check map is consistent - String[] valuesFromMap = wrapper.getParameterMap().get("action"); + String[] valuesFromMap = (String[]) wrapper.getParameterMap().get("action"); assertThat(valuesFromMap).hasSize(2); assertThat(valuesFromMap[0]).isEqualTo("bar"); } @@ -169,13 +169,4 @@ public class SavedRequestAwareWrapperTests { assertThat(wrapper.getIntHeader("nonexistent")).isEqualTo(-1); } - @Test - public void correctContentTypeIsReturned() { - MockHttpServletRequest request = new MockHttpServletRequest("PUT", "/notused"); - request.setContentType(MediaType.APPLICATION_FORM_URLENCODED_VALUE); - - SavedRequestAwareWrapper wrapper = createWrapper(request, new MockHttpServletRequest("GET", "/notused")); - assertThat(wrapper.getContentType()).isEqualTo(MediaType.APPLICATION_FORM_URLENCODED_VALUE); - } - } diff --git a/web/src/test/java/org/springframework/security/web/server/context/ReactorContextWebFilterTests.java b/web/src/test/java/org/springframework/security/web/server/context/ReactorContextWebFilterTests.java index e5e099b6ea..103c64bbf7 100644 --- a/web/src/test/java/org/springframework/security/web/server/context/ReactorContextWebFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/server/context/ReactorContextWebFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2017 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,23 +17,17 @@ package org.springframework.security.web.server.context; import java.util.List; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnJre; -import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import reactor.core.publisher.Mono; -import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; import reactor.test.publisher.TestPublisher; import reactor.util.context.Context; -import org.springframework.core.task.VirtualThreadTaskExecutor; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.security.core.Authentication; @@ -125,32 +119,4 @@ public class ReactorContextWebFilterTests { StepVerifier.create(filter).expectAccessibleContext().hasKey(contextKey).then().verifyComplete(); } - @Test - public void filterWhenThreadFactoryIsPlatformThenSecurityContextLoaded() { - ThreadFactory threadFactory = Executors.defaultThreadFactory(); - assertSecurityContextLoaded(threadFactory); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void filterWhenThreadFactoryIsVirtualThenSecurityContextLoaded() { - ThreadFactory threadFactory = new VirtualThreadTaskExecutor().getVirtualThreadFactory(); - assertSecurityContextLoaded(threadFactory); - } - - private void assertSecurityContextLoaded(ThreadFactory threadFactory) { - SecurityContextImpl context = new SecurityContextImpl(this.principal); - given(this.repository.load(any())).willReturn(Mono.just(context)); - // @formatter:off - WebFilter subscribeOnThreadFactory = (exchange, chain) -> chain.filter(exchange) - .subscribeOn(Schedulers.newSingle(threadFactory)); - WebFilter assertSecurityContext = (exchange, chain) -> ReactiveSecurityContextHolder.getContext() - .map(SecurityContext::getAuthentication) - .doOnSuccess((authentication) -> assertThat(authentication).isSameAs(this.principal)) - .then(chain.filter(exchange)); - // @formatter:on - this.handler = WebTestHandler.bindToWebFilters(subscribeOnThreadFactory, this.filter, assertSecurityContext); - this.handler.exchange(this.exchange); - } - } diff --git a/web/src/test/java/org/springframework/security/web/server/context/SecurityContextServerWebExchangeWebFilterTests.java b/web/src/test/java/org/springframework/security/web/server/context/SecurityContextServerWebExchangeWebFilterTests.java index 2d8c88fe13..dd0a3f30c2 100644 --- a/web/src/test/java/org/springframework/security/web/server/context/SecurityContextServerWebExchangeWebFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/server/context/SecurityContextServerWebExchangeWebFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2017 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,25 +17,17 @@ package org.springframework.security.web.server.context; import java.util.Collections; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnJre; -import org.junit.jupiter.api.condition.JRE; import reactor.core.publisher.Mono; -import reactor.core.scheduler.Schedulers; import reactor.test.StepVerifier; -import org.springframework.core.task.VirtualThreadTaskExecutor; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.ReactiveSecurityContextHolder; -import org.springframework.security.test.web.reactive.server.WebTestHandler; import org.springframework.web.server.ServerWebExchange; -import org.springframework.web.server.WebFilter; import org.springframework.web.server.handler.DefaultWebFilterChain; import static org.assertj.core.api.Assertions.assertThat; @@ -88,31 +80,4 @@ public class SecurityContextServerWebExchangeWebFilterTests { StepVerifier.create(result).verifyComplete(); } - @Test - public void filterWhenThreadFactoryIsPlatformThenContextPopulated() { - ThreadFactory threadFactory = Executors.defaultThreadFactory(); - assertPrincipalPopulated(threadFactory); - } - - @Test - @DisabledOnJre(JRE.JAVA_17) - public void filterWhenThreadFactoryIsVirtualThenContextPopulated() { - ThreadFactory threadFactory = new VirtualThreadTaskExecutor().getVirtualThreadFactory(); - assertPrincipalPopulated(threadFactory); - } - - private void assertPrincipalPopulated(ThreadFactory threadFactory) { - // @formatter:off - WebFilter subscribeOnThreadFactory = (exchange, chain) -> chain.filter(exchange) - .contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.principal)) - .subscribeOn(Schedulers.newSingle(threadFactory)); - WebFilter assertPrincipal = (exchange, chain) -> exchange.getPrincipal() - .doOnSuccess((principal) -> assertThat(principal).isSameAs(this.principal)) - .then(chain.filter(exchange)); - // @formatter:on - WebTestHandler handler = WebTestHandler.bindToWebFilters(subscribeOnThreadFactory, this.filter, - assertPrincipal); - handler.exchange(this.exchange); - } - } diff --git a/web/src/test/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandlerTests.java b/web/src/test/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandlerTests.java index 08b54b01f9..c6b800af06 100644 --- a/web/src/test/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandlerTests.java +++ b/web/src/test/java/org/springframework/security/web/server/csrf/XorServerCsrfTokenRequestAttributeHandlerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -188,18 +188,6 @@ public class XorServerCsrfTokenRequestAttributeHandlerTests { StepVerifier.create(csrfToken).expectNext(this.token.getToken()).verifyComplete(); } - @Test - public void resolveCsrfTokenIsInvalidThenReturnsNull() { - this.exchange = MockServerWebExchange - .builder(MockServerHttpRequest.post("/") - .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE) - .body(this.token.getParameterName() + "=" + XOR_CSRF_TOKEN_VALUE)) - .build(); - CsrfToken token = new DefaultCsrfToken("headerName", "paramName", "a"); - Mono csrfToken = this.handler.resolveCsrfTokenValue(this.exchange, token); - assertThat(csrfToken.block()).isNull(); - } - private static Answer fillByteArray() { return (invocation) -> { byte[] bytes = invocation.getArgument(0); diff --git a/web/src/test/java/org/springframework/security/web/server/savedrequest/CookieServerRequestCacheTests.java b/web/src/test/java/org/springframework/security/web/server/savedrequest/CookieServerRequestCacheTests.java index 0c4ce943e0..ac4b785bac 100644 --- a/web/src/test/java/org/springframework/security/web/server/savedrequest/CookieServerRequestCacheTests.java +++ b/web/src/test/java/org/springframework/security/web/server/savedrequest/CookieServerRequestCacheTests.java @@ -46,7 +46,7 @@ public class CookieServerRequestCacheTests { .from(MockServerHttpRequest.get("/secured/").accept(MediaType.TEXT_HTML)); this.cache.saveRequest(exchange).block(); MultiValueMap cookies = exchange.getResponse().getCookies(); - assertThat(cookies).hasSize(1); + assertThat(cookies.size()).isEqualTo(1); ResponseCookie cookie = cookies.getFirst("REDIRECT_URI"); assertThat(cookie).isNotNull(); String encodedRedirectUrl = Base64.getEncoder().encodeToString("/secured/".getBytes()); @@ -60,7 +60,7 @@ public class CookieServerRequestCacheTests { .from(MockServerHttpRequest.get("/secured/").queryParam("key", "value").accept(MediaType.TEXT_HTML)); this.cache.saveRequest(exchange).block(); MultiValueMap cookies = exchange.getResponse().getCookies(); - assertThat(cookies).hasSize(1); + assertThat(cookies.size()).isEqualTo(1); ResponseCookie cookie = cookies.getFirst("REDIRECT_URI"); assertThat(cookie).isNotNull(); String encodedRedirectUrl = Base64.getEncoder().encodeToString("/secured/?key=value".getBytes()); diff --git a/web/src/test/java/org/springframework/security/web/session/SessionManagementFilterTests.java b/web/src/test/java/org/springframework/security/web/session/SessionManagementFilterTests.java index bbd095631f..043bfa1e07 100644 --- a/web/src/test/java/org/springframework/security/web/session/SessionManagementFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/session/SessionManagementFilterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,10 +20,8 @@ import jakarta.servlet.FilterChain; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.http.HttpStatus; import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; @@ -31,7 +29,6 @@ import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.DefaultRedirectStrategy; import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.security.web.authentication.session.SessionAuthenticationException; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; @@ -49,11 +46,9 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; /** * @author Luke Taylor * @author Rob Winch - * @author Mark Chesney */ public class SessionManagementFilterTests { - @BeforeEach @AfterEach public void clearContext() { SecurityContextHolder.clearContext(); @@ -179,69 +174,6 @@ public class SessionManagementFilterTests { assertThat(response.getRedirectedUrl()).isEqualTo("/requested"); } - @Test - public void responseIsRedirectedToRequestedUrlIfContextPathIsSetAndSessionIsInvalid() throws Exception { - // given - DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); - redirectStrategy.setContextRelative(true); - RequestedUrlRedirectInvalidSessionStrategy invalidSessionStrategy = new RequestedUrlRedirectInvalidSessionStrategy(); - invalidSessionStrategy.setCreateNewSession(true); - invalidSessionStrategy.setRedirectStrategy(redirectStrategy); - SecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class); - SessionAuthenticationStrategy sessionAuthenticationStrategy = mock(SessionAuthenticationStrategy.class); - SessionManagementFilter filter = new SessionManagementFilter(securityContextRepository, - sessionAuthenticationStrategy); - filter.setInvalidSessionStrategy(invalidSessionStrategy); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setContextPath("/context"); - request.setRequestedSessionId("xxx"); - request.setRequestedSessionIdValid(false); - request.setRequestURI("/context/requested"); - MockHttpServletResponse response = new MockHttpServletResponse(); - FilterChain chain = mock(FilterChain.class); - - // when - filter.doFilter(request, response, chain); - - // then - verify(securityContextRepository).containsContext(request); - verifyNoMoreInteractions(securityContextRepository, sessionAuthenticationStrategy, chain); - assertThat(response.isCommitted()).isTrue(); - assertThat(response.getRedirectedUrl()).isEqualTo("/context/requested"); - assertThat(response.getStatus()).isEqualTo(302); - } - - @Test - public void responseIsRedirectedToRequestedUrlIfStatusCodeIsSetAndSessionIsInvalid() throws Exception { - // given - DefaultRedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); - redirectStrategy.setStatusCode(HttpStatus.TEMPORARY_REDIRECT); - RequestedUrlRedirectInvalidSessionStrategy invalidSessionStrategy = new RequestedUrlRedirectInvalidSessionStrategy(); - invalidSessionStrategy.setCreateNewSession(true); - invalidSessionStrategy.setRedirectStrategy(redirectStrategy); - SecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class); - SessionAuthenticationStrategy sessionAuthenticationStrategy = mock(SessionAuthenticationStrategy.class); - SessionManagementFilter filter = new SessionManagementFilter(securityContextRepository, - sessionAuthenticationStrategy); - filter.setInvalidSessionStrategy(invalidSessionStrategy); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setRequestedSessionId("xxx"); - request.setRequestedSessionIdValid(false); - request.setRequestURI("/requested"); - MockHttpServletResponse response = new MockHttpServletResponse(); - FilterChain chain = mock(FilterChain.class); - - // when - filter.doFilter(request, response, chain); - - // then - verify(securityContextRepository).containsContext(request); - verifyNoMoreInteractions(securityContextRepository, sessionAuthenticationStrategy, chain); - assertThat(response.isCommitted()).isTrue(); - assertThat(response.getRedirectedUrl()).isEqualTo("/requested"); - assertThat(response.getStatus()).isEqualTo(307); - } - @Test public void customAuthenticationTrustResolver() throws Exception { AuthenticationTrustResolver trustResolver = mock(AuthenticationTrustResolver.class); diff --git a/web/src/test/java/org/springframework/security/web/util/TextEscapeUtilsTests.java b/web/src/test/java/org/springframework/security/web/util/TextEscapeUtilsTests.java index 2a8cc8235e..71482298c2 100644 --- a/web/src/test/java/org/springframework/security/web/util/TextEscapeUtilsTests.java +++ b/web/src/test/java/org/springframework/security/web/util/TextEscapeUtilsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2016 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. @@ -62,7 +62,7 @@ public class TextEscapeUtilsTests { @Test public void undefinedSurrogatePairIsIgnored() { - assertThat(TextEscapeUtils.escapeEntities("abc\uDBFF\uDFFFa")).isEqualTo("abca"); + assertThat(TextEscapeUtils.escapeEntities("abc\uD888\uDC00a")).isEqualTo("abca"); } } diff --git a/web/src/test/java/org/springframework/security/web/util/matcher/MediaTypeRequestMatcherTests.java b/web/src/test/java/org/springframework/security/web/util/matcher/MediaTypeRequestMatcherTests.java index a7adcabb8f..6267e81e32 100644 --- a/web/src/test/java/org/springframework/security/web/util/matcher/MediaTypeRequestMatcherTests.java +++ b/web/src/test/java/org/springframework/security/web/util/matcher/MediaTypeRequestMatcherTests.java @@ -88,7 +88,7 @@ public class MediaTypeRequestMatcherTests { @Test public void constructorWhenEmptyMediaTypeThenIAE() { - assertThatIllegalArgumentException().isThrownBy(MediaTypeRequestMatcher::new); + assertThatIllegalArgumentException().isThrownBy(() -> new MediaTypeRequestMatcher()); } @Test diff --git a/web/src/test/resources/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPointTest-context.xml b/web/src/test/resources/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPointTest-context.xml index 508daae44c..66d965955b 100644 --- a/web/src/test/resources/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPointTest-context.xml +++ b/web/src/test/resources/org/springframework/security/web/authentication/DelegatingAuthenticationEntryPointTest-context.xml @@ -11,15 +11,15 @@ - + - + - - + + - + - +