1
0
mirror of synced 2026-07-08 12:20:02 +00:00

Compare commits

...

104 Commits

Author SHA1 Message Date
Rob Winch ada3337104 Release 5.5.7 2022-05-16 11:37:33 -05:00
Josh Cummings 0bd7daf899 Improve Upgrading 2022-05-16 11:37:33 -05:00
Rob Winch c6461d61ba AntRegexRequestMatcher Optimization
Closes gh-11234
2022-05-16 10:18:12 -05:00
Rob Winch 4405cf18f3 Extract rejectNonPrintableAsciiCharactersInFieldName
Closes gh-11234
2022-05-16 10:18:11 -05:00
Rob Winch 7eb8a58244 Update org.springframework.data to 2021.0.11
Closes gh-11216
2022-05-13 09:51:32 -05:00
Rob Winch 415ee4d823 Update org.springframework to 5.3.20
Closes gh-11215
2022-05-13 09:51:30 -05:00
Rob Winch 4d47d513b5 Update io.projectreactor to 2020.0.19
Closes gh-11213
2022-05-13 09:51:26 -05:00
Marcus Da Coregio 7983c695e2 Fix mvcMatchers overriding previous paths
Closes gh-10956
2022-05-12 10:22:54 -03:00
Marcus Da Coregio 15b3744dcf Fix setServletContext not being called for AuthorizationManagerWebInvocationPrivilegeEvaluator
Issue gh-10908
2022-05-12 10:12:31 -03:00
Steve Riesenberg 20b94c4a26 Next development version 2022-04-18 12:07:24 -05:00
Steve Riesenberg 69a13dfcd4 Release 5.5.6 2022-04-18 10:58:36 -05:00
Steve Riesenberg 89afd4c487 Update spring-ldap-core to 2.3.7.RELEASE
Closes gh-11139
2022-04-18 10:55:22 -05:00
Steve Riesenberg 35e769d90b Update org.springframework.data to 2021.0.10
Closes gh-11138
2022-04-18 10:55:22 -05:00
Steve Riesenberg d9e1906281 Update org.springframework to 5.3.19
Closes gh-11137
2022-04-18 10:55:21 -05:00
Steve Riesenberg e6f28b9c02 Update org.eclipse.jetty to 9.4.46.v20220331
Closes gh-11136
2022-04-18 10:55:21 -05:00
Steve Riesenberg 65230b8ee2 Update org.aspectj to 1.9.9.1
Closes gh-11135
2022-04-18 10:55:21 -05:00
Steve Riesenberg 57d94ce816 Update io.rsocket to 1.1.2
Closes gh-11134
2022-04-18 10:55:21 -05:00
Steve Riesenberg c967d6569d Update io.projectreactor to 2020.0.18
Closes gh-11132
Closes gh-11133
2022-04-18 10:55:20 -05:00
Steve Riesenberg 10f7a968c1 Update jackson-bom to 2.12.6.20220326
Closes gh-11129
Closes gh-11130
Closes gh-11131
2022-04-18 10:52:58 -05:00
Steve Riesenberg 344bad34be Update logback-classic to 1.2.11
Closes gh-11128
2022-04-18 10:33:45 -05:00
Marcus Da Coregio 9792e2a0fa Use ServletContext in AuthorizationManagerWebInvocationPrivilegeEvaluator
Closes gh-10908
2022-03-28 10:21:15 -03:00
Marcus Da Coregio 44508df940 AuthorizationManagerWebInvocationPrivilegeEvaluator grant access when AuthorizationManager abstains
Closes gh-10950
2022-03-09 15:38:11 -03:00
Josh Cummings 35ac1dd71e Polish Formatting
Issue gh-10799
2022-03-02 17:11:04 -07:00
Sander van Schouwenburg 1460d53b2f Preserve order of RelyingPartRegistration credentials
Issue gh-10799
2022-03-02 17:11:04 -07:00
Josh Cummings 040a28a8c9 Replace Apache Commons Base64 Decoding
Issue gh-10923
2022-03-02 17:00:23 -07:00
Talerngpong Virojwutikul 0ae6e6e767 Update PasswordEncoder declaration
Closes gh-10910
2022-03-01 07:51:00 -07:00
Filip Hanik 2f25c03609 Change HashSet to LinkedHashSet
For various RelyingPartyRegistration.credentials to preserve order of insertion.

Issue gh-10799
2022-02-28 15:02:07 -07:00
Eleftheria Stein 7271499e74 Next development version 2022-02-21 13:15:57 +01:00
Eleftheria Stein f2fc2a593f Release 5.5.5 2022-02-21 12:36:32 +01:00
Eleftheria Stein f491cc8938 Update spring-ldap-core to 2.3.6.RELEASE
Closes gh-10863
2022-02-21 12:24:40 +01:00
Eleftheria Stein 22a39545b9 Update org.springframework.data to 2021.0.9
Closes gh-10862
2022-02-21 12:24:38 +01:00
Eleftheria Stein 36d57b597e Update org.springframework to 5.3.16
Closes gh-10861
2022-02-21 12:24:36 +01:00
Eleftheria Stein 02c6b04320 Update org.slf4j to 1.7.36
Closes gh-10860
2022-02-21 12:24:33 +01:00
Eleftheria Stein 2c3522e290 Update org.eclipse.jetty to 9.4.45.v20220203
Closes gh-10859
2022-02-21 12:24:31 +01:00
Eleftheria Stein 01e37feb0d Update org.aspectj to 1.9.8
Closes gh-10858
2022-02-21 12:24:29 +01:00
Eleftheria Stein 6403cba2f5 Update io.spring.javaformat to 0.0.31
Closes gh-10857
2022-02-21 12:24:26 +01:00
Eleftheria Stein d655deb718 Update r2dbc-h2 to 0.8.5.RELEASE
Closes gh-10856
2022-02-21 12:24:24 +01:00
Eleftheria Stein 6156f29c3b Update io.projectreactor to 2020.0.16
Closes gh-10854
2022-02-21 12:24:19 +01:00
Eleftheria Stein 25363ac13e Update logback-classic to 1.2.10
Closes gh-10851
2022-02-21 12:24:12 +01:00
Rob Winch 15d902322f Add .sdkmanrc 2022-02-18 13:49:09 -06:00
Talerngpong Virojwutikul 070b67a9a1 add Kotlin examples for Spring Data Integration of servlet application 2022-02-18 08:50:06 -03:00
giger85 304ef2302c Replace StringUtils class completely
Issue gh-9925
Closes gh-10805
2022-02-10 18:10:53 +01:00
Josh Cummings a09f6e15ad Polish ignoring() log messaging
- Public API remains unchanged

Issue gh-9334
2022-02-07 15:22:49 -07:00
Manuel Jordan 7e0302be5c Print ignore message DefaultSecurityFilterChain
When either `web.ignoring().mvcMatchers(...)` or
`web.ignoring().antMatchers(...)` methods are used, for all their
variations, the DefaultSecurityFilterChain class now indicates
correctly through its ouput what paths are ignored according the
`ignoring()` settings.

Closes gh-9334
2022-02-07 15:22:49 -07:00
Eleftheria Stein 7554ee8fec Fix typo in getting started docs
Closes gh-10736
2022-02-02 11:04:46 +01:00
Marcus Da Coregio 893b651aea RequestMatcherDelegatingWebInvocationPrivilegeEvaluator doesn't provided access to the ServletContext
Closes gh-10779
2022-01-31 09:57:34 -03:00
Josh Cummings ca353d6781 Use noNullElements
Collection#contains(null) does not work for all collection types

Closes gh-10703
2022-01-14 15:19:13 -07:00
Marcus Da Coregio a763382c3e Make source code compatible with JDK 8
Closes gh-10695
2022-01-12 17:26:25 -03:00
Marcus Da Coregio 60595f2801 Fix @since tag
Issue gh-10590, gh-10554
2022-01-06 13:22:58 -03:00
Marcus Da Coregio ba810e468f Configure WebInvocationPrivilegeEvaluator bean for multiple filter chains
Closes gh-10554
2022-01-05 14:01:57 -03:00
Marcus Da Coregio 40dfe8f259 Add RequestMatcherEntry 2022-01-05 14:00:47 -03:00
Marcus Da Coregio b448954f43 Introduce AuthorizationManagerWebInvocationPrivilegeEvaluator
Closes gh-10590
2022-01-05 13:57:36 -03:00
Marcus Da Coregio b0c7d7703b Next development version 2021-12-20 11:02:26 -03:00
Marcus Da Coregio 5bc0537f1b Release 5.5.4 2021-12-20 10:18:36 -03:00
Marcus Da Coregio 355bf8ddee Update spring-ldap-core to 2.3.5.RELEASE
Closes gh-10641
2021-12-20 10:02:03 -03:00
Marcus Da Coregio 05585a5f13 Update org.springframework.data to 2021.0.7
Closes gh-10640
2021-12-20 10:02:00 -03:00
Marcus Da Coregio 00d350608a Update org.springframework to 5.3.14
Closes gh-10639
2021-12-20 10:01:58 -03:00
Marcus Da Coregio 906c520fa0 Update org.jetbrains.kotlin to 1.5.32
Closes gh-10638
2021-12-20 10:01:55 -03:00
Marcus Da Coregio ca19601f8b Update cas-client-core to 3.6.4
Closes gh-10637
2021-12-20 10:01:52 -03:00
Marcus Da Coregio bdd4d05ce2 Update hsqldb to 2.6.1
Closes gh-10636
2021-12-20 10:01:50 -03:00
Marcus Da Coregio 0eec932917 Update hibernate-entitymanager to 5.4.33
Closes gh-10635
2021-12-20 10:01:47 -03:00
Marcus Da Coregio af5c1761e8 Update io.projectreactor to 2020.0.14
Closes gh-10633
2021-12-20 10:01:42 -03:00
Marcus Da Coregio 491989c692 Update jackson-bom to 2.12.6
Closes gh-10630
2021-12-20 10:01:34 -03:00
Marcus Da Coregio 731069a69a Update logback-classic to 1.2.9
Closes gh-10629
2021-12-20 10:01:31 -03:00
Steve Riesenberg 47b8860681 Update copyright year
Issue gh-10557
2021-12-01 17:36:52 -06:00
Steve Riesenberg 828cac8889 Fix case sensitive headers comparison
Closes gh-10557
2021-12-01 15:19:33 -06:00
Jonas Erbe aefd2d497c Fix JwtClaimValidator wrong error code
Previously JwtClaimValidator returned the invalid_request error on claim validation failure.
But validators have to return invalid_token errors on failure according to:
https://datatracker.ietf.org/doc/html/rfc6750#section-3.1.
Also see gh-10337

Closes gh-10337
2021-11-29 12:22:30 -07:00
Henning Poettker 775bf91c77 Fix return type for NoOpPasswordEncoder bean in documentation 2021-11-23 14:14:41 -03:00
Marcus Da Coregio 2bf7a5ae80 Improve log message when no CSRF token found
Closes gh-10436
2021-11-19 08:37:25 -03:00
Josh Cummings c495ff8759 Fix AuthnRequestConverter Sample Typos
Closes gh-10364
2021-11-18 13:37:45 -07:00
Josh Cummings b7a12e1b80 Fix setJWTClaimSetJWSKeySelector Typo
Closes gh-10504
2021-11-16 15:34:40 -07:00
Josh Cummings 670d10dadd Fix jwtDecoder Documentation Usage
Closes gh-10505
2021-11-16 15:21:05 -07:00
Josh Cummings 72db6a20c9 Don't Cache ReactiveJwtDecoders Errors
Closes gh-10444
2021-11-10 17:44:15 -07:00
Marcus Da Coregio 00f4033b9b Update DefaultWebInvocationPrivilegeEvaluator to use current ServletContext
Closes gh-10208
2021-10-22 13:22:12 -03:00
Steve Riesenberg ddb59d439f Update io.spring.javaformat to 0.0.29
Closes gh-10411
2021-10-22 10:19:35 -05:00
Rob Winch e4a76b0ec9 Checkstyle Fixes
- Javadoc tag ordering
- Private constructors before inner classes

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

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

Issue gh-10254
2021-09-14 15:22:32 -03:00
Derek Van Blerkom c55f1f8bea Fix return type to allow further security config
Issue gh-10245
2021-09-13 15:41:04 -03:00
Marcus Da Coregio 670cf99258 Update docs to point to ACL samples
Closes gh-10110
2021-09-06 11:30:38 -03:00
Josh Cummings 41feec4766 Remove unused Sonar from Build
Closes gh-10205
2021-08-20 16:47:55 -06:00
Josh Cummings 34e04f642c Next Development Version 2021-08-16 12:48:56 -06:00
137 changed files with 2144 additions and 336 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# EditorConfig for Spring Security
# see https://github.com/spring-projects/spring-security/blob/master/CONTRIBUTING.md#mind-the-whitespace
# see https://github.com/spring-projects/spring-security/blob/master/CONTRIBUTING.adoc#mind-the-whitespace
root = true
@@ -45,7 +45,7 @@ jobs:
- name: Setup gradle user name
run: |
mkdir -p ~/.gradle
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
- name: Cache Gradle packages
uses: actions/cache@v2
with:
@@ -71,49 +71,16 @@ jobs:
- name: Setup gradle user name
run: |
mkdir -p ~/.gradle
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
- name: Snapshot Tests
run: |
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
./gradlew test --refresh-dependencies -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" -PforceMavenRepositories=snapshot -PspringVersion='5.+' -PreactorVersion='20+' -PspringDataVersion='Neumann-BUILD-SNAPSHOT' -PrsocketVersion=1.1.0-SNAPSHOT -PspringBootVersion=2.4.0-SNAPSHOT -PlocksDisabled --stacktrace
sonar_analysis:
name: Static Code Analysis
needs: [prerequisites]
runs-on: ubuntu-latest
if: needs.prerequisites.outputs.runjobs
env:
SONAR_URL: ${{ secrets.SONAR_URL }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
steps:
- uses: actions/checkout@v2
- name: Set up JDK
uses: actions/setup-java@v1
with:
java-version: '11'
- name: Setup gradle user name
run: |
mkdir -p ~/.gradle
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
- name: Run Sonar on given (non-main) branch
if: ${{ github.ref != 'refs/heads/main' }}
run: |
export BRANCH=${GITHUB_REF#refs/heads/}
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
./gradlew sonarqube -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" -PexcludeProjects='**/samples/**' -Dsonar.projectKey="spring-security-${GITHUB_REF#refs/heads/}" -Dsonar.projectName="spring-security-${GITHUB_REF#refs/heads/}" -Dsonar.host.url="$SONAR_URL" -Dsonar.login="$SONAR_TOKEN" --stacktrace
- name: Run Sonar on main
if: ${{ github.ref == 'refs/heads/main' }}
run: |
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
./gradlew sonarqube -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" -PexcludeProjects='**/samples/**' -Dsonar.host.url="$SONAR_URL" -Dsonar.login="$SONAR_TOKEN" --stacktrace
deploy_artifacts:
name: Deploy Artifacts
needs: [build_jdk_11, snapshot_tests, sonar_analysis]
needs: [build_jdk_11, snapshot_tests]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@@ -124,7 +91,7 @@ jobs:
- name: Setup gradle user name
run: |
mkdir -p ~/.gradle
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
- name: Deploy artifacts
run: |
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
@@ -140,7 +107,7 @@ jobs:
ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
deploy_docs:
name: Deploy Docs
needs: [build_jdk_11, snapshot_tests, sonar_analysis]
needs: [build_jdk_11, snapshot_tests]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@@ -151,7 +118,7 @@ jobs:
- name: Setup gradle user name
run: |
mkdir -p ~/.gradle
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
- name: Deploy Docs
run: |
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
@@ -164,7 +131,7 @@ jobs:
DOCS_HOST: ${{ secrets.DOCS_HOST }}
deploy_schema:
name: Deploy Schema
needs: [build_jdk_11, snapshot_tests, sonar_analysis]
needs: [build_jdk_11, snapshot_tests]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@@ -175,7 +142,7 @@ jobs:
- name: Setup gradle user name
run: |
mkdir -p ~/.gradle
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
- name: Deploy Schema
run: |
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
@@ -188,7 +155,7 @@ jobs:
DOCS_HOST: ${{ secrets.DOCS_HOST }}
notify_result:
name: Check for failures
needs: [build_jdk_11, snapshot_tests, sonar_analysis, deploy_artifacts, deploy_docs, deploy_schema]
needs: [build_jdk_11, snapshot_tests, deploy_artifacts, deploy_docs, deploy_schema]
if: failure()
runs-on: ubuntu-latest
steps:
+6
View File
@@ -0,0 +1,6 @@
# Use sdkman to run "sdk env" to initialize with correct JDK version
# Enable auto-env through the sdkman_auto_env config
# See https://sdkman.io/usage#config
# A summary is to add the following to ~/.sdkman/etc/config
# sdkman_auto_env=true
java=11.0.14-tem
+3 -1
View File
@@ -1,7 +1,7 @@
buildscript {
dependencies {
classpath "io.spring.javaformat:spring-javaformat-gradle-plugin:$springJavaformatVersion"
classpath 'io.spring.nohttp:nohttp-gradle:0.0.8'
classpath 'io.spring.nohttp:nohttp-gradle:0.0.10'
classpath "io.freefair.gradle:aspectj-plugin:5.3.3.3"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
classpath "com.netflix.nebula:nebula-project-plugin:8.0.0"
@@ -106,6 +106,8 @@ subprojects {
}
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
options.compilerArgs.add("-parameters")
options.release = 8
}
}
+2 -2
View File
@@ -72,14 +72,14 @@ dependencies {
implementation localGroovy()
implementation 'io.github.gradle-nexus:publish-plugin:1.1.0'
implementation 'io.projectreactor:reactor-core:3.4.9'
implementation 'io.projectreactor:reactor-core:3.4.18'
implementation 'gradle.plugin.org.gretty:gretty:3.0.1'
implementation 'com.apollographql.apollo:apollo-runtime:2.4.5'
implementation 'com.github.ben-manes:gradle-versions-plugin:0.38.0'
implementation 'io.spring.gradle:docbook-reference-plugin:0.3.1'
implementation 'io.spring.gradle:propdeps-plugin:0.0.10.RELEASE'
implementation 'io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.15'
implementation 'io.spring.nohttp:nohttp-gradle:0.0.8'
implementation 'io.spring.nohttp:nohttp-gradle:0.0.10'
implementation 'org.aim42:htmlSanityCheck:1.1.6'
implementation 'org.asciidoctor:asciidoctor-gradle-jvm:3.1.0'
implementation 'org.asciidoctor:asciidoctor-gradle-jvm-pdf:3.1.0'
@@ -36,7 +36,7 @@ class CheckstylePlugin implements Plugin<Project> {
if (checkstyleDir.exists() && checkstyleDir.directory) {
project.getPluginManager().apply('checkstyle')
project.dependencies.add('checkstyle', 'io.spring.javaformat:spring-javaformat-checkstyle:0.0.15')
project.dependencies.add('checkstyle', 'io.spring.nohttp:nohttp-checkstyle:0.0.8')
project.dependencies.add('checkstyle', 'io.spring.nohttp:nohttp-checkstyle:0.0.10')
project.checkstyle {
configDirectory = checkstyleDir
@@ -40,9 +40,9 @@ import org.jasig.cas.client.authentication.AttributePrincipal;
* </pre>
*
* @author Jitendra Singh
* @since 4.2
* @see CasJackson2Module
* @see org.springframework.security.jackson2.SecurityJackson2Modules
* @since 4.2
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
@@ -37,9 +37,9 @@ import org.jasig.cas.client.proxy.ProxyRetriever;
* </pre>
*
* @author Jitendra Singh
* @since 4.2
* @see CasJackson2Module
* @see org.springframework.security.jackson2.SecurityJackson2Modules
* @since 4.2
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
@@ -47,9 +47,9 @@ import org.springframework.security.core.userdetails.UserDetails;
* </pre>
*
* @author Jitendra Singh
* @since 4.2
* @see CasJackson2Module
* @see org.springframework.security.jackson2.SecurityJackson2Modules
* @since 4.2
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,
@@ -19,8 +19,8 @@ package org.springframework.security.config;
/**
* Callback interface that accepts a single input argument and returns no result.
*
* @author Eleftheria Stein
* @param <T> the type of the input to the operation
* @author Eleftheria Stein
* @since 5.2
*/
@FunctionalInterface
@@ -93,7 +93,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
if (!namespaceMatchesVersion(element)) {
pc.getReaderContext().fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or "
+ "spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema "
+ "with Spring Security 5.4. Please update your schema declarations to the 5.4 schema.", element);
+ "with Spring Security 5.5. Please update your schema declarations to the 5.5 schema.", element);
}
String name = pc.getDelegate().getLocalName(element);
BeanDefinitionParser parser = this.parsers.get(name);
@@ -213,7 +213,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
private boolean matchesVersionInternal(Element element) {
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
return schemaLocation.matches("(?m).*spring-security-5\\.4.*.xsd.*")
return schemaLocation.matches("(?m).*spring-security-5\\.5.*.xsd.*")
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|| !schemaLocation.matches("(?m).*spring-security.*");
}
@@ -27,8 +27,8 @@ import org.springframework.security.config.annotation.authentication.builders.Au
* {@link AuthenticationConfiguration} to configure the global
* {@link AuthenticationManagerBuilder}.
*
* @since 5.0
* @author Rob Winch
* @since 5.0
*/
@Order(100)
public abstract class GlobalAuthenticationConfigurerAdapter
@@ -30,10 +30,10 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
* class is not intended to be imported manually rather it is imported automatically when
* using {@link EnableWebSecurity} or {@link EnableGlobalMethodSecurity}.
*
* @see EnableWebSecurity
* @see EnableGlobalMethodSecurity
* @author Rob Winch
* @since 3.2
* @see EnableWebSecurity
* @see EnableGlobalMethodSecurity
*/
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -3008,20 +3008,26 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
*/
public final class MvcMatchersRequestMatcherConfigurer extends RequestMatcherConfigurer {
private final List<MvcRequestMatcher> mvcMatchers;
/**
* Creates a new instance
* @param context the {@link ApplicationContext} to use
* @param matchers the {@link MvcRequestMatcher} instances to set the servlet path
* on if {@link #servletPath(String)} is set.
* @param mvcMatchers the {@link MvcRequestMatcher} instances to set the servlet
* path on if {@link #servletPath(String)} is set.
* @param allMatchers the {@link RequestMatcher} instances to continue the
* configuration
*/
private MvcMatchersRequestMatcherConfigurer(ApplicationContext context, List<MvcRequestMatcher> matchers) {
private MvcMatchersRequestMatcherConfigurer(ApplicationContext context, List<MvcRequestMatcher> mvcMatchers,
List<RequestMatcher> allMatchers) {
super(context);
this.matchers = new ArrayList<>(matchers);
this.mvcMatchers = new ArrayList<>(mvcMatchers);
this.matchers = allMatchers;
}
public RequestMatcherConfigurer servletPath(String servletPath) {
for (RequestMatcher matcher : this.matchers) {
((MvcRequestMatcher) matcher).setServletPath(servletPath);
for (MvcRequestMatcher matcher : this.mvcMatchers) {
matcher.setServletPath(servletPath);
}
return this;
}
@@ -3046,7 +3052,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
public MvcMatchersRequestMatcherConfigurer mvcMatchers(HttpMethod method, String... mvcPatterns) {
List<MvcRequestMatcher> mvcMatchers = createMvcMatchers(method, mvcPatterns);
setMatchers(mvcMatchers);
return new MvcMatchersRequestMatcherConfigurer(getContext(), mvcMatchers);
return new MvcMatchersRequestMatcherConfigurer(getContext(), mvcMatchers, this.matchers);
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
@@ -33,6 +34,7 @@ import org.springframework.http.HttpMethod;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityBuilder;
@@ -47,9 +49,12 @@ import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.debug.DebugFilter;
import org.springframework.security.web.firewall.HttpFirewall;
@@ -57,7 +62,9 @@ import org.springframework.security.web.firewall.RequestRejectedHandler;
import org.springframework.security.web.firewall.StrictHttpFirewall;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
import org.springframework.util.Assert;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.filter.DelegatingFilterProxy;
/**
@@ -81,7 +88,7 @@ import org.springframework.web.filter.DelegatingFilterProxy;
* @see WebSecurityConfiguration
*/
public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter, WebSecurity>
implements SecurityBuilder<Filter>, ApplicationContextAware {
implements SecurityBuilder<Filter>, ApplicationContextAware, ServletContextAware {
private final Log logger = LogFactory.getLog(getClass());
@@ -108,6 +115,8 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
private Runnable postBuildAction = () -> {
};
private ServletContext servletContext;
/**
* Creates a new instance
* @param objectPostProcessor the {@link ObjectPostProcessor} to use
@@ -252,6 +261,8 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
* {@link WebSecurityConfigurerAdapter}.
* @param securityInterceptor the {@link FilterSecurityInterceptor} to use
* @return the {@link WebSecurity} for further customizations
* @deprecated Use {@link #privilegeEvaluator(WebInvocationPrivilegeEvaluator)}
* instead
*/
public WebSecurity securityInterceptor(FilterSecurityInterceptor securityInterceptor) {
this.filterSecurityInterceptor = securityInterceptor;
@@ -278,11 +289,24 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
+ ".addSecurityFilterChainBuilder directly");
int chainSize = this.ignoredRequests.size() + this.securityFilterChainBuilders.size();
List<SecurityFilterChain> securityFilterChains = new ArrayList<>(chainSize);
List<RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>>> requestMatcherPrivilegeEvaluatorsEntries = new ArrayList<>();
for (RequestMatcher ignoredRequest : this.ignoredRequests) {
securityFilterChains.add(new DefaultSecurityFilterChain(ignoredRequest));
WebSecurity.this.logger.warn("You are asking Spring Security to ignore " + ignoredRequest
+ ". This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.");
SecurityFilterChain securityFilterChain = new DefaultSecurityFilterChain(ignoredRequest);
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
securityFilterChains.add(securityFilterChainBuilder.build());
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
securityFilterChains.add(securityFilterChain);
requestMatcherPrivilegeEvaluatorsEntries
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
}
if (this.privilegeEvaluator == null) {
this.privilegeEvaluator = new RequestMatcherDelegatingWebInvocationPrivilegeEvaluator(
requestMatcherPrivilegeEvaluatorsEntries);
}
FilterChainProxy filterChainProxy = new FilterChainProxy(securityFilterChains);
if (this.httpFirewall != null) {
@@ -306,6 +330,29 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
return result;
}
private RequestMatcherEntry<List<WebInvocationPrivilegeEvaluator>> getRequestMatcherPrivilegeEvaluatorsEntry(
SecurityFilterChain securityFilterChain) {
List<WebInvocationPrivilegeEvaluator> privilegeEvaluators = new ArrayList<>();
for (Filter filter : securityFilterChain.getFilters()) {
if (filter instanceof FilterSecurityInterceptor) {
DefaultWebInvocationPrivilegeEvaluator defaultWebInvocationPrivilegeEvaluator = new DefaultWebInvocationPrivilegeEvaluator(
(FilterSecurityInterceptor) filter);
defaultWebInvocationPrivilegeEvaluator.setServletContext(this.servletContext);
privilegeEvaluators.add(defaultWebInvocationPrivilegeEvaluator);
continue;
}
if (filter instanceof AuthorizationFilter) {
AuthorizationManager<HttpServletRequest> authorizationManager = ((AuthorizationFilter) filter)
.getAuthorizationManager();
AuthorizationManagerWebInvocationPrivilegeEvaluator evaluator = new AuthorizationManagerWebInvocationPrivilegeEvaluator(
authorizationManager);
evaluator.setServletContext(this.servletContext);
privilegeEvaluators.add(evaluator);
}
}
return new RequestMatcherEntry<>(securityFilterChain::matches, privilegeEvaluators);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.defaultWebSecurityExpressionHandler.setApplicationContext(applicationContext);
@@ -333,6 +380,11 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
}
}
@Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
/**
* An {@link IgnoredRequestConfigurer} that allows optionally configuring the
* {@link MvcRequestMatcher#setMethod(HttpMethod)}
@@ -60,11 +60,11 @@ import org.springframework.util.Assert;
* {@link WebSecurityConfigurer} and exposing it as a {@link Configuration}. This
* configuration is imported when using {@link EnableWebSecurity}.
*
* @see EnableWebSecurity
* @see WebSecurity
* @author Rob Winch
* @author Keesun Baik
* @since 3.2
* @see EnableWebSecurity
* @see WebSecurity
*/
@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
@@ -128,8 +128,8 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
}
/**
* Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary for the JSP
* tag support.
* Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary to evaluate
* privileges for a given web URI
* @return the {@link WebInvocationPrivilegeEvaluator}
*/
@Bean
@@ -87,8 +87,8 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
* org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyClassThatExtendsAbstractHttpConfigurer, sample.OtherThatExtendsAbstractHttpConfigurer
* </pre>
*
* @see EnableWebSecurity
* @author Rob Winch
* @see EnableWebSecurity
*/
@Order(100)
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {
@@ -490,8 +490,8 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
* @return the {@link FeaturePolicyConfig} for additional configuration
* @throws IllegalArgumentException if policyDirectives is {@code null} or empty
* @since 5.1
* @see FeaturePolicyHeaderWriter
* @deprecated Use {@link #permissionsPolicy(Customizer)} instead.
* @seeObjectPostProcessorConfiguration FeaturePolicyHeaderWriter
*/
@Deprecated
public FeaturePolicyConfig featurePolicy(String policyDirectives) {
@@ -49,15 +49,15 @@ import org.springframework.util.Assert;
* <li>{@link ClientRegistrationRepository}</li>
* </ul>
*
* @author Joe Grandja
* @since 5.0
* @see OAuth2AuthorizationRequestRedirectFilter
* @see ClientRegistrationRepository
* @deprecated It is not recommended to use the implicit flow due to the inherent risks of
* returning access tokens in an HTTP redirect without any confirmation that it has been
* received by the client. See reference
* <a target="_blank" href="https://oauth.net/2/grant-types/implicit/">OAuth 2.0 Implicit
* Grant</a>.
* @author Joe Grandja
* @since 5.0
* @see OAuth2AuthorizationRequestRedirectFilter
* @see ClientRegistrationRepository
*/
@Deprecated
public final class ImplicitGrantConfigurer<B extends HttpSecurityBuilder<B>>
@@ -683,10 +683,10 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
/**
* Sets a custom {@link OAuth2User} type and associates it to the provided client
* {@link ClientRegistration#getRegistrationId() registration identifier}.
* @deprecated See {@link CustomUserTypesOAuth2UserService} for alternative usage.
* @param customUserType a custom {@link OAuth2User} type
* @param clientRegistrationId the client registration identifier
* @return the {@link UserInfoEndpointConfig} for further configuration
* @deprecated See {@link CustomUserTypesOAuth2UserService} for alternative usage.
*/
@Deprecated
public UserInfoEndpointConfig customUserType(Class<? extends OAuth2User> customUserType,
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -160,7 +160,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
* @param repo the repository of relying parties
* @return the {@link Saml2LoginConfigurer} for further configuration
*/
public Saml2LoginConfigurer relyingPartyRegistrationRepository(RelyingPartyRegistrationRepository repo) {
public Saml2LoginConfigurer<B> relyingPartyRegistrationRepository(RelyingPartyRegistrationRepository repo) {
this.relyingPartyRegistrationRepository = repo;
return this;
}
@@ -172,10 +172,19 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
return this;
}
/**
* Specifies the URL to validate the credentials. If specified a custom URL, consider
* specifying a custom {@link AuthenticationConverter} via
* {@link #authenticationConverter(AuthenticationConverter)}, since the default
* {@link AuthenticationConverter} implementation relies on the
* <code>{registrationId}</code> path variable to be present in the URL
* @param loginProcessingUrl the URL to validate the credentials
* @return the {@link Saml2LoginConfigurer} for additional customization
* @see Saml2WebSsoAuthenticationFilter#DEFAULT_FILTER_PROCESSES_URI
*/
@Override
public Saml2LoginConfigurer<B> loginProcessingUrl(String loginProcessingUrl) {
Assert.hasText(loginProcessingUrl, "loginProcessingUrl cannot be empty");
Assert.state(loginProcessingUrl.contains("{registrationId}"), "{registrationId} path variable is required");
this.loginProcessingUrl = loginProcessingUrl;
return this;
}
@@ -254,6 +263,8 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
private AuthenticationConverter getAuthenticationConverter(B http) {
if (this.authenticationConverter == null) {
Assert.state(this.loginProcessingUrl.contains("{registrationId}"),
"loginProcessingUrl must contain {registrationId} path variable");
return new Saml2AuthenticationTokenConverter(
new DefaultRelyingPartyRegistrationResolver(this.relyingPartyRegistrationRepository));
}
@@ -265,8 +276,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
if (version != null) {
return version;
}
return Version.class.getModule().getDescriptor().version().map(Object::toString)
.orElseThrow(() -> new IllegalStateException("cannot determine OpenSAML version"));
return Version.getVersion();
}
private void registerDefaultAuthenticationProvider(B http) {
@@ -37,9 +37,9 @@ import org.springframework.web.servlet.support.RequestDataValueProcessor;
* {@link AuthenticationPrincipalArgumentResolver} as a
* {@link HandlerMethodArgumentResolver}.
*
* @deprecated This is applied internally using SpringWebMvcImportSelector
* @author Rob Winch
* @since 3.2
* @deprecated This is applied internally using SpringWebMvcImportSelector
*/
@Deprecated
@Configuration(proxyBeanMethods = false)
@@ -79,8 +79,8 @@ import org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsSe
* }
* </pre>
*
* @since 4.0
* @author Rob Winch
* @since 4.0
*/
@Order(Ordered.HIGHEST_PRECEDENCE + 100)
@Import(ObjectPostProcessorConfiguration.class)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.messaging.Message;
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
import org.springframework.security.config.util.SpringSecurityVersions;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.util.ClassUtils;
@@ -147,4 +148,11 @@ public class SecurityNamespaceHandlerTests {
// should load just fine since no websocket block
}
@Test
public void configureWhenOldVersionThenErrorMessageContainsCorrectVersion() {
assertThatExceptionOfType(BeanDefinitionParsingException.class)
.isThrownBy(() -> new InMemoryXmlApplicationContext(XML_AUTHENTICATION_MANAGER, "3.0", null))
.withMessageContaining(SpringSecurityVersions.getCurrentXsdVersionFromSpringSchemas());
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -33,6 +33,7 @@ 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.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
@@ -61,7 +62,7 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler;
import org.springframework.test.web.servlet.MockMvc;
@@ -83,6 +84,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
* @author Rob Winch
* @author Joe Grandja
* @author Evgeniy Cheban
* @author Marcus Da Coregio
*/
public class WebSecurityConfigurationTests {
@@ -218,10 +220,10 @@ public class WebSecurityConfigurationTests {
}
@Test
public void loadConfigWhenDefaultWebInvocationPrivilegeEvaluatorThenDefaultIsRegistered() {
public void loadConfigWhenDefaultWebInvocationPrivilegeEvaluatorThenRequestMatcherIsRegistered() {
this.spring.register(WebInvocationPrivilegeEvaluatorDefaultsConfig.class).autowire();
assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
.isInstanceOf(DefaultWebInvocationPrivilegeEvaluator.class);
.isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class);
}
@Test
@@ -229,7 +231,7 @@ public class WebSecurityConfigurationTests {
this.spring.register(AuthorizeRequestsFilterChainConfig.class).autowire();
assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
.isInstanceOf(DefaultWebInvocationPrivilegeEvaluator.class);
.isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class);
}
// SEC-2303
@@ -375,6 +377,69 @@ public class WebSecurityConfigurationTests {
assertThat(filterChains.get(1).matches(request)).isTrue();
}
@Test
public void loadConfigWhenTwoSecurityFilterChainsThenRequestMatcherDelegatingWebInvocationPrivilegeEvaluator() {
this.spring.register(TwoSecurityFilterChainConfig.class).autowire();
assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
.isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class);
}
@Test
public void loadConfigWhenTwoSecurityFilterChainDebugThenRequestMatcherDelegatingWebInvocationPrivilegeEvaluator() {
this.spring.register(TwoSecurityFilterChainConfig.class).autowire();
assertThat(this.spring.getContext().getBean(WebInvocationPrivilegeEvaluator.class))
.isInstanceOf(RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class);
}
// gh-10554
@Test
public void loadConfigWhenMultipleSecurityFilterChainsThenWebInvocationPrivilegeEvaluatorApplySecurity() {
this.spring.register(MultipleSecurityFilterChainConfig.class).autowire();
WebInvocationPrivilegeEvaluator privilegeEvaluator = this.spring.getContext()
.getBean(WebInvocationPrivilegeEvaluator.class);
assertUserPermissions(privilegeEvaluator);
assertAdminPermissions(privilegeEvaluator);
assertAnotherUserPermission(privilegeEvaluator);
}
// gh-10554
@Test
public void loadConfigWhenMultipleSecurityFilterChainAndIgnoringThenWebInvocationPrivilegeEvaluatorAcceptsNullAuthenticationOnIgnored() {
this.spring.register(MultipleSecurityFilterChainIgnoringConfig.class).autowire();
WebInvocationPrivilegeEvaluator privilegeEvaluator = this.spring.getContext()
.getBean(WebInvocationPrivilegeEvaluator.class);
assertUserPermissions(privilegeEvaluator);
assertAdminPermissions(privilegeEvaluator);
assertAnotherUserPermission(privilegeEvaluator);
// null authentication
assertThat(privilegeEvaluator.isAllowed("/user", null)).isFalse();
assertThat(privilegeEvaluator.isAllowed("/admin", null)).isFalse();
assertThat(privilegeEvaluator.isAllowed("/another", null)).isFalse();
assertThat(privilegeEvaluator.isAllowed("/ignoring1", null)).isTrue();
assertThat(privilegeEvaluator.isAllowed("/ignoring1/child", null)).isTrue();
}
private void assertAnotherUserPermission(WebInvocationPrivilegeEvaluator privilegeEvaluator) {
Authentication anotherUser = new TestingAuthenticationToken("anotherUser", "password", "ROLE_ANOTHER");
assertThat(privilegeEvaluator.isAllowed("/user", anotherUser)).isFalse();
assertThat(privilegeEvaluator.isAllowed("/admin", anotherUser)).isFalse();
assertThat(privilegeEvaluator.isAllowed("/another", anotherUser)).isTrue();
}
private void assertAdminPermissions(WebInvocationPrivilegeEvaluator privilegeEvaluator) {
Authentication admin = new TestingAuthenticationToken("admin", "password", "ROLE_ADMIN");
assertThat(privilegeEvaluator.isAllowed("/user", admin)).isFalse();
assertThat(privilegeEvaluator.isAllowed("/admin", admin)).isTrue();
assertThat(privilegeEvaluator.isAllowed("/another", admin)).isTrue();
}
private void assertUserPermissions(WebInvocationPrivilegeEvaluator privilegeEvaluator) {
Authentication user = new TestingAuthenticationToken("user", "password", "ROLE_USER");
assertThat(privilegeEvaluator.isAllowed("/user", user)).isTrue();
assertThat(privilegeEvaluator.isAllowed("/admin", user)).isFalse();
assertThat(privilegeEvaluator.isAllowed("/another", user)).isTrue();
}
@EnableWebSecurity
@Import(AuthenticationTestConfiguration.class)
static class SortedWebSecurityConfigurerAdaptersConfig {
@@ -1008,4 +1073,125 @@ public class WebSecurityConfigurationTests {
}
@EnableWebSecurity
static class TwoSecurityFilterChainConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain path1(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers((requests) -> requests.antMatchers("/path1/**"))
.authorizeRequests((requests) -> requests.anyRequest().authenticated());
// @formatter:on
return http.build();
}
@Bean
@Order(Ordered.LOWEST_PRECEDENCE)
public SecurityFilterChain permitAll(HttpSecurity http) throws Exception {
http.authorizeRequests((requests) -> requests.anyRequest().permitAll());
return http.build();
}
}
@EnableWebSecurity(debug = true)
static class TwoSecurityFilterChainDebugConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain path1(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers((requests) -> requests.antMatchers("/path1/**"))
.authorizeRequests((requests) -> requests.anyRequest().authenticated());
// @formatter:on
return http.build();
}
@Bean
@Order(Ordered.LOWEST_PRECEDENCE)
public SecurityFilterChain permitAll(HttpSecurity http) throws Exception {
http.authorizeRequests((requests) -> requests.anyRequest().permitAll());
return http.build();
}
}
@EnableWebSecurity
@Import(AuthenticationTestConfiguration.class)
static class MultipleSecurityFilterChainConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain notAuthorized(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers((requests) -> requests.antMatchers("/user"))
.authorizeRequests((requests) -> requests.anyRequest().hasRole("USER"));
// @formatter:on
return http.build();
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
public SecurityFilterChain path1(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers((requests) -> requests.antMatchers("/admin"))
.authorizeRequests((requests) -> requests.anyRequest().hasRole("ADMIN"));
// @formatter:on
return http.build();
}
@Bean
@Order(Ordered.LOWEST_PRECEDENCE)
public SecurityFilterChain permitAll(HttpSecurity http) throws Exception {
http.authorizeRequests((requests) -> requests.anyRequest().permitAll());
return http.build();
}
}
@EnableWebSecurity
@Import(AuthenticationTestConfiguration.class)
static class MultipleSecurityFilterChainIgnoringConfig {
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().antMatchers("/ignoring1/**");
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain notAuthorized(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers((requests) -> requests.antMatchers("/user"))
.authorizeRequests((requests) -> requests.anyRequest().hasRole("USER"));
// @formatter:on
return http.build();
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
public SecurityFilterChain admin(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers((requests) -> requests.antMatchers("/admin"))
.authorizeRequests((requests) -> requests.anyRequest().hasRole("ADMIN"));
// @formatter:on
return http.build();
}
@Bean
@Order(Ordered.LOWEST_PRECEDENCE)
public SecurityFilterChain permitAll(HttpSecurity http) throws Exception {
http.authorizeRequests((requests) -> requests.anyRequest().permitAll());
return http.build();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,16 +21,21 @@ import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.test.SpringTestRule;
import org.springframework.security.web.PortMapperImpl;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.access.channel.InsecureChannelProcessor;
import org.springframework.security.web.access.channel.SecureChannelProcessor;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.spy;
@@ -92,6 +97,24 @@ public class ChannelSecurityConfigurerTests {
this.mvc.perform(get("/")).andExpect(redirectedUrl("https://localhost/"));
}
// gh-10956
@Test
public void requestWhenRequiresChannelWithMultiMvcMatchersThenRedirectsToHttps() throws Exception {
this.spring.register(RequiresChannelMultiMvcMatchersConfig.class).autowire();
this.mvc.perform(get("/test-1")).andExpect(redirectedUrl("https://localhost/test-1"));
this.mvc.perform(get("/test-2")).andExpect(redirectedUrl("https://localhost/test-2"));
this.mvc.perform(get("/test-3")).andExpect(redirectedUrl("https://localhost/test-3"));
}
// gh-10956
@Test
public void requestWhenRequiresChannelWithMultiMvcMatchersInLambdaThenRedirectsToHttps() throws Exception {
this.spring.register(RequiresChannelMultiMvcMatchersInLambdaConfig.class).autowire();
this.mvc.perform(get("/test-1")).andExpect(redirectedUrl("https://localhost/test-1"));
this.mvc.perform(get("/test-2")).andExpect(redirectedUrl("https://localhost/test-2"));
this.mvc.perform(get("/test-3")).andExpect(redirectedUrl("https://localhost/test-3"));
}
@EnableWebSecurity
static class ObjectPostProcessorConfig extends WebSecurityConfigurerAdapter {
@@ -154,4 +177,59 @@ public class ChannelSecurityConfigurerTests {
}
@EnableWebSecurity
@EnableWebMvc
static class RequiresChannelMultiMvcMatchersConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.portMapper()
.portMapper(new PortMapperImpl())
.and()
.requiresChannel()
.mvcMatchers("/test-1")
.requiresSecure()
.mvcMatchers("/test-2")
.requiresSecure()
.mvcMatchers("/test-3")
.requiresSecure()
.anyRequest()
.requiresInsecure();
// @formatter:on
return http.build();
}
}
@EnableWebSecurity
@EnableWebMvc
static class RequiresChannelMultiMvcMatchersInLambdaConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.portMapper((port) -> port
.portMapper(new PortMapperImpl())
)
.requiresChannel((channel) -> channel
.mvcMatchers("/test-1")
.requiresSecure()
.mvcMatchers("/test-2")
.requiresSecure()
.mvcMatchers("/test-3")
.requiresSecure()
.anyRequest()
.requiresInsecure()
);
// @formatter:on
return http.build();
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,7 +23,10 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
@@ -33,6 +36,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
@@ -167,6 +171,38 @@ public class HttpSecurityRequestMatchersTests {
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@Test
public void requestMatcherWhenMultiMvcMatcherInLambdaThenAllPathsAreDenied() throws Exception {
loadConfig(MultiMvcMatcherInLambdaConfig.class);
this.request.setRequestURI("/test-1");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setRequestURI("/test-2");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setRequestURI("/test-3");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
}
@Test
public void requestMatcherWhenMultiMvcMatcherThenAllPathsAreDenied() throws Exception {
loadConfig(MultiMvcMatcherConfig.class);
this.request.setRequestURI("/test-1");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setRequestURI("/test-2");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setRequestURI("/test-3");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
}
public void loadConfig(Class<?>... configs) {
this.context = new AnnotationConfigWebApplicationContext();
this.context.register(configs);
@@ -175,6 +211,101 @@ public class HttpSecurityRequestMatchersTests {
this.context.getAutowireCapableBeanFactory().autowireBean(this);
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MultiMvcMatcherInLambdaConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
SecurityFilterChain first(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers((requests) -> requests
.mvcMatchers("/test-1")
.mvcMatchers("/test-2")
.mvcMatchers("/test-3")
)
.authorizeRequests((authorize) -> authorize.anyRequest().denyAll())
.httpBasic(withDefaults());
// @formatter:on
return http.build();
}
@Bean
SecurityFilterChain second(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers((requests) -> requests
.mvcMatchers("/test-1")
)
.authorizeRequests((authorize) -> authorize
.anyRequest().permitAll()
);
// @formatter:on
return http.build();
}
@RestController
static class PathController {
@RequestMapping({ "/test-1", "/test-2", "/test-3" })
String path() {
return "path";
}
}
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class MultiMvcMatcherConfig {
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
SecurityFilterChain first(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers()
.mvcMatchers("/test-1")
.mvcMatchers("/test-2")
.mvcMatchers("/test-3")
.and()
.authorizeRequests()
.anyRequest().denyAll()
.and()
.httpBasic(withDefaults());
// @formatter:on
return http.build();
}
@Bean
SecurityFilterChain second(HttpSecurity http) throws Exception {
// @formatter:off
http
.requestMatchers()
.mvcMatchers("/test-1")
.and()
.authorizeRequests()
.anyRequest().permitAll();
// @formatter:on
return http.build();
}
@RestController
static class PathController {
@RequestMapping({ "/test-1", "/test-2", "/test-3" })
String path() {
return "path";
}
}
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
@@ -21,11 +21,13 @@ import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.security.auth.x500.X500Principal;
import javax.servlet.http.HttpServletRequest;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.junit.Rule;
import org.junit.Test;
import sun.security.x509.X500Name;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
@@ -239,12 +241,8 @@ public class NamespaceHttpX509Tests {
}
private String extractCommonName(X509Certificate certificate) {
try {
return ((X500Name) certificate.getSubjectDN()).getCommonName();
}
catch (Exception ex) {
throw new IllegalArgumentException(ex);
}
X500Principal principal = certificate.getSubjectX500Principal();
return new X500Name(principal.getName()).getRDNs(BCStyle.CN)[0].getFirst().getValue().toString();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 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,6 +16,8 @@
package org.springframework.security.config.annotation.web.configurers;
import java.util.Base64;
import javax.servlet.http.HttpServletResponse;
import org.junit.After;
@@ -23,16 +25,24 @@ import org.junit.Before;
import org.junit.Test;
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.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
@@ -125,6 +135,35 @@ public class UrlAuthorizationConfigurerTests {
loadConfig(AnonymousUrlAuthorizationConfig.class);
}
// gh-10956
@Test
public void multiMvcMatchersConfig() throws Exception {
loadConfig(MultiMvcMatcherConfig.class);
this.request.addHeader("Authorization",
"Basic " + new String(Base64.getEncoder().encode("user:password".getBytes())));
this.request.setRequestURI("/test-1");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
setup();
this.request.addHeader("Authorization",
"Basic " + new String(Base64.getEncoder().encode("user:password".getBytes())));
this.request.setRequestURI("/test-2");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
setup();
this.request.addHeader("Authorization",
"Basic " + new String(Base64.getEncoder().encode("user:password".getBytes())));
this.request.setRequestURI("/test-3");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_FORBIDDEN);
setup();
this.request.addHeader("Authorization",
"Basic " + new String(Base64.getEncoder().encode("user:password".getBytes())));
this.request.setRequestURI("/test-x");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
public void loadConfig(Class<?>... configs) {
this.context = new AnnotationConfigWebApplicationContext();
this.context.register(configs);
@@ -228,4 +267,41 @@ public class UrlAuthorizationConfigurerTests {
}
@EnableWebSecurity
@EnableWebMvc
static class MultiMvcMatcherConfig {
@Bean
SecurityFilterChain security(HttpSecurity http, ApplicationContext context) throws Exception {
// @formatter:off
http
.httpBasic(Customizer.withDefaults())
.apply(new UrlAuthorizationConfigurer<>(context)).getRegistry()
.mvcMatchers("/test-1").hasRole("ADMIN")
.mvcMatchers("/test-2").hasRole("ADMIN")
.mvcMatchers("/test-3").hasRole("ADMIN")
.anyRequest().hasRole("USER");
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder().username("user").password("password").roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
}
@RestController
static class PathController {
@RequestMapping({ "/test-1", "/test-2", "/test-3", "/test-x" })
String path() {
return "path";
}
}
}
}
@@ -37,6 +37,7 @@ import org.mockito.ArgumentCaptor;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.AuthnRequest;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
@@ -49,6 +50,7 @@ import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -77,7 +79,9 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestContextResolver;
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.context.HttpRequestResponseHolder;
@@ -91,6 +95,7 @@ import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.given;
@@ -117,6 +122,8 @@ public class Saml2LoginConfigurerTests {
private static final String SIGNED_RESPONSE = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9ycC5leGFtcGxlLm9yZy9hY3MiIElEPSJfYzE3MzM2YTAtNTM1My00MTQ5LWI3MmMtMDNkOWY5YWYzMDdlIiBJc3N1ZUluc3RhbnQ9IjIwMjAtMDgtMDRUMjI6MDQ6NDUuMDE2WiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5hcC1lbnRpdHktaWQ8L3NhbWwyOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KPGRzOlNpZ25lZEluZm8+CjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CjxkczpSZWZlcmVuY2UgVVJJPSIjX2MxNzMzNmEwLTUzNTMtNDE0OS1iNzJjLTAzZDlmOWFmMzA3ZSI+CjxkczpUcmFuc2Zvcm1zPgo8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz4KPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgo8L2RzOlRyYW5zZm9ybXM+CjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz4KPGRzOkRpZ2VzdFZhbHVlPjYzTmlyenFzaDVVa0h1a3NuRWUrM0hWWU5aYWFsQW1OQXFMc1lGMlRuRDA9PC9kczpEaWdlc3RWYWx1ZT4KPC9kczpSZWZlcmVuY2U+CjwvZHM6U2lnbmVkSW5mbz4KPGRzOlNpZ25hdHVyZVZhbHVlPgpLMVlvWWJVUjBTclY4RTdVMkhxTTIvZUNTOTNoV25mOExnNnozeGZWMUlyalgzSXhWYkNvMVlYcnRBSGRwRVdvYTJKKzVOMmFNbFBHJiMxMzsKN2VpbDBZRC9xdUVRamRYbTNwQTBjZmEvY25pa2RuKzVhbnM0ZWQwanU1amo2dkpvZ2w2Smt4Q25LWUpwTU9HNzhtampmb0phengrWCYjMTM7CkM2NktQVStBYUdxeGVwUEQ1ZlhRdTFKSy9Jb3lBaitaa3k4Z2Jwc3VyZHFCSEJLRWxjdnVOWS92UGY0OGtBeFZBKzdtRGhNNUMvL1AmIzEzOwp0L084Y3NZYXB2UjZjdjZrdk45QXZ1N3FRdm9qVk1McHVxZWNJZDJwTUVYb0NSSnE2Nkd4MStNTUVPeHVpMWZZQlRoMEhhYjRmK3JyJiMxMzsKOEY2V1NFRC8xZllVeHliRkJqZ1Q4d2lEWHFBRU8wSVY4ZWRQeEE9PQo8L2RzOlNpZ25hdHVyZVZhbHVlPgo8L2RzOlNpZ25hdHVyZT48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iQWUzZjQ5OGI4LTliMTctNDA3OC05ZDM1LTg2YTA4NDA4NDk5NSIgSXNzdWVJbnN0YW50PSIyMDIwLTA4LTA0VDIyOjA0OjQ1LjA3N1oiIFZlcnNpb249IjIuMCI+PHNhbWwyOklzc3Vlcj5hcC1lbnRpdHktaWQ8L3NhbWwyOklzc3Vlcj48c2FtbDI6U3ViamVjdD48c2FtbDI6TmFtZUlEPnRlc3RAc2FtbC51c2VyPC9zYW1sMjpOYW1lSUQ+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90QmVmb3JlPSIyMDIwLTA4LTA0VDIxOjU5OjQ1LjA5MFoiIE5vdE9uT3JBZnRlcj0iMjA0MC0wNy0zMFQyMjowNTowNi4wODhaIiBSZWNpcGllbnQ9Imh0dHBzOi8vcnAuZXhhbXBsZS5vcmcvYWNzIi8+PC9zYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDI6U3ViamVjdD48c2FtbDI6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMjAtMDgtMDRUMjE6NTk6NDUuMDgwWiIgTm90T25PckFmdGVyPSIyMDQwLTA3LTMwVDIyOjA1OjA2LjA4N1oiLz48L3NhbWwyOkFzc2VydGlvbj48L3NhbWwycDpSZXNwb25zZT4=";
private static final AuthenticationConverter AUTHENTICATION_CONVERTER = mock(AuthenticationConverter.class);
@Autowired
private ConfigurableApplicationContext context;
@@ -216,7 +223,7 @@ public class Saml2LoginConfigurerTests {
public void authenticateWithInvalidDeflatedSAMLResponseThenFailureHandlerUses() throws Exception {
this.spring.register(CustomAuthenticationFailureHandler.class).autowire();
byte[] invalidDeflated = "invalid".getBytes();
String encoded = Saml2Utils.samlEncode(invalidDeflated);
String encoded = Saml2Utils.samlEncodeNotRfc2045(invalidDeflated);
MockHttpServletRequestBuilder request = get("/login/saml2/sso/registration-id").queryParam("SAMLResponse",
encoded);
this.mvc.perform(request);
@@ -230,6 +237,33 @@ public class Saml2LoginConfigurerTests {
assertThat(exception.getCause()).isInstanceOf(IOException.class);
}
@Test
public void saml2LoginWhenLoginProcessingUrlWithoutRegistrationIdAndDefaultAuthenticationConverterThenValidates() {
assertThatExceptionOfType(BeanCreationException.class)
.isThrownBy(() -> this.spring.register(CustomLoginProcessingUrlDefaultAuthenticationConverter.class)
.autowire())
.havingRootCause().isInstanceOf(IllegalStateException.class)
.withMessage("loginProcessingUrl must contain {registrationId} path variable");
}
@Test
public void authenticateWhenCustomLoginProcessingUrlAndCustomAuthenticationConverterThenAuthenticate()
throws Exception {
this.spring.register(CustomLoginProcessingUrlCustomAuthenticationConverter.class).autowire();
RelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.noCredentials()
.assertingPartyDetails((party) -> party.verificationX509Credentials(
(c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())))
.build();
String response = new String(Saml2Utils.samlDecode(SIGNED_RESPONSE));
given(AUTHENTICATION_CONVERTER.convert(any(HttpServletRequest.class)))
.willReturn(new Saml2AuthenticationToken(relyingPartyRegistration, response));
// @formatter:off
MockHttpServletRequestBuilder request = post("/my/custom/url").param("SAMLResponse", SIGNED_RESPONSE);
// @formatter:on
this.mvc.perform(request).andExpect(redirectedUrl("/"));
verify(AUTHENTICATION_CONVERTER).convert(any(HttpServletRequest.class));
}
private void validateSaml2WebSsoAuthenticationFilterConfiguration() {
// get the OpenSamlAuthenticationProvider
Saml2WebSsoAuthenticationFilter filter = getSaml2SsoFilter(this.springSecurityFilterChain);
@@ -337,10 +371,10 @@ public class Saml2LoginConfigurerTests {
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests((authz) -> authz
.anyRequest().authenticated()
)
.saml2Login(withDefaults());
.authorizeRequests((authz) -> authz
.anyRequest().authenticated()
)
.saml2Login(withDefaults());
// @formatter:on
}
@@ -359,11 +393,11 @@ public class Saml2LoginConfigurerTests {
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests((authz) -> authz
.anyRequest().authenticated()
)
.saml2Login((saml2) -> {
});
.authorizeRequests((authz) -> authz
.anyRequest().authenticated()
)
.saml2Login((saml2) -> {
});
// @formatter:on
}
@@ -395,6 +429,62 @@ public class Saml2LoginConfigurerTests {
}
@EnableWebSecurity
@Import(Saml2LoginConfigBeans.class)
static class CustomAuthenticationConverterBean {
private final Saml2AuthenticationTokenConverter authenticationConverter = mock(
Saml2AuthenticationTokenConverter.class);
@Bean
SecurityFilterChain app(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated())
.saml2Login(Customizer.withDefaults());
return http.build();
}
@Bean
Saml2AuthenticationTokenConverter authenticationConverter() {
return this.authenticationConverter;
}
}
@EnableWebSecurity
@Import(Saml2LoginConfigBeans.class)
static class CustomLoginProcessingUrlDefaultAuthenticationConverter {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests((authz) -> authz.anyRequest().authenticated())
.saml2Login((saml2) -> saml2.loginProcessingUrl("/my/custom/url"));
// @formatter:on
return http.build();
}
}
@EnableWebSecurity
@Import(Saml2LoginConfigBeans.class)
static class CustomLoginProcessingUrlCustomAuthenticationConverter {
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeRequests((authz) -> authz.anyRequest().authenticated())
.saml2Login((saml2) -> saml2
.loginProcessingUrl("/my/custom/url")
.authenticationConverter(AUTHENTICATION_CONVERTER)
);
// @formatter:on
return http.build();
}
}
static class Saml2LoginConfigBeans {
@Bean
@@ -1,5 +1,5 @@
/*
* Copyright 2009-2020 the original author or authors.
* Copyright 2009-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ import org.springframework.security.util.InMemoryResource;
/**
* @author Luke Taylor
* @author Eddú Meléndez
* @author Emil Sierżęga
*/
public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext {
@@ -42,7 +43,7 @@ public class InMemoryXmlApplicationContext extends AbstractXmlApplicationContext
+ "http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context-2.5.xsd\n"
+ "http://www.springframework.org/schema/security https://www.springframework.org/schema/security/spring-security-";
static final String BEANS_CLOSE = "</b:beans>\n";
static final String SPRING_SECURITY_VERSION = "5.4";
static final String SPRING_SECURITY_VERSION = SpringSecurityVersions.getCurrentXsdVersionFromSpringSchemas();
Resource inMemoryXml;
@@ -0,0 +1,57 @@
/*
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.util;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.springframework.security.core.SpringSecurityCoreVersion;
/**
* For computing different Spring Security versions
*/
public final class SpringSecurityVersions {
static final Pattern SCHEMA_VERSION_PATTERN = Pattern.compile("\\d+\\.\\d+(\\.\\d+)?");
public static String getCurrentXsdVersionFromSpringSchemas() {
Properties properties = new Properties();
try (InputStream is = SpringSecurityCoreVersion.class.getClassLoader()
.getResourceAsStream("META-INF/spring.schemas")) {
properties.load(is);
}
catch (IOException ex) {
throw new RuntimeException("Could not read 'META-INF/spring.schemas'", ex);
}
String inPackageLocation = properties
.getProperty("https://www.springframework.org/schema/security/spring-security.xsd");
Matcher matcher = SCHEMA_VERSION_PATTERN.matcher(inPackageLocation);
if (matcher.find()) {
return matcher.group(0);
}
throw new IllegalStateException("Failed to find version");
}
private SpringSecurityVersions() {
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2016 the original author or authors.
* Copyright 2010-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -266,7 +266,7 @@ public abstract class AbstractJaasAuthenticationProvider implements Authenticati
* Publishes the {@link JaasAuthenticationFailedEvent}. Can be overridden by
* subclasses for different functionality
* @param token The authentication token being processed
* @param ase The excetion that caused the authentication failure
* @param ase The exception that caused the authentication failure
*/
protected void publishFailureEvent(UsernamePasswordAuthenticationToken token, AuthenticationException ase) {
if (this.applicationEventPublisher != null) {
@@ -224,7 +224,7 @@ public class JaasAuthenticationProvider extends AbstractJaasAuthenticationProvid
* Publishes the {@link JaasAuthenticationFailedEvent}. Can be overridden by
* subclasses for different functionality
* @param token The authentication token being processed
* @param ase The excetion that caused the authentication failure
* @param ase The exception that caused the authentication failure
*/
@Override
protected void publishFailureEvent(UsernamePasswordAuthenticationToken token, AuthenticationException ase) {
@@ -40,9 +40,9 @@ import org.springframework.security.core.GrantedAuthority;
* <i>Note: This class will save full class name into a property called @class</i>
*
* @author Jitendra Singh
* @since 4.2
* @see CoreJackson2Module
* @see SecurityJackson2Modules
* @since 4.2
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,
@@ -36,8 +36,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
* called @class</i> <i>The cause and stackTrace are ignored in the serialization.</i>
*
* @author Yannick Lombardi
* @see CoreJackson2Module
* @since 5.0
* @see CoreJackson2Module
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonIgnoreProperties(ignoreUnknown = true, value = { "cause", "stackTrace" })
@@ -44,8 +44,8 @@ import org.springframework.security.core.userdetails.User;
* of all security modules.</b>
*
* @author Jitendra Singh.
* @see SecurityJackson2Modules
* @since 4.2
* @see SecurityJackson2Modules
*/
@SuppressWarnings("serial")
public class CoreJackson2Module extends SimpleModule {
@@ -47,9 +47,9 @@ import org.springframework.security.core.GrantedAuthority;
* called @class</i>
*
* @author Jitendra Singh
* @since 4.2
* @see CoreJackson2Module
* @see SecurityJackson2Modules
* @since 4.2
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
@@ -32,9 +32,9 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
* </pre>
*
* @author Jitendra Singh
* @since 4.2
* @see CoreJackson2Module
* @see SecurityJackson2Modules
* @since 4.2
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.NONE,
@@ -32,10 +32,10 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
* </pre>
*
* @author Rob Winch
* @since 5.0.2
* @see UnmodifiableListDeserializer
* @see CoreJackson2Module
* @see SecurityJackson2Modules
* @since 5.0.2
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonDeserialize(using = UnmodifiableListDeserializer.class)
@@ -32,10 +32,10 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
* </pre>
*
* @author Jitendra Singh
* @since 4.2
* @see UnmodifiableSetDeserializer
* @see CoreJackson2Module
* @see SecurityJackson2Modules
* @since 4.2
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonDeserialize(using = UnmodifiableSetDeserializer.class)
@@ -37,10 +37,10 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
* </pre>
*
* @author Jitendra Singh
* @since 4.2
* @see UserDeserializer
* @see CoreJackson2Module
* @see SecurityJackson2Modules
* @since 4.2
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonDeserialize(using = UserDeserializer.class)
@@ -39,9 +39,9 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
* </pre>
*
* @author Jitendra Singh
* @since 4.2
* @see CoreJackson2Module
* @see SecurityJackson2Modules
* @since 4.2
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class")
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
@@ -526,35 +526,47 @@ public class BCrypt {
* @param safety bit 16 is set when the safety measure is requested
* @return an array containing the binary hashed password
*/
private byte[] crypt_raw(byte password[], byte salt[], int log_rounds, boolean sign_ext_bug, int safety) {
int rounds, i, j;
private byte[] crypt_raw(byte password[], byte salt[], int log_rounds, boolean sign_ext_bug, int safety,
boolean for_check) {
int cdata[] = bf_crypt_ciphertext.clone();
int clen = cdata.length;
byte ret[];
long rounds;
if (log_rounds < 4 || log_rounds > 31) {
throw new IllegalArgumentException("Bad number of rounds");
if (!for_check) {
throw new IllegalArgumentException("Bad number of rounds");
}
if (log_rounds != 0) {
throw new IllegalArgumentException("Bad number of rounds");
}
rounds = 0;
}
rounds = 1 << log_rounds;
else {
rounds = roundsForLogRounds(log_rounds);
if (rounds < 16 || rounds > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Bad number of rounds");
}
}
if (salt.length != BCRYPT_SALT_LEN) {
throw new IllegalArgumentException("Bad salt length");
}
init_key();
ekskey(salt, password, sign_ext_bug, safety);
for (i = 0; i < rounds; i++) {
for (int i = 0; i < rounds; i++) {
key(password, sign_ext_bug, safety);
key(salt, false, safety);
}
for (i = 0; i < 64; i++) {
for (j = 0; j < (clen >> 1); j++) {
for (int i = 0; i < 64; i++) {
for (int j = 0; j < (clen >> 1); j++) {
encipher(cdata, j << 1);
}
}
ret = new byte[clen * 4];
for (i = 0, j = 0; i < clen; i++) {
byte[] ret = new byte[clen * 4];
for (int i = 0, j = 0; i < clen; i++) {
ret[j++] = (byte) ((cdata[i] >> 24) & 0xff);
ret[j++] = (byte) ((cdata[i] >> 16) & 0xff);
ret[j++] = (byte) ((cdata[i] >> 8) & 0xff);
@@ -563,6 +575,10 @@ public class BCrypt {
return ret;
}
private static String hashpwforcheck(byte[] passwordb, String salt) {
return hashpw(passwordb, salt, true);
}
/**
* Hash a password using the OpenBSD bcrypt scheme
* @param password the password to hash
@@ -584,6 +600,10 @@ public class BCrypt {
* @return the hashed password
*/
public static String hashpw(byte passwordb[], String salt) {
return hashpw(passwordb, salt, false);
}
private static String hashpw(byte passwordb[], String salt, boolean for_check) {
BCrypt B;
String real_salt;
byte saltb[], hashed[];
@@ -633,7 +653,7 @@ public class BCrypt {
}
B = new BCrypt();
hashed = B.crypt_raw(passwordb, saltb, rounds, minor == 'x', minor == 'a' ? 0x10000 : 0);
hashed = B.crypt_raw(passwordb, saltb, rounds, minor == 'x', minor == 'a' ? 0x10000 : 0, for_check);
rs.append("$2");
if (minor >= 'a') {
@@ -740,7 +760,8 @@ public class BCrypt {
* @return true if the passwords match, false otherwise
*/
public static boolean checkpw(String plaintext, String hashed) {
return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed));
byte[] passwordb = plaintext.getBytes(StandardCharsets.UTF_8);
return equalsNoEarlyReturn(hashed, hashpwforcheck(passwordb, hashed));
}
/**
@@ -751,7 +772,7 @@ public class BCrypt {
* @since 5.3
*/
public static boolean checkpw(byte[] passwordb, String hashed) {
return equalsNoEarlyReturn(hashed, hashpw(passwordb, hashed));
return equalsNoEarlyReturn(hashed, hashpwforcheck(passwordb, hashed));
}
static boolean equalsNoEarlyReturn(String a, String b) {
@@ -208,4 +208,18 @@ public class BCryptPasswordEncoderTests {
assertThatIllegalArgumentException().isThrownBy(() -> encoder.matches(null, "does-not-matter"));
}
@Test
public void upgradeWhenNoRoundsThenTrue() {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
assertThat(encoder.upgradeEncoding("$2a$00$9N8N35BVs5TLqGL3pspAte5OWWA2a2aZIs.EGp7At7txYakFERMue")).isTrue();
}
@Test
public void checkWhenNoRoundsThenTrue() {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
assertThat(encoder.matches("password", "$2a$00$9N8N35BVs5TLqGL3pspAte5OWWA2a2aZIs.EGp7At7txYakFERMue"))
.isTrue();
assertThat(encoder.matches("wrong", "$2a$00$9N8N35BVs5TLqGL3pspAte5OWWA2a2aZIs.EGp7At7txYakFERMue")).isFalse();
}
}
@@ -456,4 +456,11 @@ public class BCryptTests {
assertThat(BCrypt.equalsNoEarlyReturn("test", "pass")).isFalse();
}
@Test
public void checkpwWhenZeroRoundsThenMatches() {
String password = "$2a$00$9N8N35BVs5TLqGL3pspAte5OWWA2a2aZIs.EGp7At7txYakFERMue";
assertThat(BCrypt.checkpw("password", password)).isTrue();
assertThat(BCrypt.checkpw("wrong", password)).isFalse();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -129,7 +129,7 @@ public class DelegatingPasswordEncoderTests {
}
@Test
public void matchesWhenNoClosingPrefixStringThenIllegalArgumentExcetion() {
public void matchesWhenNoClosingPrefixStringThenIllegalArgumentException() {
assertThatIllegalArgumentException()
.isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "{bcrypt" + this.rawPassword))
.withMessage("There is no PasswordEncoder mapped for the id \"null\"");
+15 -15
View File
@@ -8,14 +8,14 @@ javaPlatform {
dependencies {
api platform("org.springframework:spring-framework-bom:$springFrameworkVersion")
api platform("io.projectreactor:reactor-bom:2020.0.10")
api platform("io.rsocket:rsocket-bom:1.1.1")
api platform("org.springframework.data:spring-data-bom:2021.0.4")
api platform("io.projectreactor:reactor-bom:2020.0.19")
api platform("io.rsocket:rsocket-bom:1.1.2")
api platform("org.springframework.data:spring-data-bom:2021.0.11")
api platform("org.jetbrains.kotlin:kotlin-bom:$kotlinVersion")
api platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.1")
api platform("com.fasterxml.jackson:jackson-bom:2.12.4")
api platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2")
api platform("com.fasterxml.jackson:jackson-bom:2.12.6.20220326")
constraints {
api "ch.qos.logback:logback-classic:1.2.5"
api "ch.qos.logback:logback-classic:1.2.11"
api "com.google.inject:guice:3.0"
api "com.nimbusds:nimbus-jose-jwt:9.10.1"
api "com.nimbusds:oauth2-oidc-sdk:9.9.1"
@@ -49,12 +49,12 @@ dependencies {
api "org.assertj:assertj-core:3.19.0"
api "org.bouncycastle:bcpkix-jdk15on:1.68"
api "org.bouncycastle:bcprov-jdk15on:1.68"
api "org.eclipse.jetty:jetty-server:9.4.43.v20210629"
api "org.eclipse.jetty:jetty-servlet:9.4.43.v20210629"
api "org.eclipse.jetty:jetty-server:9.4.46.v20220331"
api "org.eclipse.jetty:jetty-servlet:9.4.46.v20220331"
api "org.eclipse.persistence:javax.persistence:2.2.1"
api "org.hibernate:hibernate-entitymanager:5.4.32.Final"
api "org.hsqldb:hsqldb:2.6.0"
api "org.jasig.cas.client:cas-client-core:3.6.2"
api "org.hibernate:hibernate-entitymanager:5.4.33"
api "org.hsqldb:hsqldb:2.6.1"
api "org.jasig.cas.client:cas-client-core:3.6.4"
api "org.mockito:mockito-core:3.9.0"
api "org.openid4java:openid4java-nodeps:0.9.6"
api "org.opensaml:opensaml-core:$openSamlVersion"
@@ -71,10 +71,10 @@ dependencies {
api "org.seleniumhq.selenium:selenium-java:3.141.59"
api "org.seleniumhq.selenium:selenium-support:3.141.59"
api "org.skyscreamer:jsonassert:1.5.0"
api "org.slf4j:jcl-over-slf4j:1.7.32"
api "org.slf4j:log4j-over-slf4j:1.7.32"
api "org.slf4j:slf4j-api:1.7.32"
api "org.springframework.ldap:spring-ldap-core:2.3.4.RELEASE"
api "org.slf4j:jcl-over-slf4j:1.7.36"
api "org.slf4j:log4j-over-slf4j:1.7.36"
api "org.slf4j:slf4j-api:1.7.36"
api "org.springframework.ldap:spring-ldap-core:2.3.7.RELEASE"
api "org.synchronoss.cloud:nio-multipart-parser:1.1.0"
}
}
@@ -463,7 +463,7 @@ You should instead migrate to using `DelegatingPasswordEncoder` to support secur
[source,java,role="primary"]
----
@Bean
public static NoOpPasswordEncoder passwordEncoder() {
public static PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
----
@@ -53,7 +53,7 @@ If you wish to override the Spring Security version, you may do so by providing
<properties>
<!-- ... -->
<spring-security.version>{spring-security-version}</spring-security.version>
</dependencies>
</properties>
----
====
@@ -68,7 +68,7 @@ You can do so by adding a Maven property, as the following example shows:
<properties>
<!-- ... -->
<spring.version>{spring-core-version}</spring.version>
</dependencies>
</properties>
----
====
@@ -184,7 +184,7 @@ then Spring Security's test support can come in handy.
Testing the method above with `WebTestClient` would require simulating some kind of grant flow with an authorization server.
Certainly this would be a daunting task, which is why Spring Security ships with support for removing this boilerplate.
For example, we can tell Spring Security to include a default `OidcUser` using the `SecurityMockServerConfigurers#oidcLogin` method, like so:
For example, we can tell Spring Security to include a default `OidcUser` using the `SecurityMockServerConfigurers#mockOidcLogin` method, like so:
[source,java]
----
@@ -311,7 +311,7 @@ public Mono<String> foo(@AuthenticationPrincipal OAuth2User oauth2User) {
}
----
In that case, we can tell Spring Security to include a default `OAuth2User` using the `SecurityMockServerConfigurers#oauth2User` method, like so:
In that case, we can tell Spring Security to include a default `OAuth2User` using the `SecurityMockServerConfigurers#mockOAuth2Login` method, like so:
[source,java]
----
@@ -431,7 +431,7 @@ public Mono<String> foo(@RegisteredOAuth2AuthorizedClient("my-app") OAuth2Author
----
Simulating this handshake with the authorization server could be cumbersome.
Instead, you can use `SecurityMockServerConfigurers#oauth2Client` to add a `OAuth2AuthorizedClient` into a mock `ServerOAuth2AuthorizedClientRepository`:
Instead, you can use `SecurityMockServerConfigurers#mockOAuth2Client` to add a `OAuth2AuthorizedClient` into a mock `ServerOAuth2AuthorizedClientRepository`:
[source,java]
----
@@ -541,7 +541,7 @@ We'll look at two of them now:
==== `mockJwt() WebTestClientConfigurer`
The first way is via a `WebTestClientConfigurer`.
The simplest of these would look something like this:
The simplest of these would be to use the `SecurityMockServerConfigurers#mockJwt` method like the following:
[source,java]
----
@@ -664,7 +664,7 @@ public Mono<String> foo(BearerTokenAuthentication authentication) {
}
----
In that case, we can tell Spring Security to include a default `BearerTokenAuthentication` using the `SecurityMockServerConfigurers#opaqueToken` method, like so:
In that case, we can tell Spring Security to include a default `BearerTokenAuthentication` using the `SecurityMockServerConfigurers#mockOpaqueToken` method, like so:
[source,java]
----
@@ -125,7 +125,7 @@ This should therefore work with all major databases.
At the time of writing, the system had been successfully tested using Hypersonic SQL, PostgreSQL, Microsoft SQL Server and Oracle.
Two samples ship with Spring Security that demonstrate the ACL module.
The first is the Contacts Sample, and the other is the Document Management System (DMS) Sample.
The first is the {gh-samples-url}/servlet/xml/java/contacts[Contacts Sample], and the other is the {gh-samples-url}/servlet/xml/java/dms[Document Management System (DMS) Sample].
We suggest taking a look over these for examples.
@@ -258,7 +258,7 @@ Their use is enabled through the `global-method-security` namespace element:
===== Access Control using @PreAuthorize and @PostAuthorize
The most obviously useful annotation is `@PreAuthorize` which decides whether a method can actually be invoked or not.
For example (from the "Contacts" sample application)
For example (from the {gh-samples-url}/servlet/xml/java/contacts[Contacts] sample application)
====
.Java
@@ -492,7 +492,7 @@ This would look something like this:
Where `myPermissionEvaluator` is the bean which implements `PermissionEvaluator`.
Usually this will be the implementation from the ACL module which is called `AclPermissionEvaluator`.
See the "Contacts" sample application configuration for more details.
See the {gh-samples-url}/servlet/xml/java/contacts[Contacts] sample application configuration for more details.
===== Method Security Meta Annotations
@@ -198,10 +198,13 @@ The password package of the spring-security-crypto module provides support for e
[source,java]
----
public interface PasswordEncoder {
String encode(CharSequence rawPassword);
String encode(String rawPassword);
boolean matches(CharSequence rawPassword, String encodedPassword);
boolean matches(String rawPassword, String encodedPassword);
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
----
@@ -7,10 +7,11 @@ It is not only useful but necessary to include the user in the queries to suppor
[[data-configuration]]
=== Spring Data & Spring Security Configuration
To use this support, add `org.springframework.security:spring-security-data` dependency and provide a bean of type `SecurityEvaluationContextExtension`.
In Java Configuration, this would look like:
To use this support, add `org.springframework.security:spring-security-data` dependency and provide a bean of type `SecurityEvaluationContextExtension`:
[source,java]
====
.Java
[source,java,role="primary"]
----
@Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
@@ -18,6 +19,16 @@ public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Bean
fun securityEvaluationContextExtension(): SecurityEvaluationContextExtension {
return SecurityEvaluationContextExtension()
}
----
====
In XML Configuration, this would look like:
[source,xml]
@@ -31,7 +42,9 @@ In XML Configuration, this would look like:
Now Spring Security can be used within your queries.
For example:
[source,java]
====
.Java
[source,java,role="primary"]
----
@Repository
public interface MessageRepository extends PagingAndSortingRepository<Message,Long> {
@@ -40,6 +53,17 @@ public interface MessageRepository extends PagingAndSortingRepository<Message,Lo
}
----
.Kotlin
[source,kotlin,role="secondary"]
----
@Repository
interface MessageRepository : PagingAndSortingRepository<Message,Long> {
@Query("select m from Message m where m.to.id = ?#{ principal?.id }")
fun findInbox(pageable: Pageable): Page<Message>
}
----
====
This checks to see if the `Authentication.getPrincipal().getId()` is equal to the recipient of the `Message`.
Note that this example assumes you have customized the principal to be an Object that has an id property.
By exposing the `SecurityEvaluationContextExtension` bean, all of the <<common-expressions,Common Security Expressions>> are available within the Query.
@@ -2641,7 +2641,7 @@ Next, we can construct a `JWTProcessor`:
JWTProcessor jwtProcessor(JWTClaimSetJWSKeySelector keySelector) {
ConfigurableJWTProcessor<SecurityContext> jwtProcessor =
new DefaultJWTProcessor();
jwtProcessor.setJWTClaimSetJWSKeySelector(keySelector);
jwtProcessor.setJWTClaimsSetAwareJWSKeySelector(keySelector);
return jwtProcessor;
}
----
@@ -2734,9 +2734,9 @@ Now that we have a tenant-aware processor and a tenant-aware validator, we can p
----
@Bean
JwtDecoder jwtDecoder(JWTProcessor jwtProcessor, OAuth2TokenValidator<Jwt> jwtValidator) {
NimbusJwtDecoder decoder = new NimbusJwtDecoder(processor);
NimbusJwtDecoder decoder = new NimbusJwtDecoder(jwtProcessor);
OAuth2TokenValidator<Jwt> validator = new DelegatingOAuth2TokenValidator<>
(JwtValidators.createDefault(), this.jwtValidator);
(JwtValidators.createDefault(), jwtValidator);
decoder.setJwtValidator(validator);
return decoder;
}
@@ -739,7 +739,7 @@ But, if you do need something from the request, then you can use create a custom
----
@Component
public class AuthnRequestConverter implements
Converter<MySaml2AuthenticationRequestContext, AuthnRequest> {
Converter<Saml2AuthenticationRequestContext, AuthnRequest> {
private final AuthnRequestBuilder authnRequestBuilder;
private final IssuerBuilder issuerBuilder;
+5 -5
View File
@@ -1,10 +1,10 @@
aspectjVersion=1.9.7
springJavaformatVersion=0.0.28
aspectjVersion=1.9.9.1
springJavaformatVersion=0.0.31
springBootVersion=2.4.2
springFrameworkVersion=5.3.9
springFrameworkVersion=5.3.20
openSamlVersion=3.4.6
version=5.5.2
kotlinVersion=1.5.21
version=5.5.7
kotlinVersion=1.5.32
samplesBranch=5.5.x
org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError
org.gradle.parallel=true
@@ -29,9 +29,9 @@ import org.springframework.util.Assert;
/**
* Simple expression configuration attribute for use in {@link Message} authorizations.
*
* @since 4.0
* @author Rob Winch
* @author Daniel Bustamante Ospina
* @since 4.0
*/
@SuppressWarnings("serial")
class MessageExpressionConfigAttribute implements ConfigAttribute, EvaluationContextPostProcessor<Message<?>> {
@@ -24,8 +24,8 @@ dependencies {
testImplementation 'io.projectreactor:reactor-test'
testImplementation 'io.projectreactor.tools:blockhound'
testImplementation 'org.skyscreamer:jsonassert'
testImplementation 'io.r2dbc:r2dbc-h2:0.8.4.RELEASE'
testImplementation 'io.r2dbc:r2dbc-spi-test:0.8.5.RELEASE'
testImplementation 'io.r2dbc:r2dbc-h2:0.8.5.RELEASE'
testImplementation 'io.r2dbc:r2dbc-spi-test:0.8.6.RELEASE'
testRuntimeOnly 'org.hsqldb:hsqldb'
@@ -58,7 +58,6 @@ import org.springframework.util.CollectionUtils;
*
* @author Joe Grandja
* @since 5.0
* @deprecated Use {@link DefaultAuthorizationCodeTokenResponseClient}
* @see OAuth2AccessTokenResponseClient
* @see OAuth2AuthorizationCodeGrantRequest
* @see OAuth2AccessTokenResponse
@@ -71,6 +70,7 @@ import org.springframework.util.CollectionUtils;
* @see <a target="_blank" href=
* "https://tools.ietf.org/html/rfc6749#section-4.1.4">Section 4.1.4 Access Token Response
* (Authorization Code Grant)</a>
* @deprecated Use {@link DefaultAuthorizationCodeTokenResponseClient}
*/
@Deprecated
public class NimbusAuthorizationCodeTokenResponseClient
@@ -112,8 +112,8 @@ public final class ClientRegistration implements Serializable {
/**
* Returns the uri (or uri template) for the redirection endpoint.
* @deprecated Use {@link #getRedirectUri()} instead
* @return the uri (or uri template) for the redirection endpoint
* @deprecated Use {@link #getRedirectUri()} instead
*/
@Deprecated
public String getRedirectUriTemplate() {
@@ -445,10 +445,10 @@ public final class ClientRegistration implements Serializable {
/**
* Sets the uri (or uri template) for the redirection endpoint.
* @deprecated Use {@link #redirectUri(String)} instead
* @param redirectUriTemplate the uri (or uri template) for the redirection
* endpoint
* @return the {@link Builder}
* @deprecated Use {@link #redirectUri(String)} instead
*/
@Deprecated
public Builder redirectUriTemplate(String redirectUriTemplate) {
@@ -42,18 +42,18 @@ import org.springframework.web.client.RestTemplate;
* {@link OAuth2User} type(s) keyed by {@code String}, which represents the
* {@link ClientRegistration#getRegistrationId() Registration Id} of the Client.
*
* @deprecated It is recommended to use a delegation-based strategy of an
* {@link OAuth2UserService} to support custom {@link OAuth2User} types, as it provides
* much greater flexibility compared to this implementation. See the
* <a target="_blank" href=
* "https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2login-advanced-map-authorities-oauth2userservice">reference
* manual</a> for details on how to implement.
* @author Joe Grandja
* @since 5.0
* @see OAuth2UserService
* @see OAuth2UserRequest
* @see OAuth2User
* @see ClientRegistration
* @deprecated It is recommended to use a delegation-based strategy of an
* {@link OAuth2UserService} to support custom {@link OAuth2User} types, as it provides
* much greater flexibility compared to this implementation. See the
* <a target="_blank" href=
* "https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2login-advanced-map-authorities-oauth2userservice">reference
* manual</a> for details on how to implement.
*/
@Deprecated
public class CustomUserTypesOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
@@ -27,13 +27,13 @@ import org.springframework.security.oauth2.core.user.OAuth2User;
* {@link OAuth2UserRequest#getClientRegistration() Client} and returning an
* {@link AuthenticatedPrincipal} in the form of an {@link OAuth2User}.
*
* @param <R> The type of OAuth 2.0 User Request
* @param <U> The type of OAuth 2.0 User
* @author Joe Grandja
* @since 5.0
* @see OAuth2UserRequest
* @see OAuth2User
* @see AuthenticatedPrincipal
* @param <R> The type of OAuth 2.0 User Request
* @param <U> The type of OAuth 2.0 User
*/
@FunctionalInterface
public interface OAuth2UserService<R extends OAuth2UserRequest, U extends OAuth2User> {
@@ -29,13 +29,13 @@ import org.springframework.security.oauth2.core.user.OAuth2User;
* {@link OAuth2UserRequest#getClientRegistration() Client} and returning an
* {@link AuthenticatedPrincipal} in the form of an {@link OAuth2User}.
*
* @param <R> The type of OAuth 2.0 User Request
* @param <U> The type of OAuth 2.0 User
* @author Rob Winch
* @since 5.1
* @see OAuth2UserRequest
* @see OAuth2User
* @see AuthenticatedPrincipal
* @param <R> The type of OAuth 2.0 User Request
* @param <U> The type of OAuth 2.0 User
*/
@FunctionalInterface
public interface ReactiveOAuth2UserService<R extends OAuth2UserRequest, U extends OAuth2User> {
@@ -59,12 +59,12 @@ public interface AuthorizationRequestRepository<T extends OAuth2AuthorizationReq
/**
* Removes and returns the {@link OAuth2AuthorizationRequest} associated to the
* provided {@code HttpServletRequest} or if not available returns {@code null}.
* @deprecated Use
* {@link #removeAuthorizationRequest(HttpServletRequest, HttpServletResponse)}
* instead
* @param request the {@code HttpServletRequest}
* @return the removed {@link OAuth2AuthorizationRequest} or {@code null} if not
* available
* @deprecated Use
* {@link #removeAuthorizationRequest(HttpServletRequest, HttpServletResponse)}
* instead
*/
@Deprecated
T removeAuthorizationRequest(HttpServletRequest request);
@@ -156,6 +156,9 @@ public final class OAuth2AuthorizedClientArgumentResolver implements HandlerMeth
/**
* Sets the client used when requesting an access token credential at the Token
* Endpoint for the {@code client_credentials} grant.
* @param clientCredentialsTokenResponseClient the client used when requesting an
* access token credential at the Token Endpoint for the {@code client_credentials}
* grant
* @deprecated Use
* {@link #OAuth2AuthorizedClientArgumentResolver(OAuth2AuthorizedClientManager)}
* instead. Create an instance of
@@ -165,9 +168,6 @@ public final class OAuth2AuthorizedClientArgumentResolver implements HandlerMeth
* to
* {@link DefaultOAuth2AuthorizedClientManager#setAuthorizedClientProvider(OAuth2AuthorizedClientProvider)
* DefaultOAuth2AuthorizedClientManager}.
* @param clientCredentialsTokenResponseClient the client used when requesting an
* access token credential at the Token Endpoint for the {@code client_credentials}
* grant
*/
@Deprecated
public void setClientCredentialsTokenResponseClient(
@@ -355,6 +355,7 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
/**
* Sets the {@link ReactiveOAuth2AccessTokenResponseClient} used for getting an
* {@link OAuth2AuthorizedClient} for the client_credentials grant.
* @param clientCredentialsTokenResponseClient the client to use
* @deprecated Use
* {@link #ServerOAuth2AuthorizedClientExchangeFilterFunction(ReactiveOAuth2AuthorizedClientManager)}
* instead. Create an instance of
@@ -364,7 +365,6 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
* supply it to
* {@link DefaultReactiveOAuth2AuthorizedClientManager#setAuthorizedClientProvider(ReactiveOAuth2AuthorizedClientProvider)
* DefaultReactiveOAuth2AuthorizedClientManager}.
* @param clientCredentialsTokenResponseClient the client to use
*/
@Deprecated
public void setClientCredentialsTokenResponseClient(
@@ -407,13 +407,13 @@ public final class ServerOAuth2AuthorizedClientExchangeFilterFunction implements
/**
* An access token will be considered expired by comparing its expiration to now +
* this skewed Duration. The default is 1 minute.
* @param accessTokenExpiresSkew the Duration to use.
* @deprecated The {@code accessTokenExpiresSkew} should be configured with the
* specific {@link ReactiveOAuth2AuthorizedClientProvider} implementation, e.g.
* {@link ClientCredentialsReactiveOAuth2AuthorizedClientProvider#setClockSkew(Duration)
* ClientCredentialsReactiveOAuth2AuthorizedClientProvider} or
* {@link RefreshTokenReactiveOAuth2AuthorizedClientProvider#setClockSkew(Duration)
* RefreshTokenReactiveOAuth2AuthorizedClientProvider}.
* @param accessTokenExpiresSkew the Duration to use.
*/
@Deprecated
public void setAccessTokenExpiresSkew(Duration accessTokenExpiresSkew) {
@@ -239,6 +239,7 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
/**
* Sets the {@link OAuth2AccessTokenResponseClient} used for getting an
* {@link OAuth2AuthorizedClient} for the client_credentials grant.
* @param clientCredentialsTokenResponseClient the client to use
* @deprecated Use
* {@link #ServletOAuth2AuthorizedClientExchangeFilterFunction(OAuth2AuthorizedClientManager)}
* instead. Create an instance of
@@ -248,7 +249,6 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
* to
* {@link DefaultOAuth2AuthorizedClientManager#setAuthorizedClientProvider(OAuth2AuthorizedClientProvider)
* DefaultOAuth2AuthorizedClientManager}.
* @param clientCredentialsTokenResponseClient the client to use
*/
@Deprecated
public void setClientCredentialsTokenResponseClient(
@@ -397,13 +397,13 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
/**
* An access token will be considered expired by comparing its expiration to now +
* this skewed Duration. The default is 1 minute.
* @param accessTokenExpiresSkew the Duration to use.
* @deprecated The {@code accessTokenExpiresSkew} should be configured with the
* specific {@link OAuth2AuthorizedClientProvider} implementation, e.g.
* {@link ClientCredentialsOAuth2AuthorizedClientProvider#setClockSkew(Duration)
* ClientCredentialsOAuth2AuthorizedClientProvider} or
* {@link RefreshTokenOAuth2AuthorizedClientProvider#setClockSkew(Duration)
* RefreshTokenOAuth2AuthorizedClientProvider}.
* @param accessTokenExpiresSkew the Duration to use.
*/
@Deprecated
public void setAccessTokenExpiresSkew(Duration accessTokenExpiresSkew) {
@@ -35,10 +35,10 @@ import org.springframework.web.server.ServerWebExchange;
* {@link ServerWebExchange} is null and that the {@link Authentication} is either null or
* anonymous to prevent using it incorrectly.
*
* @deprecated Use {@link AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager}
* instead
* @author Rob Winch
* @since 5.1
* @deprecated Use {@link AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager}
* instead
*/
@Deprecated
public class UnAuthenticatedServerOAuth2AuthorizedClientRepository implements ServerOAuth2AuthorizedClientRepository {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,11 +42,10 @@ public interface ClaimAccessor {
/**
* Returns the claim value as a {@code T} type. The claim value is expected to be of
* type {@code T}.
*
* @since 5.2
* @param claim the name of the claim
* @param <T> the type of the claim value
* @return the claim value
* @since 5.2
*/
@SuppressWarnings("unchecked")
default <T> T getClaim(String claim) {
@@ -133,11 +132,13 @@ public interface ClaimAccessor {
}
/**
* Returns the claim value as a {@code Map<String, Object>} or {@code null} if it does
* not exist or cannot be assigned to a {@code Map}.
* Returns the claim value as a {@code Map<String, Object>} or {@code null} if the
* claim does not exist.
* @param claim the name of the claim
* @return the claim value or {@code null} if it does not exist or cannot be assigned
* to a {@code Map}
* @return the claim value or {@code null} if the claim does not exist
* @throws IllegalArgumentException if the claim value cannot be converted to a
* {@code List}
* @throws NullPointerException if the claim value is {@code null}
*/
@SuppressWarnings("unchecked")
default Map<String, Object> getClaimAsMap(String claim) {
@@ -156,11 +157,13 @@ public interface ClaimAccessor {
}
/**
* Returns the claim value as a {@code List<String>} or {@code null} if it does not
* exist or cannot be assigned to a {@code List}.
* Returns the claim value as a {@code List<String>} or {@code null} if the claim does
* not exist.
* @param claim the name of the claim
* @return the claim value or {@code null} if it does not exist or cannot be assigned
* to a {@code List}
* @return the claim value or {@code null} if the claim does not exist
* @throws IllegalArgumentException if the claim value cannot be converted to a
* {@code List}
* @throws NullPointerException if the claim value is {@code null}
*/
@SuppressWarnings("unchecked")
default List<String> getClaimAsStringList(String claim) {
@@ -34,6 +34,7 @@ import org.springframework.util.StringUtils;
* @author Joe Grandja
* @author Nikita Konev
* @since 5.3
* @deprecated Use {@link DefaultMapOAuth2AccessTokenResponseConverter} instead
*/
public final class MapOAuth2AccessTokenResponseConverter
implements Converter<Map<String, String>, OAuth2AccessTokenResponse> {
@@ -32,6 +32,7 @@ import org.springframework.util.StringUtils;
* @author Joe Grandja
* @author Nikita Konev
* @since 5.3
* @deprecated Use {@link DefaultOAuth2AccessTokenResponseMapConverter} instead
*/
public final class OAuth2AccessTokenResponseMapConverter
implements Converter<OAuth2AccessTokenResponse, Map<String, String>> {
@@ -110,6 +110,7 @@ public class OAuth2AccessTokenResponseHttpMessageConverter
* parameters to an {@link OAuth2AccessTokenResponse}.
* @param tokenResponseConverter the {@link Converter} used for converting to an
* {@link OAuth2AccessTokenResponse}
* @deprecated Use {@link #setAccessTokenResponseConverter(Converter)} instead
*/
public final void setTokenResponseConverter(
Converter<Map<String, String>, OAuth2AccessTokenResponse> tokenResponseConverter) {
@@ -123,6 +124,8 @@ public class OAuth2AccessTokenResponseHttpMessageConverter
* Access Token Response parameters.
* @param tokenResponseParametersConverter the {@link Converter} used for converting
* to a {@code Map} representation of the Access Token Response parameters
* @deprecated Use {@link #setAccessTokenResponseParametersConverter(Converter)}
* instead
*/
public final void setTokenResponseParametersConverter(
Converter<OAuth2AccessTokenResponse, Map<String, String>> tokenResponseParametersConverter) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -54,7 +54,7 @@ public final class JwtClaimValidator<T> implements OAuth2TokenValidator<Jwt> {
Assert.notNull(test, "test can not be null");
this.claim = claim;
this.test = test;
this.error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, "The " + this.claim + " claim is not valid",
this.error = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, "The " + this.claim + " claim is not valid",
"https://tools.ietf.org/html/rfc6750#section-3.1");
}
@@ -20,11 +20,11 @@ package org.springframework.security.oauth2.jwt;
* A factory for {@link JwtDecoder}(s). This factory should be supplied with a type that
* provides contextual information used to create a specific {@code JwtDecoder}.
*
* @param <C> The type that provides contextual information used to create a specific
* {@code JwtDecoder}.
* @author Joe Grandja
* @since 5.2
* @see JwtDecoder
* @param <C> The type that provides contextual information used to create a specific
* {@code JwtDecoder}.
*/
@FunctionalInterface
public interface JwtDecoderFactory<C> {
@@ -90,7 +90,7 @@ public final class JwtTimestampValidator implements OAuth2TokenValidator<Jwt> {
private OAuth2Error createOAuth2Error(String reason) {
this.logger.debug(reason);
return new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, reason,
return new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, reason,
"https://tools.ietf.org/html/rfc6750#section-3.1");
}
@@ -35,7 +35,6 @@ import org.springframework.web.client.RestOperations;
* <p>
* <b>NOTE:</b> This implementation uses the Nimbus JOSE + JWT SDK internally.
*
* @deprecated Use {@link NimbusJwtDecoder} or {@link JwtDecoders} instead
* @author Joe Grandja
* @author Josh Cummings
* @since 5.0
@@ -49,6 +48,7 @@ import org.springframework.web.client.RestOperations;
* (JWK)</a>
* @see <a target="_blank" href="https://connect2id.com/products/nimbus-jose-jwt">Nimbus
* JOSE + JWT SDK</a>
* @deprecated Use {@link NimbusJwtDecoder} or {@link JwtDecoders} instead
*/
@Deprecated
public final class NimbusJwtDecoderJwkSupport implements JwtDecoder {
@@ -21,11 +21,11 @@ package org.springframework.security.oauth2.jwt;
* type that provides contextual information used to create a specific
* {@code ReactiveJwtDecoder}.
*
* @param <C> The type that provides contextual information used to create a specific
* {@code ReactiveJwtDecoder}.
* @author Joe Grandja
* @since 5.2
* @see ReactiveJwtDecoder
* @param <C> The type that provides contextual information used to create a specific
* {@code ReactiveJwtDecoder}.
*/
@FunctionalInterface
public interface ReactiveJwtDecoderFactory<C> {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,10 +16,14 @@
package org.springframework.security.oauth2.jwt;
import java.util.Collection;
import java.util.Objects;
import java.util.function.Predicate;
import org.junit.Test;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import static org.assertj.core.api.Assertions.assertThat;
@@ -45,7 +49,9 @@ public class JwtClaimValidatorTests {
@Test
public void validateWhenClaimFailsTheTestThenReturnsFailure() {
Jwt jwt = TestJwts.jwt().claim(JwtClaimNames.ISS, "http://abc").build();
Collection<OAuth2Error> details = this.validator.validate(jwt).getErrors();
assertThat(this.validator.validate(jwt).getErrors().isEmpty()).isFalse();
assertThat(details).allMatch((error) -> Objects.equals(error.getErrorCode(), OAuth2ErrorCodes.INVALID_TOKEN));
}
@Test
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,11 +23,13 @@ import java.time.ZoneId;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import org.junit.Test;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.jose.jws.JwsAlgorithms;
@@ -63,6 +65,7 @@ public class JwtTimestampValidatorTests {
.collect(Collectors.toList());
// @formatter:on
assertThat(messages).contains("Jwt expired at " + oneHourAgo);
assertThat(details).allMatch((error) -> Objects.equals(error.getErrorCode(), OAuth2ErrorCodes.INVALID_TOKEN));
}
@Test
@@ -77,6 +80,7 @@ public class JwtTimestampValidatorTests {
.collect(Collectors.toList());
// @formatter:on
assertThat(messages).contains("Jwt used before " + oneHourFromNow);
assertThat(details).allMatch((error) -> Objects.equals(error.getErrorCode(), OAuth2ErrorCodes.INVALID_TOKEN));
}
@Test
@@ -109,6 +113,7 @@ public class JwtTimestampValidatorTests {
.collect(Collectors.toList());
// @formatter:on
assertThat(result.hasErrors()).isTrue();
assertThat(result.getErrors().iterator().next().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN);
assertThat(messages).contains("Jwt used before " + justOverOneDayFromNow);
}
@@ -16,6 +16,7 @@
package org.springframework.security.oauth2.server.resource.authentication;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -186,7 +187,7 @@ public final class JwtIssuerReactiveAuthenticationManagerResolver
return this.authenticationManagers.computeIfAbsent(issuer,
(k) -> Mono.<ReactiveAuthenticationManager>fromCallable(() -> new JwtReactiveAuthenticationManager(ReactiveJwtDecoders.fromIssuerLocation(k)))
.subscribeOn(Schedulers.boundedElastic())
.cache()
.cache((manager) -> Duration.ofMillis(Long.MAX_VALUE), (ex) -> Duration.ZERO, () -> Duration.ZERO)
);
// @formatter:on
}
@@ -33,6 +33,7 @@ import org.springframework.security.oauth2.core.ClaimAccessor;
* @see OAuth2IntrospectionAuthenticatedPrincipal
* @see <a target="_blank" href=
* "https://tools.ietf.org/html/rfc7662#section-2.2">Introspection Response</a>
* @deprecated Use {@link OAuth2TokenIntrospectionClaimAccessor} instead
*/
public interface OAuth2IntrospectionClaimAccessor extends ClaimAccessor {
@@ -23,6 +23,7 @@ package org.springframework.security.oauth2.server.resource.introspection;
*
* @author Josh Cummings
* @since 5.2
* @deprecated Use {@link OAuth2TokenIntrospectionClaimNames} instead
*/
public interface OAuth2IntrospectionClaimNames {
@@ -96,6 +96,44 @@ public class JwtIssuerAuthenticationManagerResolverTests {
}
}
@Test
public void resolveWhednUsingTrustedIssuerThenReturnsAuthenticationManager() throws Exception {
try (MockWebServer server = new MockWebServer()) {
server.start();
String issuer = server.url("").toString();
// @formatter:off
server.enqueue(new MockResponse().setResponseCode(500)
.setHeader("Content-Type", "application/json")
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer))
);
server.enqueue(new MockResponse().setResponseCode(200)
.setHeader("Content-Type", "application/json")
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer))
);
server.enqueue(new MockResponse().setResponseCode(200)
.setHeader("Content-Type", "application/json")
.setBody(JWK_SET)
);
server.enqueue(new MockResponse().setResponseCode(200)
.setHeader("Content-Type", "application/json")
.setBody(JWK_SET)
);
// @formatter:on
JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
JwtIssuerAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerAuthenticationManagerResolver(
issuer);
Authentication token = withBearerToken(jws.serialize());
AuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null);
assertThat(authenticationManager).isNotNull();
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> authenticationManager.authenticate(token));
Authentication authentication = authenticationManager.authenticate(token);
assertThat(authentication.isAuthenticated()).isTrue();
}
}
@Test
public void resolveWhenUsingSameIssuerThenReturnsSameAuthenticationManager() throws Exception {
try (MockWebServer server = new MockWebServer()) {
@@ -95,6 +95,36 @@ public class JwtIssuerReactiveAuthenticationManagerResolverTests {
}
}
// gh-10444
@Test
public void resolveWhednUsingTrustedIssuerThenReturnsAuthenticationManager() throws Exception {
try (MockWebServer server = new MockWebServer()) {
String issuer = server.url("").toString();
// @formatter:off
server.enqueue(new MockResponse().setResponseCode(500).setHeader("Content-Type", "application/json")
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)));
server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json")
.setBody(String.format(DEFAULT_RESPONSE_TEMPLATE, issuer, issuer)));
server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json")
.setBody(JWK_SET));
server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json")
.setBody(JWK_SET));
// @formatter:on
JWSObject jws = new JWSObject(new JWSHeader(JWSAlgorithm.RS256),
new Payload(new JSONObject(Collections.singletonMap(JwtClaimNames.ISS, issuer))));
jws.sign(new RSASSASigner(TestKeys.DEFAULT_PRIVATE_KEY));
JwtIssuerReactiveAuthenticationManagerResolver authenticationManagerResolver = new JwtIssuerReactiveAuthenticationManagerResolver(
issuer);
ReactiveAuthenticationManager authenticationManager = authenticationManagerResolver.resolve(null).block();
assertThat(authenticationManager).isNotNull();
Authentication token = withBearerToken(jws.serialize());
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> authenticationManager.authenticate(token).block());
Authentication authentication = authenticationManager.authenticate(token).block();
assertThat(authentication.isAuthenticated()).isTrue();
}
}
@Test
public void resolveWhenUsingSameIssuerThenReturnsSameAuthenticationManager() throws Exception {
try (MockWebServer server = new MockWebServer()) {
@@ -43,12 +43,12 @@ import org.openid4java.message.ax.FetchResponse;
import org.springframework.util.StringUtils;
/**
* @author Ray Krueger
* @author Luke Taylor
* @deprecated The OpenID 1.0 and 2.0 protocols have been deprecated and users are
* <a href="https://openid.net/specs/openid-connect-migration-1_0.html">encouraged to
* migrate</a> to <a href="https://openid.net/connect/">OpenID Connect</a>, which is
* supported by <code>spring-security-oauth2</code>.
* @author Ray Krueger
* @author Luke Taylor
*/
@Deprecated
@SuppressWarnings("unchecked")
@@ -126,6 +126,10 @@ final class OpenSamlSigningUtils {
return credentials;
}
private OpenSamlSigningUtils() {
}
static class QueryParametersPartial {
final RelyingPartyRegistration registration;
@@ -166,8 +170,4 @@ final class OpenSamlSigningUtils {
}
private OpenSamlSigningUtils() {
}
}
@@ -85,6 +85,10 @@ final class OpenSamlVerificationUtils {
DefaultSecurityConfigurationBootstrap.buildBasicInlineKeyInfoCredentialResolver());
}
private OpenSamlVerificationUtils() {
}
static class VerifierPartial {
private final String id;
@@ -210,8 +214,4 @@ final class OpenSamlVerificationUtils {
}
private OpenSamlVerificationUtils() {
}
}
@@ -19,13 +19,12 @@ package org.springframework.security.saml2.provider.service.authentication;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterOutputStream;
import org.apache.commons.codec.binary.Base64;
import org.springframework.security.saml2.Saml2Exception;
/**
@@ -33,17 +32,15 @@ import org.springframework.security.saml2.Saml2Exception;
*/
final class Saml2Utils {
private static Base64 BASE64 = new Base64(0, new byte[] { '\n' });
private Saml2Utils() {
}
static String samlEncode(byte[] b) {
return BASE64.encodeAsString(b);
return Base64.getMimeEncoder().encodeToString(b);
}
static byte[] samlDecode(String s) {
return BASE64.decode(s);
return Base64.getMimeDecoder().decode(s);
}
static byte[] samlDeflate(String s) {
@@ -21,7 +21,7 @@ import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -383,7 +383,7 @@ public final class RelyingPartyRegistration {
org.springframework.security.saml2.credentials.Saml2X509Credential credential) {
PrivateKey privateKey = credential.getPrivateKey();
X509Certificate certificate = credential.getCertificate();
Set<Saml2X509Credential.Saml2X509CredentialType> credentialTypes = new HashSet<>();
Set<Saml2X509Credential.Saml2X509CredentialType> credentialTypes = new LinkedHashSet<>();
if (credential.isSigningCredential()) {
credentialTypes.add(Saml2X509Credential.Saml2X509CredentialType.SIGNING);
}
@@ -403,7 +403,7 @@ public final class RelyingPartyRegistration {
Saml2X509Credential credential) {
PrivateKey privateKey = credential.getPrivateKey();
X509Certificate certificate = credential.getCertificate();
Set<org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType> credentialTypes = new HashSet<>();
Set<org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType> credentialTypes = new LinkedHashSet<>();
if (credential.isSigningCredential()) {
credentialTypes.add(
org.springframework.security.saml2.credentials.Saml2X509Credential.Saml2X509CredentialType.SIGNING);
@@ -573,9 +573,9 @@ public final class RelyingPartyRegistration {
private List<String> signingAlgorithms = new ArrayList<>();
private Collection<Saml2X509Credential> verificationX509Credentials = new HashSet<>();
private Collection<Saml2X509Credential> verificationX509Credentials = new LinkedHashSet<>();
private Collection<Saml2X509Credential> encryptionX509Credentials = new HashSet<>();
private Collection<Saml2X509Credential> encryptionX509Credentials = new LinkedHashSet<>();
private String singleSignOnServiceLocation;
@@ -822,9 +822,9 @@ public final class RelyingPartyRegistration {
private String entityId = "{baseUrl}/saml2/service-provider-metadata/{registrationId}";
private Collection<Saml2X509Credential> signingX509Credentials = new HashSet<>();
private Collection<Saml2X509Credential> signingX509Credentials = new LinkedHashSet<>();
private Collection<Saml2X509Credential> decryptionX509Credentials = new HashSet<>();
private Collection<Saml2X509Credential> decryptionX509Credentials = new LinkedHashSet<>();
private String assertionConsumerServiceLocation = "{baseUrl}/login/saml2/sso/{registrationId}";
@@ -832,7 +832,7 @@ public final class RelyingPartyRegistration {
private ProviderDetails.Builder providerDetails = new ProviderDetails.Builder();
private Collection<org.springframework.security.saml2.credentials.Saml2X509Credential> credentials = new HashSet<>();
private Collection<org.springframework.security.saml2.credentials.Saml2X509Credential> credentials = new LinkedHashSet<>();
private Builder(String registrationId) {
this.registrationId = registrationId;
@@ -63,23 +63,21 @@ public class Saml2WebSsoAuthenticationFilter extends AbstractAuthenticationProce
String filterProcessesUrl) {
this(new Saml2AuthenticationTokenConverter(
new DefaultRelyingPartyRegistrationResolver(relyingPartyRegistrationRepository)), filterProcessesUrl);
Assert.isTrue(filterProcessesUrl.contains("{registrationId}"),
"filterProcessesUrl must contain a {registrationId} match variable");
}
/**
* Creates a {@link Saml2WebSsoAuthenticationFilter} given the provided parameters
* @param authenticationConverter the strategy for converting an
* {@link HttpServletRequest} into an {@link Authentication}
* @param filterProcessingUrl the processing URL, must contain a {registrationId}
* variable
* @param filterProcessesUrl the processing URL
* @since 5.4
*/
public Saml2WebSsoAuthenticationFilter(AuthenticationConverter authenticationConverter,
String filterProcessingUrl) {
super(filterProcessingUrl);
public Saml2WebSsoAuthenticationFilter(AuthenticationConverter authenticationConverter, String filterProcessesUrl) {
super(filterProcessesUrl);
Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
Assert.hasText(filterProcessingUrl, "filterProcessesUrl must contain a URL pattern");
Assert.isTrue(filterProcessingUrl.contains("{registrationId}"),
"filterProcessesUrl must contain a {registrationId} match variable");
Assert.hasText(filterProcessesUrl, "filterProcessesUrl must contain a URL pattern");
this.authenticationConverter = authenticationConverter;
setAllowSessionCreation(true);
setSessionAuthenticationStrategy(new ChangeSessionIdAuthenticationStrategy());
@@ -19,28 +19,30 @@ package org.springframework.security.saml2.core;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterOutputStream;
import org.apache.commons.codec.binary.Base64;
import org.springframework.security.saml2.Saml2Exception;
public final class Saml2Utils {
private static Base64 BASE64 = new Base64(0, new byte[] { '\n' });
private Saml2Utils() {
}
@Deprecated
public static String samlEncodeNotRfc2045(byte[] b) {
return Base64.getEncoder().encodeToString(b);
}
public static String samlEncode(byte[] b) {
return BASE64.encodeAsString(b);
return Base64.getMimeEncoder().encodeToString(b);
}
public static byte[] samlDecode(String s) {
return BASE64.decode(s);
return Base64.getMimeDecoder().decode(s);
}
public static byte[] samlDeflate(String s) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -51,6 +51,10 @@ public final class TestSaml2X509Credentials {
return new Saml2X509Credential(idpCertificate(), Saml2X509CredentialType.VERIFICATION);
}
public static Saml2X509Credential relyingPartyEncryptingCredential() {
return new Saml2X509Credential(idpCertificate(), Saml2X509CredentialType.ENCRYPTION);
}
public static Saml2X509Credential relyingPartySigningCredential() {
return new Saml2X509Credential(spPrivateKey(), spCertificate(), Saml2X509CredentialType.SIGNING);
}
@@ -59,6 +63,16 @@ public final class TestSaml2X509Credentials {
return new Saml2X509Credential(spPrivateKey(), spCertificate(), Saml2X509CredentialType.DECRYPTION);
}
public static Saml2X509Credential altPublicCredential() {
return new Saml2X509Credential(altCertificate(), Saml2X509CredentialType.VERIFICATION,
Saml2X509CredentialType.ENCRYPTION);
}
public static Saml2X509Credential altPrivateCredential() {
return new Saml2X509Credential(altPrivateKey(), altCertificate(), Saml2X509CredentialType.SIGNING,
Saml2X509CredentialType.DECRYPTION);
}
private static X509Certificate certificate(String cert) {
ByteArrayInputStream certBytes = new ByteArrayInputStream(cert.getBytes());
try {
@@ -170,4 +184,40 @@ public final class TestSaml2X509Credentials {
+ "-----END PRIVATE KEY-----");
}
private static X509Certificate altCertificate() {
return certificate(
"-----BEGIN CERTIFICATE-----\n" + "MIICkDCCAfkCFEstVfmWSFQp/j88GaMUwqVK72adMA0GCSqGSIb3DQEBCwUAMIGG\n"
+ "MQswCQYDVQQGEwJVUzETMBEGA1UECAwKV2FzaGluZ3RvbjESMBAGA1UEBwwJVmFu\n"
+ "Y291dmVyMR0wGwYDVQQKDBRTcHJpbmcgU2VjdXJpdHkgU0FNTDEMMAoGA1UECwwD\n"
+ "YWx0MSEwHwYDVQQDDBhhbHQuc3ByaW5nLnNlY3VyaXR5LnNhbWwwHhcNMjIwMjEw\n"
+ "MTY1ODA4WhcNMzIwMjEwMTY1ODA4WjCBhjELMAkGA1UEBhMCVVMxEzARBgNVBAgM\n"
+ "Cldhc2hpbmd0b24xEjAQBgNVBAcMCVZhbmNvdXZlcjEdMBsGA1UECgwUU3ByaW5n\n"
+ "IFNlY3VyaXR5IFNBTUwxDDAKBgNVBAsMA2FsdDEhMB8GA1UEAwwYYWx0LnNwcmlu\n"
+ "Zy5zZWN1cml0eS5zYW1sMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC9ZGWj\n"
+ "TPDsymQCJL044py4xLsBI/S9RvzNeR9oD/tHyoxCE+YZzjf0PyBtwqKzkKWqCPf4\n"
+ "XGUYHfEpkM5kJYwCW8TsOx5fnwLIQweiPqjYrBr/O0IjHMqYG9HlR/ros7iBt4ab\n"
+ "EGUu/B9yYg1YRYPxKQ6TNP3AD+9tBT8TsFFyjwIDAQABMA0GCSqGSIb3DQEBCwUA\n"
+ "A4GBAKJf2VHLjkCHRxlbWn63jGiquq3ENYgd1JS0DZ3ggFmuc6zQiqxzRGtArIDZ\n"
+ "0jH5nrG0jcvO0fqDqBQh0iT8thfUnkViAQvACZ9a+0x0NzUicJ+Ra51c8Z2enqbg\n"
+ "pXy+ga67HcAXrDekm1MCGCgiEb/Cgl41lsideqhC8Efl7PRN\n" + "-----END CERTIFICATE-----");
}
private static PrivateKey altPrivateKey() {
return privateKey(
"-----BEGIN PRIVATE KEY-----\n" + "MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAL1kZaNM8OzKZAIk\n"
+ "vTjinLjEuwEj9L1G/M15H2gP+0fKjEIT5hnON/Q/IG3CorOQpaoI9/hcZRgd8SmQ\n"
+ "zmQljAJbxOw7Hl+fAshDB6I+qNisGv87QiMcypgb0eVH+uizuIG3hpsQZS78H3Ji\n"
+ "DVhFg/EpDpM0/cAP720FPxOwUXKPAgMBAAECgYEApYKslAZ0cer5dSoYNzNLFOnQ\n"
+ "J1H92r/Dw+k6+h0lUvr+keyD5T9jhM76DxHOUDBzpmIKGoDcVDQugk2rILfzXsQA\n"
+ "JtwvDRJk32Z02Vt0jb7t/WUOOQhjKCjQuv9/tOx90GCl0VxYG69UOjaMRWrlg/i9\n"
+ "6/zcTRIahIn5XxF0psECQQD7ivJCpDbOLJGsc8gNJR4cvjZ1q0mHIOrbKqJC0y1n\n"
+ "5DrzGEflPeyCUwnOKNp9HJQP8gmZzXfj0JM9KsjpiUChAkEAwL+FmhDoTiqStIrH\n"
+ "h9Kdnsev//imMmRHxjwDhntYvqavUsISRmY3imd8inoYq5dzWQMzBtoTyMRmqeLT\n"
+ "DHV1LwJAW4xaV37Eo4z9B7Kr4Hzd1MA1ueW5QQDt+Q4vN/r7z4/1FHyFzh0Xcucd\n"
+ "7nZX7qj0CkmgzOVG+Rb0P5LOxJA7gQJBAK1KQ2qNct375qPM9bEGSVGchH6k5X7+\n"
+ "q4ztHdpFgTb/EzdbZiTG935GpjC1rwJuinTnrHOnkwv4j7iDRm24GF8CQQDqPvrQ\n"
+ "GcItR6UUy0q/B8UxLzlE6t+HiznfiJKfyGgCHU56Y4/ZhzSQz2MZHz9SK4DsUL9s\n" + "bOYrWq8VY2fyjV1t\n"
+ "-----END PRIVATE KEY-----");
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 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.
@@ -18,7 +18,8 @@ package org.springframework.security.saml2.provider.service.registration;
import org.junit.Test;
import org.springframework.security.saml2.credentials.TestSaml2X509Credentials;
import org.springframework.security.saml2.core.Saml2X509Credential;
import org.springframework.security.saml2.core.TestSaml2X509Credentials;
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
import static org.assertj.core.api.Assertions.assertThat;
@@ -81,9 +82,68 @@ public class RelyingPartyRegistrationTests {
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("id")
.entityId("entity-id").assertionConsumerServiceLocation("location")
.assertingPartyDetails((assertingParty) -> assertingParty.entityId("entity-id")
.singleSignOnServiceLocation("location"))
.credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())).build();
.singleSignOnServiceLocation("location").verificationX509Credentials(
(c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())))
.build();
assertThat(relyingPartyRegistration.getAssertionConsumerServiceBinding()).isEqualTo(Saml2MessageBinding.POST);
}
@Test
public void buildPreservesCredentialsOrder() {
Saml2X509Credential altRpCredential = TestSaml2X509Credentials.altPrivateCredential();
Saml2X509Credential altApCredential = TestSaml2X509Credentials.altPublicCredential();
Saml2X509Credential verifyingCredential = TestSaml2X509Credentials.relyingPartyVerifyingCredential();
Saml2X509Credential encryptingCredential = TestSaml2X509Credentials.relyingPartyEncryptingCredential();
Saml2X509Credential signingCredential = TestSaml2X509Credentials.relyingPartySigningCredential();
Saml2X509Credential decryptionCredential = TestSaml2X509Credentials.relyingPartyDecryptingCredential();
// Test with the alt credentials first
RelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.noCredentials()
.assertingPartyDetails((assertingParty) -> assertingParty.verificationX509Credentials((c) -> {
c.add(altApCredential);
c.add(verifyingCredential);
}).encryptionX509Credentials((c) -> {
c.add(altApCredential);
c.add(encryptingCredential);
})).signingX509Credentials((c) -> {
c.add(altRpCredential);
c.add(signingCredential);
}).decryptionX509Credentials((c) -> {
c.add(altRpCredential);
c.add(decryptionCredential);
}).build();
assertThat(relyingPartyRegistration.getSigningX509Credentials()).containsExactly(altRpCredential,
signingCredential);
assertThat(relyingPartyRegistration.getDecryptionX509Credentials()).containsExactly(altRpCredential,
decryptionCredential);
assertThat(relyingPartyRegistration.getAssertingPartyDetails().getVerificationX509Credentials())
.containsExactly(altApCredential, verifyingCredential);
assertThat(relyingPartyRegistration.getAssertingPartyDetails().getEncryptionX509Credentials())
.containsExactly(altApCredential, encryptingCredential);
// Test with the alt credentials last
relyingPartyRegistration = TestRelyingPartyRegistrations.noCredentials()
.assertingPartyDetails((assertingParty) -> assertingParty.verificationX509Credentials((c) -> {
c.add(verifyingCredential);
c.add(altApCredential);
}).encryptionX509Credentials((c) -> {
c.add(encryptingCredential);
c.add(altApCredential);
})).signingX509Credentials((c) -> {
c.add(signingCredential);
c.add(altRpCredential);
}).decryptionX509Credentials((c) -> {
c.add(decryptionCredential);
c.add(altRpCredential);
}).build();
assertThat(relyingPartyRegistration.getSigningX509Credentials()).containsExactly(signingCredential,
altRpCredential);
assertThat(relyingPartyRegistration.getDecryptionX509Credentials()).containsExactly(decryptionCredential,
altRpCredential);
assertThat(relyingPartyRegistration.getAssertingPartyDetails().getVerificationX509Credentials())
.containsExactly(verifyingCredential, altApCredential);
assertThat(relyingPartyRegistration.getAssertingPartyDetails().getEncryptionX509Credentials())
.containsExactly(encryptingCredential, altApCredential);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.web.authentication.AuthenticationConverter;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.BDDMockito.given;
@@ -60,6 +61,12 @@ public class Saml2WebSsoAuthenticationFilterTests {
this.filter = new Saml2WebSsoAuthenticationFilter(this.repository, "/url/variable/is/present/{registrationId}");
}
@Test
public void constructingFilterWithMissingRegistrationIdVariableAndCustomAuthenticationConverterThenSucceeds() {
AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class);
this.filter = new Saml2WebSsoAuthenticationFilter(authenticationConverter, "/url/missing/variable");
}
@Test
public void requiresAuthenticationWhenHappyPathThenReturnsTrue() {
Assert.assertTrue(this.filter.requiresAuthentication(this.request, this.response));
@@ -60,7 +60,8 @@ public class Saml2AuthenticationTokenConverterTests {
given(this.relyingPartyRegistrationResolver.convert(any(HttpServletRequest.class)))
.willReturn(this.relyingPartyRegistration);
MockHttpServletRequest request = new MockHttpServletRequest();
request.setParameter("SAMLResponse", Saml2Utils.samlEncode("response".getBytes(StandardCharsets.UTF_8)));
request.setParameter("SAMLResponse",
Saml2Utils.samlEncodeNotRfc2045("response".getBytes(StandardCharsets.UTF_8)));
Saml2AuthenticationToken token = converter.convert(request);
assertThat(token.getSaml2Response()).isEqualTo("response");
assertThat(token.getRelyingPartyRegistration().getRegistrationId())
@@ -111,7 +112,7 @@ public class Saml2AuthenticationTokenConverterTests {
MockHttpServletRequest request = new MockHttpServletRequest();
request.setMethod("GET");
byte[] deflated = Saml2Utils.samlDeflate("response");
String encoded = Saml2Utils.samlEncode(deflated);
String encoded = Saml2Utils.samlEncodeNotRfc2045(deflated);
request.setParameter("SAMLResponse", encoded);
Saml2AuthenticationToken token = converter.convert(request);
assertThat(token.getSaml2Response()).isEqualTo("response");
@@ -41,8 +41,6 @@ import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.nimbusds.oauth2.sdk.util.StringUtils;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
@@ -102,6 +100,7 @@ import org.springframework.test.web.servlet.request.RequestPostProcessor;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
@@ -1206,7 +1205,7 @@ public final class SecurityMockMvcRequestPostProcessors {
return getAuthorities((Collection) scope);
}
String scopes = scope.toString();
if (StringUtils.isBlank(scopes)) {
if (!StringUtils.hasText(scopes)) {
return Collections.emptyList();
}
return getAuthorities(Arrays.asList(scopes.split(" ")));
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -48,7 +48,12 @@ public final class DefaultSecurityFilterChain implements SecurityFilterChain {
}
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
logger.info(LogMessage.format("Will secure %s with %s", requestMatcher, filters));
if (!filters.isEmpty()) {
logger.info(LogMessage.format("Will not secure %s", requestMatcher));
}
else {
logger.info(LogMessage.format("Will secure %s with %s", requestMatcher, filters));
}
this.requestMatcher = requestMatcher;
this.filters = new ArrayList<>(filters);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
@@ -78,10 +79,19 @@ public class FilterInvocation {
}
public FilterInvocation(String contextPath, String servletPath, String method) {
this(contextPath, servletPath, null, null, method);
this(contextPath, servletPath, method, null);
}
public FilterInvocation(String contextPath, String servletPath, String method, ServletContext servletContext) {
this(contextPath, servletPath, null, null, method, servletContext);
}
public FilterInvocation(String contextPath, String servletPath, String pathInfo, String query, String method) {
this(contextPath, servletPath, pathInfo, query, method, null);
}
public FilterInvocation(String contextPath, String servletPath, String pathInfo, String query, String method,
ServletContext servletContext) {
DummyRequest request = new DummyRequest();
contextPath = (contextPath != null) ? contextPath : "/cp";
request.setContextPath(contextPath);
@@ -90,6 +100,7 @@ public class FilterInvocation {
request.setPathInfo(pathInfo);
request.setQueryString(query);
request.setMethod(method);
request.setServletContext(servletContext);
this.request = request;
}
@@ -160,6 +171,8 @@ public class FilterInvocation {
private String method;
private ServletContext servletContext;
private final HttpHeaders headers = new HttpHeaders();
private final Map<String, String[]> parameters = new LinkedHashMap<>();
@@ -290,6 +303,15 @@ public class FilterInvocation {
this.parameters.put(name, values);
}
@Override
public ServletContext getServletContext() {
return this.servletContext;
}
void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
}
static final class UnsupportedOperationExceptionInvocationHandler implements InvocationHandler {

Some files were not shown because too many files have changed in this diff Show More