1
0
mirror of synced 2026-09-05 08:49:54 +00:00

Compare commits

...

81 Commits

Author SHA1 Message Date
Eleftheria Stein 46fdb250dc Release 5.2.10.RELEASE 2021-04-12 18:17:48 +02:00
Eleftheria Stein b500b3ea69 Update to OpenSAML 3.4.6
Closes gh-9607
2021-04-12 10:55:05 +02:00
Eleftheria Stein 59171434d5 Update to hibernate-entitymanager 5.4.30.Final
Closes gh-9606
2021-04-12 10:54:38 +02:00
Eleftheria Stein 5d18dd6d7d Update to Groovy 2.4.21
Closes gh-9605
2021-04-12 10:54:17 +02:00
Eleftheria Stein 41b0e51dbb Update to embedded Apache Tomcat 9.0.45
Closes gh-9604
2021-04-12 10:53:38 +02:00
Eleftheria Stein 310c1148ce Update blockhound to 1.0.6.RELEASE
Closes gh-9603
2021-04-12 10:53:11 +02:00
Eleftheria Stein 93defb2ff2 Update to RSocket 1.0.4
Closes gh-9602
2021-04-12 10:52:33 +02:00
Eleftheria Stein fb7efffad3 Update to Spring Data Moore-SR13
Closes gh-9601
2021-04-12 10:52:09 +02:00
Eleftheria Stein 6db79b70e6 Update to Spring Framework 5.2.13.RELEASE
Close gh-9600
2021-04-12 10:51:41 +02:00
Eleftheria Stein 78a618c260 Update to Reactor Dysprosium-SR18
Closes gh-9599
2021-04-12 10:51:11 +02:00
Eleftheria Stein cfc5256fad Update to GAE 1.9.88
Closes gh-9608
2021-04-12 10:50:46 +02:00
Eleftheria Stein 289b11b873 Update to nohttp 0.0.6.RELEASE
Closes gh-9609
2021-04-12 10:50:22 +02:00
佚名 8dc702c80f Add null check in CsrfFilter and CsrfWebFilter
Solve the problem that CsrfFilter and CsrfWebFilter
throws NPE exception when comparing two byte array
is equal in low JDK version.

When JDK version is lower than 1.8.0_45, method
java.security.MessageDigest#isEqual does not verify
whether the two arrays are null. And the above two
class call this method without null judgment.

ZiQiang Zhao<1694392889@qq.com>

Closes gh-9561
2021-04-09 21:57:14 -06:00
Josh Cummings bd0247adef Next Development Version 2021-02-11 12:22:42 -07:00
Josh Cummings 974156d5fb Release 5.2.9.RELEASE 2021-02-11 10:37:22 -07:00
Rob Winch e2121532a2 Optimize HttpSessionSecurityContextRepository
Closes gh-9387
2021-02-11 09:38:04 -07:00
Rob Winch 7cab7b06c5 Optimize HttpSessionSecurityContextRepository
Closes gh-9387
2021-02-11 09:38:04 -07:00
Josh Cummings f60daa5152 Update to GAE 1.9.86
Closes gh-9442
2021-02-11 09:31:37 -07:00
Josh Cummings f63b770ec5 Update to Tomcat 9.0.43
Closes gh-9441
2021-02-11 09:31:30 -07:00
Josh Cummings 44bb975f82 Update to Jetty 9.4.36.v20210114
Closes gh-9440
2021-02-11 09:31:25 -07:00
Josh Cummings 3cb6b3e5d6 Update to hibernate-validator 6.1.7.Final
Closes gh-9439
2021-02-11 09:31:20 -07:00
Josh Cummings db07cea579 Update to hibernate-entitymanager 5.4.28.Final
Closes gh-9438
2021-02-11 09:31:15 -07:00
Josh Cummings 0fb60c3aa7 Update to thymeleaf-spring5 3.0.12
Closes gh-9437
2021-02-11 09:31:11 -07:00
Josh Cummings 31cb29cb2d Update to Spring Data Moore-SR12
Closes gh-9436
2021-02-11 09:31:03 -07:00
Josh Cummings 46bfc00db2 Update to Reactor Dysprosium-SR16
Closes gh-9435
2021-02-11 09:30:57 -07:00
Josh Cummings 987b14d1d4 Update to Spring Framework 5.2.12.RELEASE
Closes gh-9434
2021-02-11 09:30:52 -07:00
Josh Cummings 1f19ee04e1 Update to Spring Boot 2.2.13.RELEASE
Closes gh-9433
2021-02-11 09:30:39 -07:00
Josh Cummings 005eca7bd9 Fix Test Configuration
- Typo in PlaceholderConfig was causing Windows builds to
resolve the CLASSPATH environment variable

Closes gh-9421
2021-02-10 11:37:32 -07:00
Josh Cummings 68ac3ef36b Polish Tests
Issue gh-9331
2021-02-03 09:34:20 -07:00
happier233 7a5c34ca57 Configure CurrentSecurityContextArgumentResolver BeanResolver
Closes gh-9331
2021-02-03 09:34:13 -07:00
Joe Grandja 542c625d7d Allow null or empty authorities for DefaultOAuth2User
Make DefaultOAuth2User more inline with other part of
spring-security.
For example,
- DefaultOAuth2AuthenticatedPrincipal
- AbstractAuthenticationToken

Closes gh-9366
2021-02-02 04:43:29 -05:00
Rob Winch 4de2dbb4cd Update to spring-build-conventions:0.0.23.1.RELEASE
Fixes use of repo.spring.io
2021-01-26 11:11:05 -06:00
Benjamin Faal 98399c920a Make user info response status check error only
Closes gh-9336
2021-01-25 11:10:03 -05:00
Josh Cummings 52ad49074d Migrate SAML 2.0 Tests and Docs to PCFOne
Issue gh-9362
2021-01-25 08:32:17 -07:00
Josh Cummings 6df5dc4ecf Migrate SAML 2.0 Samples to PCFOne
Closes gh-9362
2021-01-22 13:51:46 -07:00
Josh Cummings 32acb04efe Fix SAML 2.0 Javaconfig Sample
Issue gh-9362
2021-01-22 13:51:37 -07:00
Eleftheria Stein 57dfbeecbb Provide artifactoryUsername/Password in docs and schema jobs 2021-01-22 16:07:17 +01:00
Rob Winch 1181740f79 Constant Time Comparison for CSRF tokens
Closes gh-9291
2021-01-20 16:18:25 -06:00
Rob Winch 628ea00ad4 Fix CsrfWebFilter error message when expected CSRF not found
Closes gh-9337
2021-01-12 11:31:26 -06:00
Josh Cummings 6dc22835fd Renew Sample Certificate
Closes gh-9320
2021-01-04 12:12:17 -07:00
Ovidiu Popa 174b71c017 OidcIdToken cannot be serialized to JSON if token contains claim of type JSONArray or JSONObject
ObjectToListStringConverter and ObjectToMapStringObjectConverter were checking if the source object is of type List or Map and if the first element or key is a String. If we have a JSONArray containing Strings the above check will pass, meaning that a JSONArray will be returned which is not serializable (same applies to JSONObject)

With this change, even if the check is passing a new List or Map will be returned.

Closes gh-9210
2020-12-03 11:42:00 -05:00
Josh Cummings c7b4e4a493 Next Development Version 2020-12-02 19:09:04 -07:00
Josh Cummings d03d31c2e7 Release 5.2.8.RELEASE 2020-12-02 16:06:32 -07:00
Josh Cummings 9976eb9e3a Update to Spring LDAP Core 2.3.3
Closes gh-9245
2020-12-02 16:04:41 -07:00
Josh Cummings 7cf1ca7ab1 Update to Powermock 2.0.9
Closes gh-9244
2020-12-02 16:04:36 -07:00
Josh Cummings 664d9f1ba6 Update to HSQLDB 2.5.1
Closes gh-9243
2020-12-02 16:04:31 -07:00
Josh Cummings f7b160d7a8 Update to Hibernate EntityManager 5.4.25
Closes gh-9242
2020-12-02 16:04:26 -07:00
Josh Cummings 5b60098548 Update to Jetty 9.4.35
Closes gh-9241
2020-12-02 16:04:13 -07:00
Josh Cummings e6b383a9c4 Update to HttpComponents HttpClient 4.5.13
Closes gh-9240
2020-12-02 16:04:07 -07:00
Josh Cummings 5342133494 Update to RSocket 1.0.3
Closes gh-9239
2020-12-02 16:04:01 -07:00
Josh Cummings 7498ad95a1 Update to Reactor Dysprosium-SR14
Closes gh-9238
2020-12-02 16:03:56 -07:00
Josh Cummings 04d382d8d8 Update to Google App Engine 1.9.83
Closes gh-9237
2020-12-02 16:03:51 -07:00
Josh Cummings 83a177d327 Update to Jackson 2.10.5.1
Closes gh-9236
2020-12-02 16:03:46 -07:00
Josh Cummings ebb2afa5e8 Update to Spring Data Moore-SR11
Closes gh-9235
2020-12-02 16:03:41 -07:00
Josh Cummings 276a934bcd Update to Spring 5.2.11
Closes gh-9234
2020-12-02 16:03:35 -07:00
Josh Cummings e4b538146a Update to Spring Boot 2.2.11
Closes gh-9233
2020-12-02 16:03:27 -07:00
Rob Winch 5948f492f5 Add missing withCredentials in Jenkinsfile 2020-11-17 09:28:13 -06:00
Rob Winch 1bac5498dd Fix artifactoryUsername/Password in build.gradle
Missing )
2020-11-17 09:05:37 -06:00
Rob Winch 07479dce6d Use artifactoryUsername/Password in Jenkinsfile 2020-11-17 09:02:49 -06:00
Rob Winch 05c0176b98 allprojects uses artifactoryUsername/Password 2020-11-17 09:02:21 -06:00
Rob Winch 03c2cc846d Use artifactoryUsername/Password for plugin repositories 2020-11-17 08:54:39 -06:00
Hideaki Matsunami ec7deca76f add white space before strong notation. 2020-10-30 15:51:27 -06:00
Ayush Kohli 21c7f7518f Closes gh-8196
Add leveloffset
2020-10-28 15:15:19 -06:00
Josh Cummings 0462c42290 Update Test Controllers
Closes gh-9121
2020-10-12 18:12:02 -06:00
Josh Cummings 21f7187f40 Next Development Version 2020-10-07 11:37:33 -06:00
Josh Cummings 52814ab387 Release 5.2.7.RELEASE 2020-10-07 10:49:34 -06:00
Eleftheria Stein 9c5ce287d7 Update to Hibernate Validator 6.1.6
Closes gh-9086
2020-10-02 17:13:54 +02:00
Eleftheria Stein cb055f1402 Update to Hibernate Entity manager 5.4.22
Closes gh-9087
2020-10-02 17:13:17 +02:00
Eleftheria Stein 8b3777d0f1 Upgrade to embedded Apache Tomcat 9.0.38
Closes gh-9085
2020-10-02 17:12:37 +02:00
Eleftheria Stein 7181f576fc Update to RSocket 1.0.2
Closes gh-9084
2020-10-02 17:12:06 +02:00
Eleftheria Stein dba3dacc19 Update to Spring Data Moore-SR10
Closes gh-9088
2020-10-02 17:11:44 +02:00
Eleftheria Stein 5d0eb32f4d Update to Spring Framework 5.2.9
Closes gh-9083
2020-10-02 17:10:10 +02:00
Eleftheria Stein 984fdd4c00 Update to Reactor Dysprosium-SR12
Closes gh-9082
2020-10-02 17:09:40 +02:00
Eleftheria Stein 58b68625bb Update to Spring Boot 2.2.10
Closes gh-9081
2020-10-02 17:08:58 +02:00
Eleftheria Stein 6a87ccca4a Update to GAE 1.9.82
Closes gh-9080
2020-10-02 14:34:12 +02:00
Eleftheria Stein 925a6685d9 Update to org.aspectj 1.9.6
Closes gh-9079
2020-10-02 14:30:11 +02:00
Malyshau Stanislau bd3e3ed2c5 Add try-with-resources to close stream
Closes gh-9041
2020-09-29 08:46:18 -06:00
Artem Grankin 6dad918e7b Replace expired msdn link with latest web archive copy
Initial link expired in March, 2016. Latest copy found in web archive is from February, 2016
2020-09-28 17:19:42 -06:00
ilee 89ad42a06f Update ssl setup guide link in tomcat server 2020-09-24 13:53:48 -06:00
Tomoki Tsubaki 85889d5e0b Create the CSRF token on the bounded elactic scheduler
The CSRF token is generated by UUID.randomUUID() which is I/O blocking operation.
This commit changes the subscriber thread to the bounded elactic scheduler.

Closes gh-9018
2020-09-16 09:21:11 -06:00
Eleftheria Stein 8c7dbf2e3d Next development version 2020-08-05 17:16:34 +02:00
42 changed files with 588 additions and 288 deletions
Vendored
+44 -22
View File
@@ -8,6 +8,9 @@ properties(projectProperties)
def SUCCESS = hudson.model.Result.SUCCESS.toString()
currentBuild.result = SUCCESS
def ARTIFACTORY_CREDENTIALS = usernamePassword(credentialsId: '02bd1690-b54f-4c9f-819d-a77cb7a9822c', usernameVariable: 'ARTIFACTORY_USERNAME', passwordVariable: 'ARTIFACTORY_PASSWORD')
try {
parallel check: {
stage('Check') {
@@ -15,10 +18,13 @@ try {
checkout scm
sh "git clean -dfx"
try {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
sh "./gradlew clean check --refresh-dependencies --no-daemon --stacktrace"
withCredentials([ARTIFACTORY_CREDENTIALS]) {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
sh "./gradlew clean check -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --refresh-dependencies --no-daemon --stacktrace"
}
}
} catch(Exception e) {
currentBuild.result = 'FAILED: check'
throw e
} finally {
@@ -34,11 +40,13 @@ try {
sh "git clean -dfx"
withCredentials([string(credentialsId: 'spring-sonar.login', variable: 'SONAR_LOGIN')]) {
try {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
if ("master" == env.BRANCH_NAME) {
sh "./gradlew sonarqube -PexcludeProjects='**/samples/**' -Dsonar.host.url=$SPRING_SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN --refresh-dependencies --no-daemon --stacktrace"
} else {
sh "./gradlew sonarqube -PexcludeProjects='**/samples/**' -Dsonar.projectKey='spring-security-${env.BRANCH_NAME}' -Dsonar.projectName='spring-security-${env.BRANCH_NAME}' -Dsonar.host.url=$SPRING_SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN --refresh-dependencies --no-daemon --stacktrace"
withCredentials([ARTIFACTORY_CREDENTIALS]) {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
if ("master" == env.BRANCH_NAME) {
sh "./gradlew sonarqube -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD -PexcludeProjects='**/samples/**' -Dsonar.host.url=$SPRING_SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN --refresh-dependencies --no-daemon --stacktrace"
} else {
sh "./gradlew sonarqube -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD -PexcludeProjects='**/samples/**' -Dsonar.projectKey='spring-security-${env.BRANCH_NAME}' -Dsonar.projectName='spring-security-${env.BRANCH_NAME}' -Dsonar.host.url=$SPRING_SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN --refresh-dependencies --no-daemon --stacktrace"
}
}
}
} catch(Exception e) {
@@ -55,8 +63,10 @@ try {
checkout scm
sh "git clean -dfx"
try {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
sh "./gradlew clean test -PforceMavenRepositories=snapshot -PspringVersion='5.2.+' -PreactorVersion=Dysprosium-BUILD-SNAPSHOT -PspringDataVersion=Lovelace-BUILD-SNAPSHOT --refresh-dependencies --no-daemon --stacktrace"
withCredentials([ARTIFACTORY_CREDENTIALS]) {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
sh "./gradlew clean test -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD -PforceMavenRepositories=snapshot -PspringVersion='5.2.+' -PreactorVersion=Dysprosium-BUILD-SNAPSHOT -PspringDataVersion=Lovelace-BUILD-SNAPSHOT --refresh-dependencies --no-daemon --stacktrace"
}
}
} catch(Exception e) {
currentBuild.result = 'FAILED: snapshots'
@@ -71,8 +81,10 @@ try {
checkout scm
sh "git clean -dfx"
try {
withEnv(["JAVA_HOME=${ tool 'jdk9' }"]) {
sh "./gradlew clean test --refresh-dependencies --no-daemon --stacktrace"
withCredentials([ARTIFACTORY_CREDENTIALS]) {
withEnv(["JAVA_HOME=${ tool 'jdk9' }"]) {
sh "./gradlew clean test -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --refresh-dependencies --no-daemon --stacktrace"
}
}
} catch(Exception e) {
currentBuild.result = 'FAILED: jdk9'
@@ -87,8 +99,10 @@ try {
checkout scm
sh "git clean -dfx"
try {
withEnv(["JAVA_HOME=${ tool 'jdk10' }"]) {
sh "./gradlew clean test --refresh-dependencies --no-daemon --stacktrace"
withCredentials([ARTIFACTORY_CREDENTIALS]) {
withEnv(["JAVA_HOME=${ tool 'jdk10' }"]) {
sh "./gradlew clean test -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --refresh-dependencies --no-daemon --stacktrace"
}
}
} catch(Exception e) {
currentBuild.result = 'FAILED: jdk10'
@@ -103,8 +117,10 @@ try {
checkout scm
sh "git clean -dfx"
try {
withEnv(["JAVA_HOME=${ tool 'jdk11' }"]) {
sh "./gradlew clean test --refresh-dependencies --no-daemon --stacktrace"
withCredentials([ARTIFACTORY_CREDENTIALS]) {
withEnv(["JAVA_HOME=${ tool 'jdk11' }"]) {
sh "./gradlew clean test -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --refresh-dependencies --no-daemon --stacktrace"
}
}
} catch(Exception e) {
currentBuild.result = 'FAILED: jdk11'
@@ -119,8 +135,10 @@ try {
checkout scm
sh "git clean -dfx"
try {
withEnv(["JAVA_HOME=${ tool 'openjdk12' }"]) {
sh "./gradlew clean test --refresh-dependencies --no-daemon --stacktrace"
withCredentials([ARTIFACTORY_CREDENTIALS]) {
withEnv(["JAVA_HOME=${ tool 'openjdk12' }"]) {
sh "./gradlew clean test -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --refresh-dependencies --no-daemon --stacktrace"
}
}
} catch(Exception e) {
currentBuild.result = 'FAILED: jdk12'
@@ -139,7 +157,7 @@ try {
withCredentials([file(credentialsId: 'spring-signing-secring.gpg', variable: 'SIGNING_KEYRING_FILE')]) {
withCredentials([string(credentialsId: 'spring-gpg-passphrase', variable: 'SIGNING_PASSWORD')]) {
withCredentials([usernamePassword(credentialsId: 'oss-token', passwordVariable: 'OSSRH_PASSWORD', usernameVariable: 'OSSRH_USERNAME')]) {
withCredentials([usernamePassword(credentialsId: '02bd1690-b54f-4c9f-819d-a77cb7a9822c', usernameVariable: 'ARTIFACTORY_USERNAME', passwordVariable: 'ARTIFACTORY_PASSWORD')]) {
withCredentials([ARTIFACTORY_CREDENTIALS]) {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
sh "./gradlew deployArtifacts finalizeDeployArtifacts -Psigning.secretKeyRingFile=$SIGNING_KEYRING_FILE -Psigning.keyId=$SPRING_SIGNING_KEYID -Psigning.password='$SIGNING_PASSWORD' -PossrhUsername=$OSSRH_USERNAME -PossrhPassword=$OSSRH_PASSWORD -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --refresh-dependencies --no-daemon --stacktrace"
}
@@ -156,8 +174,10 @@ try {
checkout scm
sh "git clean -dfx"
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
sh "./gradlew deployDocs -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace"
withCredentials([ARTIFACTORY_CREDENTIALS]) {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
sh "./gradlew deployDocs -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME -PartifactoryUsername=$ARTIFACTORY_USERNAME -PartifactoryPassword=$ARTIFACTORY_PASSWORD --refresh-dependencies --no-daemon --stacktrace"
}
}
}
}
@@ -169,8 +189,10 @@ try {
checkout scm
sh "git clean -dfx"
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
sh "./gradlew deploySchema -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace"
withCredentials([ARTIFACTORY_CREDENTIALS]) {
withEnv(["JAVA_HOME=${ tool 'jdk8' }"]) {
sh "./gradlew deploySchema -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace"
}
}
}
}
+27 -3
View File
@@ -1,12 +1,20 @@
buildscript {
dependencies {
classpath 'io.spring.gradle:spring-build-conventions:0.0.23.RELEASE'
classpath 'io.spring.gradle:spring-build-conventions:0.0.23.1.RELEASE'
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
classpath 'io.spring.nohttp:nohttp-gradle:0.0.5.RELEASE'
classpath 'io.spring.nohttp:nohttp-gradle:0.0.6.RELEASE'
classpath "io.freefair.gradle:aspectj-plugin:4.0.2"
}
repositories {
maven { url 'https://repo.spring.io/plugins-snapshot' }
maven {
url = 'https://repo.spring.io/plugins-snapshot'
if (project.hasProperty('artifactoryUsername')) {
credentials {
username "$artifactoryUsername"
password "$artifactoryPassword"
}
}
}
maven { url 'https://plugins.gradle.org/m2/' }
}
}
@@ -35,3 +43,19 @@ subprojects {
options.encoding = "UTF-8"
}
}
if (project.hasProperty('artifactoryUsername')) {
allprojects { project ->
project.repositories { repos ->
all { repo ->
if (!repo.url.toString().startsWith("https://repo.spring.io/")) {
return;
}
repo.credentials {
username = artifactoryUsername
password = artifactoryPassword
}
}
}
}
}
@@ -1933,9 +1933,9 @@ public final class HttpSecurity extends
*
* private RelyingPartyRegistration getSaml2RelyingPartyRegistration() {
* //remote IDP entity ID
* String idpEntityId = "https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php";
* String idpEntityId = "https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php";
* //remote WebSSO Endpoint - Where to Send AuthNRequests to
* String webSsoEndpoint = "https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php";
* String webSsoEndpoint = "https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php";
* //local registration ID
* String registrationId = "simplesamlphp";
* //local entity ID - autogenerated based on URL
@@ -2024,9 +2024,9 @@ public final class HttpSecurity extends
*
* private RelyingPartyRegistration getSaml2RelyingPartyRegistration() {
* //remote IDP entity ID
* String idpEntityId = "https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php";
* String idpEntityId = "https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php";
* //remote WebSSO Endpoint - Where to Send AuthNRequests to
* String webSsoEndpoint = "https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php";
* String webSsoEndpoint = "https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php";
* //local registration ID
* String registrationId = "simplesamlphp";
* //local entity ID - autogenerated based on URL
@@ -183,7 +183,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends
*
* <p>
* Allows customizing the {@link XXssProtectionHeaderWriter} which adds the <a href=
* "https://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx"
* "https://web.archive.org/web/20160201174302/https://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx"
* >X-XSS-Protection header</a>
* </p>
*
@@ -198,7 +198,7 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>> extends
*
* <p>
* Allows customizing the {@link XXssProtectionHeaderWriter} which adds the <a href=
* "https://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx"
* "https://web.archive.org/web/20160201174302/https://blogs.msdn.com/b/ieinternals/archive/2011/01/31/controlling-the-internet-explorer-xss-filter-with-the-x-xss-protection-http-header.aspx"
* >X-XSS-Protection header</a>
* </p>
*
@@ -822,19 +822,19 @@ public class MiscHttpConfigTests {
@RestController
static class AuthenticationController {
@GetMapping("/password")
public String password(@AuthenticationPrincipal Authentication authentication) {
public String password(Authentication authentication) {
return (String) authentication.getCredentials();
}
@GetMapping("/roles")
public String roles(@AuthenticationPrincipal Authentication authentication) {
public String roles(Authentication authentication) {
return authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
}
@GetMapping("/details")
public String details(@AuthenticationPrincipal Authentication authentication) {
public String details(Authentication authentication) {
return authentication.getDetails().getClass().getName();
}
}
@@ -519,7 +519,7 @@ public class OAuth2ResourceServerSpecTests {
@EnableWebFlux
@EnableWebFluxSecurity
static class PlaceholderConfig {
@Value("${classpath:org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests-simple.pub}")
@Value("classpath:org/springframework/security/config/web/server/OAuth2ResourceServerSpecTests-simple.pub")
RSAPublicKey key;
@Bean
@@ -21,6 +21,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.core.SpringVersion;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
@@ -108,8 +109,9 @@ public class SpringSecurityCoreVersion {
*/
private static String getSpringVersion() {
Properties properties = new Properties();
try {
properties.load(SpringSecurityCoreVersion.class.getClassLoader().getResourceAsStream("META-INF/spring-security.versions"));
try (InputStream is = SpringSecurityCoreVersion.class.getClassLoader()
.getResourceAsStream("META-INF/spring-security.versions")) {
properties.load(is);
} catch (IOException | NullPointerException e) {
return null;
}
@@ -233,7 +233,7 @@ You cannot have two separate sessions at once.
So if you log in again in another window or tab you are just reauthenticating in the same session.
The server doesn't know anything about tabs, windows or browser instances.
All it sees are HTTP requests and it ties those to a particular session according to the value of the JSESSIONID cookie that they contain.
When a user authenticates during a session, Spring Security's concurrent session control checks the number of__other authenticated sessions__ that they have.
When a user authenticates during a session, Spring Security's concurrent session control checks the number of __other authenticated sessions__ that they have.
If they are already authenticated with the same session, then re-authenticating will have no effect.
@@ -1,7 +1,7 @@
= Appendix
include::database-schema.adoc[]
include::database-schema.adoc[leveloffset=+1]
include::namespace.adoc[]
@@ -15,7 +15,7 @@ It maps the certificate to an application user and loads that user's set of gran
You should be familiar with using certificates and setting up client authentication for your servlet container before attempting to use it with Spring Security.
Most of the work is in creating and installing suitable certificates and keys.
For example, if you're using Tomcat then read the instructions here https://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html[https://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html].
For example, if you're using Tomcat then read the instructions here https://tomcat.apache.org/tomcat-9.0-doc/ssl-howto.html[https://tomcat.apache.org/tomcat-9.0-doc/ssl-howto.html].
It's important that you get this working before trying it out with Spring Security
@@ -270,9 +270,9 @@ spring:
saml2:
login:
relying-parties:
- entity-id: &idp-entity-id https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php
- entity-id: &idp-entity-id https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php
registration-id: simplesamlphp
web-sso-url: &idp-sso-url https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php
web-sso-url: &idp-sso-url https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php
signing-credentials: &service-provider-credentials
- private-key: |
-----BEGIN PRIVATE KEY-----
+4 -4
View File
@@ -1,5 +1,5 @@
aspectjVersion=1.9.5
gaeVersion=1.9.81
springBootVersion=2.2.9.RELEASE
version=5.2.6.RELEASE
aspectjVersion=1.9.6
gaeVersion=1.9.88
springBootVersion=2.2.13.RELEASE
version=5.2.10.RELEASE
org.gradle.jvmargs=-Xmx3g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError
+40 -40
View File
@@ -1,16 +1,16 @@
if (!project.hasProperty('reactorVersion')) {
ext.reactorVersion = 'Dysprosium-SR10'
ext.reactorVersion = 'Dysprosium-SR18'
}
if (!project.hasProperty('springVersion')) {
ext.springVersion = '5.2.8.RELEASE'
ext.springVersion = '5.2.13.RELEASE'
}
if (!project.hasProperty('springDataVersion')) {
ext.springDataVersion = 'Moore-SR9'
ext.springDataVersion = 'Moore-SR13'
}
ext.rsocketVersion = '1.0.1'
ext.rsocketVersion = '1.0.4'
dependencyManagement {
imports {
@@ -24,12 +24,12 @@ dependencyManagement {
dependency 'opensymphony:sitemesh:2.4.2'
dependency 'org.gebish:geb-spock:0.10.0'
dependency 'org.jasig.cas:cas-server-webapp:4.2.7'
dependency 'org.powermock:powermock-api-mockito2:2.0.7'
dependency 'org.powermock:powermock-api-support:2.0.7'
dependency 'org.powermock:powermock-core:2.0.7'
dependency 'org.powermock:powermock-module-junit4-common:2.0.7'
dependency 'org.powermock:powermock-module-junit4:2.0.7'
dependency 'org.powermock:powermock-reflect:2.0.7'
dependency 'org.powermock:powermock-api-mockito2:2.0.9'
dependency 'org.powermock:powermock-api-support:2.0.9'
dependency 'org.powermock:powermock-core:2.0.9'
dependency 'org.powermock:powermock-module-junit4-common:2.0.9'
dependency 'org.powermock:powermock-module-junit4:2.0.9'
dependency 'org.powermock:powermock-reflect:2.0.9'
dependency 'org.python:jython:2.5.3'
dependency 'org.spockframework:spock-core:1.0-groovy-2.4'
dependency 'org.spockframework:spock-spring:1.0-groovy-2.4'
@@ -44,7 +44,7 @@ dependencyManagement {
dependency 'ch.qos.logback:logback-core:1.2.3'
dependency 'com.fasterxml.jackson.core:jackson-annotations:2.10.5'
dependency 'com.fasterxml.jackson.core:jackson-core:2.10.5'
dependency 'com.fasterxml.jackson.core:jackson-databind:2.10.5'
dependency 'com.fasterxml.jackson.core:jackson-databind:2.10.5.1'
dependency 'com.fasterxml:classmate:1.3.4'
dependency 'com.github.stephenc.jcip:jcip-annotations:1.0-1'
dependency 'com.google.appengine:appengine-api-1.0-sdk:$gaeVersion'
@@ -72,7 +72,7 @@ dependencyManagement {
dependency 'commons-lang:commons-lang:2.6'
dependency 'commons-logging:commons-logging:1.2'
dependency 'dom4j:dom4j:1.6.1'
dependency 'io.projectreactor.tools:blockhound:1.0.4.RELEASE'
dependency 'io.projectreactor.tools:blockhound:1.0.6.RELEASE'
dependency "io.rsocket:rsocket-core:${rsocketVersion}"
dependency "io.rsocket:rsocket-transport-netty:${rsocketVersion}"
dependency 'javax.activation:activation:1.1.1'
@@ -138,19 +138,19 @@ dependencyManagement {
dependency 'org.apache.directory.shared:shared-cursor:0.9.15'
dependency 'org.apache.directory.shared:shared-ldap-constants:0.9.15'
dependency 'org.apache.directory.shared:shared-ldap:0.9.15'
dependency 'org.apache.httpcomponents:httpclient:4.5.12'
dependency 'org.apache.httpcomponents:httpclient:4.5.13'
dependency 'org.apache.httpcomponents:httpcore:4.4.8'
dependency 'org.apache.httpcomponents:httpmime:4.5.3'
dependency 'org.apache.mina:mina-core:2.0.0-M6'
dependency 'org.apache.taglibs:taglibs-standard-impl:1.2.5'
dependency 'org.apache.taglibs:taglibs-standard-jstlel:1.2.5'
dependency 'org.apache.taglibs:taglibs-standard-spec:1.2.5'
dependency 'org.apache.tomcat.embed:tomcat-embed-core:9.0.37'
dependency 'org.apache.tomcat.embed:tomcat-embed-el:9.0.37'
dependency 'org.apache.tomcat.embed:tomcat-embed-jasper:9.0.37'
dependency 'org.apache.tomcat.embed:tomcat-embed-logging-log4j:9.0.37'
dependency 'org.apache.tomcat.embed:tomcat-embed-core:9.0.45'
dependency 'org.apache.tomcat.embed:tomcat-embed-el:9.0.45'
dependency 'org.apache.tomcat.embed:tomcat-embed-jasper:9.0.45'
dependency 'org.apache.tomcat.embed:tomcat-embed-logging-log4j:9.0.45'
dependency 'org.apache.tomcat.embed:tomcat-embed-websocket:8.5.57'
dependency 'org.apache.tomcat:tomcat-annotations-api:9.0.37'
dependency 'org.apache.tomcat:tomcat-annotations-api:9.0.45'
dependency "org.aspectj:aspectjrt:$aspectjVersion"
dependency "org.aspectj:aspectjtools:$aspectjVersion"
dependency "org.aspectj:aspectjweaver:$aspectjVersion"
@@ -158,21 +158,21 @@ dependencyManagement {
dependency 'org.attoparser:attoparser:2.0.4.RELEASE'
dependency 'org.bouncycastle:bcpkix-jdk15on:1.64'
dependency 'org.bouncycastle:bcprov-jdk15on:1.64'
dependency 'org.codehaus.groovy:groovy-all:2.4.20'
dependency 'org.codehaus.groovy:groovy-json:2.4.20'
dependency 'org.codehaus.groovy:groovy:2.4.20'
dependency 'org.codehaus.groovy:groovy-all:2.4.21'
dependency 'org.codehaus.groovy:groovy-json:2.4.21'
dependency 'org.codehaus.groovy:groovy:2.4.21'
dependency 'org.eclipse.jdt:ecj:3.12.3'
dependency 'org.eclipse.jetty.websocket:websocket-api:9.4.27.v20200227'
dependency 'org.eclipse.jetty.websocket:websocket-client:9.4.27.v20200227'
dependency 'org.eclipse.jetty.websocket:websocket-common:9.4.27.v20200227'
dependency 'org.eclipse.jetty:jetty-client:9.4.27.v20200227'
dependency 'org.eclipse.jetty:jetty-http:9.4.27.v20200227'
dependency 'org.eclipse.jetty:jetty-io:9.4.27.v20200227'
dependency 'org.eclipse.jetty:jetty-security:9.4.27.v20200227'
dependency 'org.eclipse.jetty:jetty-server:9.4.27.v20200227'
dependency 'org.eclipse.jetty:jetty-servlet:9.4.27.v20200227'
dependency 'org.eclipse.jetty:jetty-util:9.4.27.v20200227'
dependency 'org.eclipse.jetty:jetty-xml:9.4.27.v20200227'
dependency 'org.eclipse.jetty.websocket:websocket-api:9.4.36.v20210114'
dependency 'org.eclipse.jetty.websocket:websocket-client:9.4.36.v20210114'
dependency 'org.eclipse.jetty.websocket:websocket-common:9.4.36.v20210114'
dependency 'org.eclipse.jetty:jetty-client:9.4.36.v20210114'
dependency 'org.eclipse.jetty:jetty-http:9.4.36.v20210114'
dependency 'org.eclipse.jetty:jetty-io:9.4.36.v20210114'
dependency 'org.eclipse.jetty:jetty-security:9.4.36.v20210114'
dependency 'org.eclipse.jetty:jetty-server:9.4.36.v20210114'
dependency 'org.eclipse.jetty:jetty-servlet:9.4.36.v20210114'
dependency 'org.eclipse.jetty:jetty-util:9.4.36.v20210114'
dependency 'org.eclipse.jetty:jetty-xml:9.4.36.v20210114'
dependency 'org.eclipse.persistence:javax.persistence:2.2.1'
dependency 'org.gebish:geb-ast:0.10.0'
dependency 'org.gebish:geb-core:0.10.0'
@@ -182,9 +182,9 @@ dependencyManagement {
dependency 'org.hibernate.common:hibernate-commons-annotations:5.0.1.Final'
dependency 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final'
dependency 'org.hibernate:hibernate-core:5.2.18.Final'
dependency 'org.hibernate:hibernate-entitymanager:5.4.13.Final'
dependency 'org.hibernate:hibernate-validator:6.1.2.Final'
dependency 'org.hsqldb:hsqldb:2.5.0'
dependency 'org.hibernate:hibernate-entitymanager:5.4.30.Final'
dependency 'org.hibernate:hibernate-validator:6.1.7.Final'
dependency 'org.hsqldb:hsqldb:2.5.1'
dependency 'org.jasig.cas.client:cas-client-core:3.5.1'
dependency 'org.javassist:javassist:3.22.0-CR2'
dependency 'org.jboss.logging:jboss-logging:3.3.1.Final'
@@ -193,9 +193,9 @@ dependencyManagement {
dependency 'org.mockito:mockito-core:3.0.0'
dependency 'org.objenesis:objenesis:2.6'
dependency 'org.openid4java:openid4java-nodeps:0.9.6'
dependency 'org.opensaml:opensaml-core:3.4.5'
dependency 'org.opensaml:opensaml-saml-api:3.4.5'
dependency 'org.opensaml:opensaml-saml-impl:3.4.5'
dependency 'org.opensaml:opensaml-core:3.4.6'
dependency 'org.opensaml:opensaml-saml-api:3.4.6'
dependency 'org.opensaml:opensaml-saml-impl:3.4.6'
dependency 'org.ow2.asm:asm:6.2.1'
dependency 'org.reactivestreams:reactive-streams:1.0.3'
dependency 'org.seleniumhq.selenium:htmlunit-driver:2.36.0'
@@ -209,9 +209,9 @@ dependencyManagement {
dependency 'org.slf4j:slf4j-api:1.7.30'
dependency 'org.slf4j:slf4j-nop:1.7.30'
dependency 'org.sonatype.sisu.inject:cglib:2.2.1-v20090111'
dependency 'org.springframework.ldap:spring-ldap-core:2.3.2.RELEASE'
dependency 'org.springframework.ldap:spring-ldap-core:2.3.3.RELEASE'
dependency 'org.synchronoss.cloud:nio-multipart-parser:1.1.0'
dependency 'org.thymeleaf:thymeleaf-spring5:3.0.11.RELEASE'
dependency 'org.thymeleaf:thymeleaf-spring5:3.0.12.RELEASE'
dependency 'org.unbescape:unbescape:1.1.5.RELEASE'
dependency 'org.w3c.css:sac:1.3'
dependency 'xalan:serializer:2.7.2'
@@ -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.
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.client.userinfo;
@@ -22,6 +21,11 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.nimbusds.oauth2.sdk.ErrorObject;
import com.nimbusds.openid.connect.sdk.UserInfoErrorResponse;
import net.minidev.json.JSONObject;
import reactor.core.publisher.Mono;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
@@ -41,12 +45,6 @@ import org.springframework.util.StringUtils;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import com.nimbusds.oauth2.sdk.ErrorObject;
import com.nimbusds.openid.connect.sdk.UserInfoErrorResponse;
import net.minidev.json.JSONObject;
import reactor.core.publisher.Mono;
/**
* An implementation of an {@link ReactiveOAuth2UserService} that supports standard OAuth 2.0 Provider's.
* <p>
@@ -119,7 +117,7 @@ public class DefaultReactiveOAuth2UserService implements ReactiveOAuth2UserServi
}
Mono<Map<String, Object>> userAttributes = requestHeadersSpec
.retrieve()
.onStatus(s -> s != HttpStatus.OK, response -> parse(response).map(userInfoErrorResponse -> {
.onStatus(HttpStatus::isError, response -> parse(response).map(userInfoErrorResponse -> {
String description = userInfoErrorResponse.getErrorObject().getDescription();
OAuth2Error oauth2Error = new OAuth2Error(
INVALID_USER_INFO_RESPONSE_ERROR_CODE, description,
@@ -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.
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.oauth2.client.userinfo;
import java.time.Duration;
@@ -50,6 +49,7 @@ import org.springframework.security.oauth2.core.user.OAuth2UserAuthority;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
@@ -152,6 +152,24 @@ public class DefaultReactiveOAuth2UserServiceTests {
assertThat(userAuthority.getAttributes()).isEqualTo(user.getAttributes());
}
// gh-9336
@Test
public void loadUserWhenUserInfo201CreatedResponseThenReturnUser() {
// @formatter:off
String userInfoResponse = "{\n"
+ " \"id\": \"user1\",\n"
+ " \"first-name\": \"first\",\n"
+ " \"last-name\": \"last\",\n"
+ " \"middle-name\": \"middle\",\n"
+ " \"address\": \"address\",\n"
+ " \"email\": \"user1@example.com\"\n"
+ "}\n";
// @formatter:on
this.server.enqueue(new MockResponse().setResponseCode(201)
.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).setBody(userInfoResponse));
assertThatCode(() -> this.userService.loadUser(oauth2UserRequest()).block()).doesNotThrowAnyException();
}
// gh-5500
@Test
public void loadUserWhenAuthenticationMethodHeaderSuccessResponseThenHttpMethodGet() throws Exception {
@@ -56,12 +56,6 @@ final class ObjectToListStringConverter implements ConditionalGenericConverter {
if (source == null) {
return null;
}
if (source instanceof List) {
List<?> sourceList = (List<?>) source;
if (!sourceList.isEmpty() && sourceList.get(0) instanceof String) {
return source;
}
}
if (source instanceof Collection) {
Collection<String> results = new ArrayList<>();
for (Object object : ((Collection<?>) source)) {
@@ -52,9 +52,6 @@ final class ObjectToMapStringObjectConverter implements ConditionalGenericConver
return null;
}
Map<?, ?> sourceMap = (Map<?, ?>) source;
if (!sourceMap.isEmpty() && sourceMap.keySet().iterator().next() instanceof String) {
return source;
}
Map<String, Object> result = new HashMap<>();
sourceMap.forEach((k, v) -> result.put(k.toString(), v));
return result;
@@ -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.
@@ -30,6 +30,8 @@ import java.util.SortedSet;
import java.util.Comparator;
import java.util.LinkedHashSet;
import org.springframework.security.core.authority.AuthorityUtils;
/**
* The default implementation of an {@link OAuth2User}.
*
@@ -59,14 +61,16 @@ public class DefaultOAuth2User implements OAuth2User, Serializable {
* @param attributes the attributes about the user
* @param nameAttributeKey the key used to access the user's &quot;name&quot; from {@link #getAttributes()}
*/
public DefaultOAuth2User(Collection<? extends GrantedAuthority> authorities, Map<String, Object> attributes, String nameAttributeKey) {
Assert.notEmpty(authorities, "authorities cannot be empty");
public DefaultOAuth2User(Collection<? extends GrantedAuthority> authorities, Map<String, Object> attributes,
String nameAttributeKey) {
Assert.notEmpty(attributes, "attributes cannot be empty");
Assert.hasText(nameAttributeKey, "nameAttributeKey cannot be empty");
if (!attributes.containsKey(nameAttributeKey)) {
throw new IllegalArgumentException("Missing attribute '" + nameAttributeKey + "' in attributes");
}
this.authorities = Collections.unmodifiableSet(new LinkedHashSet<>(this.sortAuthorities(authorities)));
this.authorities = (authorities != null)
? Collections.unmodifiableSet(new LinkedHashSet<>(this.sortAuthorities(authorities)))
: Collections.unmodifiableSet(new LinkedHashSet<>(AuthorityUtils.NO_AUTHORITIES));
this.attributes = Collections.unmodifiableMap(new LinkedHashMap<>(attributes));
this.nameAttributeKey = nameAttributeKey;
}
@@ -15,9 +15,10 @@
*/
package org.springframework.security.oauth2.core.converter;
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
import org.assertj.core.util.Lists;
import org.junit.Test;
import org.springframework.core.convert.ConversionService;
import java.net.URL;
import java.time.Instant;
@@ -29,6 +30,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.core.convert.ConversionService;
import static org.assertj.core.api.Assertions.assertThat;
/**
@@ -141,9 +144,9 @@ public class ClaimConversionServiceTests {
}
@Test
public void convertCollectionStringWhenListStringThenReturnSame() {
public void convertCollectionStringWhenListStringThenReturnNotSameButEqual() {
List<String> list = Lists.list("1", "2", "3", "4");
assertThat(this.conversionService.convert(list, Collection.class)).isSameAs(list);
assertThat(this.conversionService.convert(list, Collection.class)).isNotSameAs(list).isEqualTo(list);
}
@Test
@@ -152,6 +155,17 @@ public class ClaimConversionServiceTests {
.isEqualTo(Lists.list("1", "2", "3", "4"));
}
@Test
public void convertListStringWhenJsonArrayThenConverts() {
JSONArray jsonArray = new JSONArray();
jsonArray.add("1");
jsonArray.add("2");
jsonArray.add("3");
jsonArray.add(null);
assertThat(this.conversionService.convert(jsonArray, List.class)).isNotInstanceOf(JSONArray.class)
.isEqualTo(Lists.list("1", "2", "3"));
}
@Test
public void convertCollectionStringWhenNotConvertibleThenReturnSingletonList() {
String string = "not-convertible-collection";
@@ -165,9 +179,9 @@ public class ClaimConversionServiceTests {
}
@Test
public void convertListStringWhenListStringThenReturnSame() {
public void convertListStringWhenListStringThenReturnNotSameButEqual() {
List<String> list = Lists.list("1", "2", "3", "4");
assertThat(this.conversionService.convert(list, List.class)).isSameAs(list);
assertThat(this.conversionService.convert(list, List.class)).isNotSameAs(list).isEqualTo(list);
}
@Test
@@ -189,7 +203,7 @@ public class ClaimConversionServiceTests {
}
@Test
public void convertMapStringObjectWhenMapStringObjectThenReturnSame() {
public void convertMapStringObjectWhenMapStringObjectThenReturnNotSameButEqual() {
Map<String, Object> mapStringObject = new HashMap<String, Object>() {
{
put("key1", "value1");
@@ -197,7 +211,8 @@ public class ClaimConversionServiceTests {
put("key3", "value3");
}
};
assertThat(this.conversionService.convert(mapStringObject, Map.class)).isSameAs(mapStringObject);
assertThat(this.conversionService.convert(mapStringObject, Map.class)).isNotSameAs(mapStringObject)
.isEqualTo(mapStringObject);
}
@Test
@@ -219,6 +234,22 @@ public class ClaimConversionServiceTests {
assertThat(this.conversionService.convert(mapIntegerObject, Map.class)).isEqualTo(mapStringObject);
}
@Test
public void convertMapStringObjectWhenJsonObjectThenConverts() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("1", "value1");
jsonObject.put("2", "value2");
Map<String, Object> mapStringObject = new HashMap<String, Object>() {
{
put("1", "value1");
put("2", "value2");
}
};
assertThat(this.conversionService.convert(jsonObject, Map.class)).isNotInstanceOf(JSONObject.class)
.isEqualTo(mapStringObject);
}
@Test
public void convertMapStringObjectWhenNotConvertibleThenReturnNull() {
List<String> notConvertibleList = Lists.list("1", "2", "3", "4");
@@ -15,7 +15,10 @@
*/
package org.springframework.security.oauth2.core.converter;
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
import org.assertj.core.util.Lists;
import org.assertj.core.util.Maps;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.convert.TypeDescriptor;
@@ -45,6 +48,8 @@ public class ClaimTypeConverterTests {
private static final String COLLECTION_STRING_CLAIM = "collection-string-claim";
private static final String LIST_STRING_CLAIM = "list-string-claim";
private static final String MAP_STRING_OBJECT_CLAIM = "map-string-object-claim";
private static final String JSON_ARRAY_CLAIM = "json-array-claim";
private static final String JSON_OBJECT_CLAIM = "json-object-claim";
private ClaimTypeConverter claimTypeConverter;
@Before
@@ -107,7 +112,12 @@ public class ClaimTypeConverterTests {
mapIntegerObject.put(1, "value1");
Map<String, Object> mapStringObject = new HashMap<>();
mapStringObject.put("1", "value1");
JSONArray jsonArray = new JSONArray();
jsonArray.add("1");
List<String> jsonArrayListString = Lists.list("1");
JSONObject jsonObject = new JSONObject();
jsonObject.put("1", "value1");
Map<String, Object> jsonObjectMap = Maps.newHashMap("1", "value1");
Map<String, Object> claims = new HashMap<>();
claims.put(STRING_CLAIM, Boolean.TRUE);
claims.put(BOOLEAN_CLAIM, "true");
@@ -116,7 +126,8 @@ public class ClaimTypeConverterTests {
claims.put(COLLECTION_STRING_CLAIM, listNumber);
claims.put(LIST_STRING_CLAIM, listNumber);
claims.put(MAP_STRING_OBJECT_CLAIM, mapIntegerObject);
claims.put(JSON_ARRAY_CLAIM, jsonArray);
claims.put(JSON_OBJECT_CLAIM, jsonObject);
claims = this.claimTypeConverter.convert(claims);
assertThat(claims.get(STRING_CLAIM)).isEqualTo("true");
@@ -126,6 +137,8 @@ public class ClaimTypeConverterTests {
assertThat(claims.get(COLLECTION_STRING_CLAIM)).isEqualTo(listString);
assertThat(claims.get(LIST_STRING_CLAIM)).isEqualTo(listString);
assertThat(claims.get(MAP_STRING_OBJECT_CLAIM)).isEqualTo(mapStringObject);
assertThat(claims.get(JSON_ARRAY_CLAIM)).isEqualTo(jsonArrayListString);
assertThat(claims.get(JSON_OBJECT_CLAIM)).isEqualTo(jsonObjectMap);
}
@Test
@@ -153,9 +166,9 @@ public class ClaimTypeConverterTests {
assertThat(claims.get(BOOLEAN_CLAIM)).isSameAs(bool);
assertThat(claims.get(INSTANT_CLAIM)).isSameAs(instant);
assertThat(claims.get(URL_CLAIM)).isSameAs(url);
assertThat(claims.get(COLLECTION_STRING_CLAIM)).isSameAs(listString);
assertThat(claims.get(LIST_STRING_CLAIM)).isSameAs(listString);
assertThat(claims.get(MAP_STRING_OBJECT_CLAIM)).isSameAs(mapStringObject);
assertThat(claims.get(COLLECTION_STRING_CLAIM)).isNotSameAs(listString).isEqualTo(listString);
assertThat(claims.get(LIST_STRING_CLAIM)).isNotSameAs(listString).isEqualTo(listString);
assertThat(claims.get(MAP_STRING_OBJECT_CLAIM)).isNotSameAs(mapStringObject).isEqualTo(mapStringObject);
}
@Test
@@ -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.
@@ -18,6 +18,7 @@ package org.springframework.security.oauth2.core.oidc.user;
import org.junit.Test;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.oidc.IdTokenClaimNames;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
@@ -57,11 +58,6 @@ public class DefaultOidcUserTests {
private static final OidcIdToken ID_TOKEN = new OidcIdToken("id-token-value", Instant.EPOCH, Instant.MAX, ID_TOKEN_CLAIMS);
private static final OidcUserInfo USER_INFO = new OidcUserInfo(USER_INFO_CLAIMS);
@Test(expected = IllegalArgumentException.class)
public void constructorWhenAuthoritiesIsNullThenThrowIllegalArgumentException() {
new DefaultOidcUser(null, ID_TOKEN);
}
@Test(expected = IllegalArgumentException.class)
public void constructorWhenIdTokenIsNullThenThrowIllegalArgumentException() {
new DefaultOidcUser(AUTHORITIES, null);
@@ -72,6 +68,26 @@ public class DefaultOidcUserTests {
new DefaultOidcUser(AUTHORITIES, ID_TOKEN, "invalid");
}
@Test
public void constructorWhenAuthoritiesIsNullThenCreatedWithEmptyAuthorities() {
DefaultOidcUser user = new DefaultOidcUser(null, ID_TOKEN);
assertThat(user.getClaims()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB);
assertThat(user.getIdToken()).isEqualTo(ID_TOKEN);
assertThat(user.getName()).isEqualTo(SUBJECT);
assertThat(user.getAuthorities()).isEmpty();
assertThat(user.getAttributes()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB);
}
@Test
public void constructorWhenAuthoritiesIsEmptyThenCreated() {
DefaultOidcUser user = new DefaultOidcUser(AuthorityUtils.NO_AUTHORITIES, ID_TOKEN);
assertThat(user.getClaims()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB);
assertThat(user.getIdToken()).isEqualTo(ID_TOKEN);
assertThat(user.getName()).isEqualTo(SUBJECT);
assertThat(user.getAuthorities()).isEmpty();
assertThat(user.getAttributes()).containsOnlyKeys(IdTokenClaimNames.ISS, IdTokenClaimNames.SUB);
}
@Test
public void constructorWhenAuthoritiesIdTokenProvidedThenCreated() {
DefaultOidcUser user = new DefaultOidcUser(AUTHORITIES, ID_TOKEN);
@@ -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.
@@ -41,16 +41,6 @@ public class DefaultOAuth2UserTests {
private static final Map<String, Object> ATTRIBUTES = Collections.singletonMap(
ATTRIBUTE_NAME_KEY, USERNAME);
@Test(expected = IllegalArgumentException.class)
public void constructorWhenAuthoritiesIsNullThenThrowIllegalArgumentException() {
new DefaultOAuth2User(null, ATTRIBUTES, ATTRIBUTE_NAME_KEY);
}
@Test(expected = IllegalArgumentException.class)
public void constructorWhenAuthoritiesIsEmptyThenThrowIllegalArgumentException() {
new DefaultOAuth2User(Collections.emptySet(), ATTRIBUTES, ATTRIBUTE_NAME_KEY);
}
@Test(expected = IllegalArgumentException.class)
public void constructorWhenAttributesIsNullThenThrowIllegalArgumentException() {
new DefaultOAuth2User(AUTHORITIES, null, ATTRIBUTE_NAME_KEY);
@@ -71,6 +61,22 @@ public class DefaultOAuth2UserTests {
new DefaultOAuth2User(AUTHORITIES, ATTRIBUTES, "invalid");
}
@Test
public void constructorWhenAuthoritiesIsNullThenCreatedWithEmptyAuthorities() {
DefaultOAuth2User user = new DefaultOAuth2User(null, ATTRIBUTES, ATTRIBUTE_NAME_KEY);
assertThat(user.getName()).isEqualTo(USERNAME);
assertThat(user.getAuthorities()).isEmpty();
assertThat(user.getAttributes()).containsOnlyKeys(ATTRIBUTE_NAME_KEY);
}
@Test
public void constructorWhenAuthoritiesIsEmptyThenCreated() {
DefaultOAuth2User user = new DefaultOAuth2User(Collections.emptySet(), ATTRIBUTES, ATTRIBUTE_NAME_KEY);
assertThat(user.getName()).isEqualTo(USERNAME);
assertThat(user.getAuthorities()).isEmpty();
assertThat(user.getAttributes()).containsOnlyKeys(ATTRIBUTE_NAME_KEY);
}
@Test
public void constructorWhenAllParametersProvidedAndValidThenCreated() {
DefaultOAuth2User user = new DefaultOAuth2User(AUTHORITIES, ATTRIBUTES, ATTRIBUTE_NAME_KEY);
@@ -37,9 +37,9 @@ import static org.springframework.util.Assert.notNull;
* A fully configured registration may look like
* <pre>
* //remote IDP entity ID
* String idpEntityId = "https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php";
* String idpEntityId = "https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php";
* //remote WebSSO Endpoint - Where to Send AuthNRequests to
* String webSsoEndpoint = "https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php";
* String webSsoEndpoint = "https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php";
* //local registration ID
* String registrationId = "simplesamlphp";
* //local entity ID - autogenerated based on URL
@@ -124,7 +124,7 @@ public class Saml2LoginIntegrationTests {
public void authenticateRequestWhenUnauthenticatedThenRespondsWithRedirectAuthNRequestXML() throws Exception {
mockMvc.perform(get("http://localhost:8080/saml2/authenticate/simplesamlphp"))
.andExpect(status().is3xxRedirection())
.andExpect(header().string("Location", startsWith("https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php?SAMLRequest=")));
.andExpect(header().string("Location", startsWith("https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php?SAMLRequest=")));
}
@Test
@@ -134,7 +134,7 @@ public class Saml2LoginIntegrationTests {
.param("RelayState", "relay state value with spaces")
)
.andExpect(status().is3xxRedirection())
.andExpect(header().string("Location", startsWith("https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php?SAMLRequest=")))
.andExpect(header().string("Location", startsWith("https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php?SAMLRequest=")))
.andExpect(header().string("Location", containsString("RelayState=relay%20state%20value%20with%20spaces")));
}
@@ -155,7 +155,7 @@ public class Saml2LoginIntegrationTests {
String destination = authnRequest.getDestination();
assertEquals(
"Destination must match",
"https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php",
"https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php",
destination
);
String acsURL = authnRequest.getAssertionConsumerServiceURL();
@@ -317,14 +317,14 @@ public class Saml2LoginIntegrationTests {
Response response = OpenSamlActionTestingSupport.buildResponse();
response.setID("_" + UUID.randomUUID().toString());
response.setDestination("http://localhost:8080/login/saml2/sso/simplesamlphp");
response.setIssuer(buildIssuer("https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php"));
response.setIssuer(buildIssuer("https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php"));
return response;
}
private Assertion buildAssertion(String username) {
Assertion assertion = OpenSamlActionTestingSupport.buildAssertion();
assertion.setIssueInstant(DateTime.now());
assertion.setIssuer(buildIssuer("https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php"));
assertion.setIssuer(buildIssuer("https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php"));
assertion.setSubject(buildSubject(username));
assertion.setConditions(buildConditions());
@@ -346,7 +346,7 @@ public class Saml2LoginIntegrationTests {
final PrivateKey privateKey = KeySupport.decodePrivateKey(key.getBytes(UTF_8), new char[0]);
BasicCredential cred = CredentialSupport.getSimpleCredential(publicKey, privateKey);
cred.setUsageType(usageType);
cred.setEntityId("https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php");
cred.setEntityId("https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php");
return cred;
}
@@ -12,5 +12,5 @@ spring:
verification:
credentials:
- certificate-location: "classpath:credentials/idp-certificate.crt"
entity-id: https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php
sso-url: https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php
entity-id: https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php
sso-url: https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php
@@ -28,7 +28,7 @@
<a th:href="@{/logout}">Log Out</a>
</li>
<li>
<a href="https://simplesaml-for-spring-saml.cfapps.io/module.php/core/authenticate.php?as=example-userpass&logout">
<a href="https://simplesaml-for-spring-saml.apps.pcfone.io/module.php/core/authenticate.php?as=example-userpass&logout">
Log out of SimpleSAMLPhp
</a>
</li>
@@ -5,6 +5,7 @@ dependencies {
compile project(':spring-security-config')
compile "org.bouncycastle:bcprov-jdk15on"
compile "org.bouncycastle:bcpkix-jdk15on"
compile slf4jDependencies
testCompile project(':spring-security-test')
}
@@ -27,6 +27,10 @@ import org.springframework.security.web.session.HttpSessionEventPublisher;
public class MessageSecurityWebApplicationInitializer extends
AbstractSecurityWebApplicationInitializer {
public MessageSecurityWebApplicationInitializer() {
super(SecurityConfig.class);
}
@Override
protected boolean enableHttpSessionEventPublisher() {
return true;
@@ -15,6 +15,8 @@
*/
package org.springframework.security.samples.config;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@@ -23,6 +25,7 @@ import org.springframework.security.converter.RsaKeyConverters;
import org.springframework.security.saml2.credentials.Saml2X509Credential;
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
import java.io.ByteArrayInputStream;
@@ -39,11 +42,12 @@ import static org.springframework.security.saml2.credentials.Saml2X509Credential
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
RelyingPartyRegistration getSaml2AuthenticationConfiguration() throws Exception {
@Bean
RelyingPartyRegistrationRepository getSaml2AuthenticationConfiguration() throws Exception {
//remote IDP entity ID
String idpEntityId = "https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/metadata.php";
String idpEntityId = "https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/metadata.php";
//remote WebSSO Endpoint - Where to Send AuthNRequests to
String webSsoEndpoint = "https://simplesaml-for-spring-saml.cfapps.io/saml2/idp/SSOService.php";
String webSsoEndpoint = "https://simplesaml-for-spring-saml.apps.pcfone.io/saml2/idp/SSOService.php";
//local registration ID
String registrationId = "simplesamlphp";
//local entity ID - autogenerated based on URL
@@ -53,14 +57,14 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
//IDP certificate for verification of incoming messages
Saml2X509Credential idpVerificationCertificate = getVerificationCertificate();
String acsUrlTemplate = "{baseUrl}" + Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;
return RelyingPartyRegistration.withRegistrationId(registrationId)
return new InMemoryRelyingPartyRegistrationRepository(RelyingPartyRegistration.withRegistrationId(registrationId)
.remoteIdpEntityId(idpEntityId)
.idpWebSsoUrl(webSsoEndpoint)
.credentials(c -> c.add(signingCredential))
.credentials(c -> c.add(idpVerificationCertificate))
.localEntityIdTemplate(localEntityIdTemplate)
.assertionConsumerServiceUrlTemplate(acsUrlTemplate)
.build();
.build());
}
@Override
@@ -70,14 +74,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
.authorizeRequests()
.anyRequest().authenticated()
.and()
.saml2Login()
.relyingPartyRegistrationRepository(
new InMemoryRelyingPartyRegistrationRepository(
getSaml2AuthenticationConfiguration()
)
)
.loginProcessingUrl("/sample/jc/saml2/sso/{registrationId}")
;
.saml2Login();
// @formatter:on
}
@@ -0,0 +1,12 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@@ -44,7 +44,7 @@ public class SecurityConfigTests {
public void filterWhenLoginProcessingUrlIsSetInJavaConfigThenTheFilterHasIt() {
FilterChainProxy filterChain = context.getBean(FilterChainProxy.class);
Assert.assertNotNull(filterChain);
final List<Filter> filters = filterChain.getFilters("/sample/jc/saml2/sso/test-id");
final List<Filter> filters = filterChain.getFilters("/login/saml2/sso/one");
Assert.assertNotNull(filters);
Saml2WebSsoAuthenticationFilter filter = (Saml2WebSsoAuthenticationFilter) filters
.stream()
@@ -55,6 +55,6 @@ public class SecurityConfigTests {
.get();
final Object matcher = ReflectionTestUtils.getField(filter, "requiresAuthenticationRequestMatcher");
final Object pattern = ReflectionTestUtils.getField(matcher, "pattern");
Assert.assertEquals("loginProcessingUrl mismatch", "/sample/jc/saml2/sso/{registrationId}", pattern);
Assert.assertEquals("loginProcessingUrl mismatch", "/login/saml2/sso/{registrationId}", pattern);
}
}
@@ -142,13 +142,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
+ response
+ ". You must use the HttpRequestResponseHolder.response after invoking loadContext");
}
// saveContext() might already be called by the response wrapper
// if something in the chain called sendError() or sendRedirect(). This ensures we
// only call it
// once per request.
if (!responseWrapper.isContextSaved()) {
responseWrapper.saveContext(context);
}
responseWrapper.saveContext(context);
}
public boolean containsContext(HttpServletRequest request) {
@@ -305,6 +299,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
private final boolean httpSessionExistedAtStartOfRequest;
private final SecurityContext contextBeforeExecution;
private final Authentication authBeforeExecution;
private boolean isSaveContextInvoked;
/**
* Takes the parameters required to call <code>saveContext()</code> successfully
@@ -355,6 +350,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
// SEC-1587 A non-anonymous context may still be in the session
// SEC-1735 remove if the contextBeforeExecution was not anonymous
httpSession.removeAttribute(springSecurityContextKey);
this.isSaveContextInvoked = true;
}
return;
}
@@ -371,7 +367,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
if (contextChanged(context)
|| httpSession.getAttribute(springSecurityContextKey) == null) {
httpSession.setAttribute(springSecurityContextKey, context);
this.isSaveContextInvoked = true;
if (logger.isDebugEnabled()) {
logger.debug("SecurityContext '" + context
+ "' stored to HttpSession: '" + httpSession);
@@ -381,7 +377,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
}
private boolean contextChanged(SecurityContext context) {
return context != contextBeforeExecution
return this.isSaveContextInvoked || context != contextBeforeExecution
|| context.getAuthentication() != authBeforeExecution;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.csrf;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HashSet;
@@ -28,6 +30,9 @@ import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.log.LogMessage;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.util.UrlUtils;
@@ -35,8 +40,6 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter;
import static java.lang.Boolean.TRUE;
/**
* <p>
* Applies
@@ -58,6 +61,7 @@ import static java.lang.Boolean.TRUE;
* @since 3.2
*/
public final class CsrfFilter extends OncePerRequestFilter {
/**
* The default {@link RequestMatcher} that indicates if CSRF protection is required or
* not. The default is to ignore GET, HEAD, TRACE, OPTIONS and process all other
@@ -66,18 +70,21 @@ public final class CsrfFilter extends OncePerRequestFilter {
public static final RequestMatcher DEFAULT_CSRF_MATCHER = new DefaultRequiresCsrfMatcher();
/**
* The attribute name to use when marking a given request as one that should not be filtered.
* The attribute name to use when marking a given request as one that should not be
* filtered.
*
* To use, set the attribute on your {@link HttpServletRequest}:
* <pre>
* To use, set the attribute on your {@link HttpServletRequest}: <pre>
* CsrfFilter.skipRequest(request);
* </pre>
*/
private static final String SHOULD_NOT_FILTER = "SHOULD_NOT_FILTER" + CsrfFilter.class.getName();
private final Log logger = LogFactory.getLog(getClass());
private final CsrfTokenRepository tokenRepository;
private RequestMatcher requireCsrfProtectionMatcher = DEFAULT_CSRF_MATCHER;
private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
public CsrfFilter(CsrfTokenRepository csrfTokenRepository) {
@@ -87,62 +94,46 @@ public final class CsrfFilter extends OncePerRequestFilter {
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return TRUE.equals(request.getAttribute(SHOULD_NOT_FILTER));
return Boolean.TRUE.equals(request.getAttribute(SHOULD_NOT_FILTER));
}
/*
* (non-Javadoc)
*
* @see
* org.springframework.web.filter.OncePerRequestFilter#doFilterInternal(javax.servlet
* .http.HttpServletRequest, javax.servlet.http.HttpServletResponse,
* javax.servlet.FilterChain)
*/
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
request.setAttribute(HttpServletResponse.class.getName(), response);
CsrfToken csrfToken = this.tokenRepository.loadToken(request);
final boolean missingToken = csrfToken == null;
boolean missingToken = (csrfToken == null);
if (missingToken) {
csrfToken = this.tokenRepository.generateToken(request);
this.tokenRepository.saveToken(csrfToken, request, response);
}
request.setAttribute(CsrfToken.class.getName(), csrfToken);
request.setAttribute(csrfToken.getParameterName(), csrfToken);
if (!this.requireCsrfProtectionMatcher.matches(request)) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Did not protect against CSRF since request did not match "
+ this.requireCsrfProtectionMatcher);
}
filterChain.doFilter(request, response);
return;
}
String actualToken = request.getHeader(csrfToken.getHeaderName());
if (actualToken == null) {
actualToken = request.getParameter(csrfToken.getParameterName());
}
if (!csrfToken.getToken().equals(actualToken)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Invalid CSRF token found for "
+ UrlUtils.buildFullRequestUrl(request));
}
if (missingToken) {
this.accessDeniedHandler.handle(request, response,
new MissingCsrfTokenException(actualToken));
}
else {
this.accessDeniedHandler.handle(request, response,
new InvalidCsrfTokenException(csrfToken, actualToken));
}
if (!equalsConstantTime(csrfToken.getToken(), actualToken)) {
this.logger.debug(
LogMessage.of(() -> "Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request)));
AccessDeniedException exception = (!missingToken) ? new InvalidCsrfTokenException(csrfToken, actualToken)
: new MissingCsrfTokenException(actualToken);
this.accessDeniedHandler.handle(request, response, exception);
return;
}
filterChain.doFilter(request, response);
}
public static void skipRequest(HttpServletRequest request) {
request.setAttribute(SHOULD_NOT_FILTER, TRUE);
request.setAttribute(SHOULD_NOT_FILTER, Boolean.TRUE);
}
/**
@@ -154,14 +145,11 @@ public final class CsrfFilter extends OncePerRequestFilter {
* The default is to apply CSRF protection for any HTTP method other than GET, HEAD,
* TRACE, OPTIONS.
* </p>
*
* @param requireCsrfProtectionMatcher the {@link RequestMatcher} used to determine if
* CSRF protection should be applied.
*/
public void setRequireCsrfProtectionMatcher(
RequestMatcher requireCsrfProtectionMatcher) {
Assert.notNull(requireCsrfProtectionMatcher,
"requireCsrfProtectionMatcher cannot be null");
public void setRequireCsrfProtectionMatcher(RequestMatcher requireCsrfProtectionMatcher) {
Assert.notNull(requireCsrfProtectionMatcher, "requireCsrfProtectionMatcher cannot be null");
this.requireCsrfProtectionMatcher = requireCsrfProtectionMatcher;
}
@@ -172,7 +160,6 @@ public final class CsrfFilter extends OncePerRequestFilter {
* <p>
* The default is to use AccessDeniedHandlerImpl with no arguments.
* </p>
*
* @param accessDeniedHandler the {@link AccessDeniedHandler} to use
*/
public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
@@ -180,20 +167,39 @@ public final class CsrfFilter extends OncePerRequestFilter {
this.accessDeniedHandler = accessDeniedHandler;
}
private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
private final HashSet<String> allowedMethods = new HashSet<>(
Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));
/**
* Constant time comparison to prevent against timing attacks.
* @param expected
* @param actual
* @return
*/
private static boolean equalsConstantTime(String expected, String actual) {
if (expected == actual) {
return true;
}
if (expected == null || actual == null) {
return false;
}
// Encode after ensure that the string is not null
byte[] expectedBytes = Utf8.encode(expected);
byte[] actualBytes = Utf8.encode(actual);
return MessageDigest.isEqual(expectedBytes, actualBytes);
}
private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
private final HashSet<String> allowedMethods = new HashSet<>(Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));
/*
* (non-Javadoc)
*
* @see
* org.springframework.security.web.util.matcher.RequestMatcher#matches(javax.
* servlet.http.HttpServletRequest)
*/
@Override
public boolean matches(HttpServletRequest request) {
return !this.allowedMethods.contains(request.getMethod());
}
@Override
public String toString() {
return "CsrfNotRequired " + this.allowedMethods;
}
}
}
@@ -108,7 +108,7 @@ public final class CurrentSecurityContextArgumentResolver
StandardEvaluationContext context = new StandardEvaluationContext();
context.setRootObject(securityContext);
context.setVariable("this", securityContext);
context.setBeanResolver(this.beanResolver);
Expression expression = this.parser.parseExpression(expressionToParse);
securityContextResult = expression.getValue(context);
}
@@ -26,6 +26,7 @@ import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
/**
* A {@link ServerCsrfTokenRepository} that persists the CSRF token in a cookie named "XSRF-TOKEN" and
@@ -62,7 +63,7 @@ public final class CookieServerCsrfTokenRepository implements ServerCsrfTokenRep
@Override
public Mono<CsrfToken> generateToken(ServerWebExchange exchange) {
return Mono.fromCallable(this::createCsrfToken);
return Mono.fromCallable(this::createCsrfToken).subscribeOn(Schedulers.boundedElastic());
}
@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.
@@ -16,26 +16,28 @@
package org.springframework.security.web.server.csrf;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import reactor.core.publisher.Mono;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FormFieldPart;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static java.lang.Boolean.TRUE;
/**
* <p>
@@ -64,13 +66,14 @@ import static java.lang.Boolean.TRUE;
* @since 5.0
*/
public class CsrfWebFilter implements WebFilter {
public static final ServerWebExchangeMatcher DEFAULT_CSRF_MATCHER = new DefaultRequireCsrfProtectionMatcher();
/**
* The attribute name to use when marking a given request as one that should not be filtered.
* The attribute name to use when marking a given request as one that should not be
* filtered.
*
* To use, set the attribute on your {@link ServerWebExchange}:
* <pre>
* To use, set the attribute on your {@link ServerWebExchange}: <pre>
* CsrfWebFilter.skipExchange(exchange);
* </pre>
*/
@@ -80,32 +83,31 @@ public class CsrfWebFilter implements WebFilter {
private ServerCsrfTokenRepository csrfTokenRepository = new WebSessionServerCsrfTokenRepository();
private ServerAccessDeniedHandler accessDeniedHandler = new HttpStatusServerAccessDeniedHandler(HttpStatus.FORBIDDEN);
private ServerAccessDeniedHandler accessDeniedHandler = new HttpStatusServerAccessDeniedHandler(
HttpStatus.FORBIDDEN);
private boolean isTokenFromMultipartDataEnabled;
public void setAccessDeniedHandler(
ServerAccessDeniedHandler accessDeniedHandler) {
public void setAccessDeniedHandler(ServerAccessDeniedHandler accessDeniedHandler) {
Assert.notNull(accessDeniedHandler, "accessDeniedHandler");
this.accessDeniedHandler = accessDeniedHandler;
}
public void setCsrfTokenRepository(
ServerCsrfTokenRepository csrfTokenRepository) {
public void setCsrfTokenRepository(ServerCsrfTokenRepository csrfTokenRepository) {
Assert.notNull(csrfTokenRepository, "csrfTokenRepository cannot be null");
this.csrfTokenRepository = csrfTokenRepository;
}
public void setRequireCsrfProtectionMatcher(
ServerWebExchangeMatcher requireCsrfProtectionMatcher) {
public void setRequireCsrfProtectionMatcher(ServerWebExchangeMatcher requireCsrfProtectionMatcher) {
Assert.notNull(requireCsrfProtectionMatcher, "requireCsrfProtectionMatcher cannot be null");
this.requireCsrfProtectionMatcher = requireCsrfProtectionMatcher;
}
/**
* Specifies if the {@code CsrfWebFilter} should try to resolve the actual CSRF token from the body of multipart
* data requests.
* @param tokenFromMultipartDataEnabled true if should read from multipart form body, else false. Default is false
* Specifies if the {@code CsrfWebFilter} should try to resolve the actual CSRF token
* from the body of multipart data requests.
* @param tokenFromMultipartDataEnabled true if should read from multipart form body,
* else false. Default is false
*/
public void setTokenFromMultipartDataEnabled(boolean tokenFromMultipartDataEnabled) {
this.isTokenFromMultipartDataEnabled = tokenFromMultipartDataEnabled;
@@ -113,38 +115,33 @@ public class CsrfWebFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
if (TRUE.equals(exchange.getAttribute(SHOULD_NOT_FILTER))) {
if (Boolean.TRUE.equals(exchange.getAttribute(SHOULD_NOT_FILTER))) {
return chain.filter(exchange).then(Mono.empty());
}
return this.requireCsrfProtectionMatcher.matches(exchange)
.filter( matchResult -> matchResult.isMatch())
.filter( matchResult -> !exchange.getAttributes().containsKey(CsrfToken.class.getName()))
.flatMap(m -> validateToken(exchange))
.flatMap(m -> continueFilterChain(exchange, chain))
.switchIfEmpty(continueFilterChain(exchange, chain).then(Mono.empty()))
.onErrorResume(CsrfException.class, e -> this.accessDeniedHandler
.handle(exchange, e));
return this.requireCsrfProtectionMatcher.matches(exchange).filter(MatchResult::isMatch)
.filter((matchResult) -> !exchange.getAttributes().containsKey(CsrfToken.class.getName()))
.flatMap((m) -> validateToken(exchange)).flatMap((m) -> continueFilterChain(exchange, chain))
.switchIfEmpty(continueFilterChain(exchange, chain).then(Mono.empty()))
.onErrorResume(CsrfException.class, (ex) -> this.accessDeniedHandler.handle(exchange, ex));
}
public static void skipExchange(ServerWebExchange exchange) {
exchange.getAttributes().put(SHOULD_NOT_FILTER, TRUE);
exchange.getAttributes().put(SHOULD_NOT_FILTER, Boolean.TRUE);
}
private Mono<Void> validateToken(ServerWebExchange exchange) {
return this.csrfTokenRepository.loadToken(exchange)
.switchIfEmpty(Mono.defer(() -> Mono.error(new CsrfException("CSRF Token has been associated to this client"))))
.filterWhen(expected -> containsValidCsrfToken(exchange, expected))
.switchIfEmpty(Mono.defer(() -> Mono.error(new CsrfException("Invalid CSRF Token"))))
.then();
.switchIfEmpty(
Mono.defer(() -> Mono.error(new CsrfException("An expected CSRF token cannot be found"))))
.filterWhen((expected) -> containsValidCsrfToken(exchange, expected))
.switchIfEmpty(Mono.defer(() -> Mono.error(new CsrfException("Invalid CSRF Token")))).then();
}
private Mono<Boolean> containsValidCsrfToken(ServerWebExchange exchange, CsrfToken expected) {
return exchange.getFormData()
.flatMap(data -> Mono.justOrEmpty(data.getFirst(expected.getParameterName())))
.switchIfEmpty(Mono.justOrEmpty(exchange.getRequest().getHeaders().getFirst(expected.getHeaderName())))
.switchIfEmpty(tokenFromMultipartData(exchange, expected))
.map(actual -> actual.equals(expected.getToken()));
return exchange.getFormData().flatMap((data) -> Mono.justOrEmpty(data.getFirst(expected.getParameterName())))
.switchIfEmpty(Mono.justOrEmpty(exchange.getRequest().getHeaders().getFirst(expected.getHeaderName())))
.switchIfEmpty(tokenFromMultipartData(exchange, expected))
.map((actual) -> equalsConstantTime(actual, expected.getToken()));
}
private Mono<String> tokenFromMultipartData(ServerWebExchange exchange, CsrfToken expected) {
@@ -157,14 +154,12 @@ public class CsrfWebFilter implements WebFilter {
if (!contentType.includes(MediaType.MULTIPART_FORM_DATA)) {
return Mono.empty();
}
return exchange.getMultipartData()
.map(d -> d.getFirst(expected.getParameterName()))
.cast(FormFieldPart.class)
.map(FormFieldPart::value);
return exchange.getMultipartData().map((d) -> d.getFirst(expected.getParameterName())).cast(FormFieldPart.class)
.map(FormFieldPart::value);
}
private Mono<Void> continueFilterChain(ServerWebExchange exchange, WebFilterChain chain) {
return Mono.defer(() ->{
return Mono.defer(() -> {
Mono<CsrfToken> csrfToken = csrfToken(exchange);
exchange.getAttributes().put(CsrfToken.class.getName(), csrfToken);
return chain.filter(exchange);
@@ -172,26 +167,45 @@ public class CsrfWebFilter implements WebFilter {
}
private Mono<CsrfToken> csrfToken(ServerWebExchange exchange) {
return this.csrfTokenRepository.loadToken(exchange)
.switchIfEmpty(generateToken(exchange));
return this.csrfTokenRepository.loadToken(exchange).switchIfEmpty(generateToken(exchange));
}
/**
* Constant time comparison to prevent against timing attacks.
* @param expected
* @param actual
* @return
*/
private static boolean equalsConstantTime(String expected, String actual) {
if (expected == actual) {
return true;
}
if (expected == null || actual == null) {
return false;
}
// Encode after ensure that the string is not null
byte[] expectedBytes = Utf8.encode(expected);
byte[] actualBytes = Utf8.encode(actual);
return MessageDigest.isEqual(expectedBytes, actualBytes);
}
private Mono<CsrfToken> generateToken(ServerWebExchange exchange) {
return this.csrfTokenRepository.generateToken(exchange)
.delayUntil(token -> this.csrfTokenRepository.saveToken(exchange, token));
.delayUntil((token) -> this.csrfTokenRepository.saveToken(exchange, token));
}
private static class DefaultRequireCsrfProtectionMatcher implements ServerWebExchangeMatcher {
private static final Set<HttpMethod> ALLOWED_METHODS = new HashSet<>(
Arrays.asList(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.TRACE, HttpMethod.OPTIONS));
Arrays.asList(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.TRACE, HttpMethod.OPTIONS));
@Override
public Mono<MatchResult> matches(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest())
.flatMap(r -> Mono.justOrEmpty(r.getMethod()))
.filter(m -> ALLOWED_METHODS.contains(m))
.flatMap(m -> MatchResult.notMatch())
.switchIfEmpty(MatchResult.match());
return Mono.just(exchange.getRequest()).flatMap((r) -> Mono.justOrEmpty(r.getMethod()))
.filter(ALLOWED_METHODS::contains).flatMap((m) -> MatchResult.notMatch())
.switchIfEmpty(MatchResult.match());
}
}
}
@@ -16,11 +16,15 @@
package org.springframework.security.web.context;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.servlet.Filter;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
@@ -30,6 +34,7 @@ import javax.servlet.http.HttpSession;
import org.junit.After;
import org.junit.Test;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
@@ -37,10 +42,14 @@ import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Transient;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -174,6 +183,48 @@ public class HttpSessionSecurityContextRepositoryTests {
verify(session).setAttribute(SPRING_SECURITY_CONTEXT_KEY, ctx);
}
@Test
public void saveContextWhenSaveNewContextThenOriginalContextThenOriginalContextSaved() throws Exception {
HttpSessionSecurityContextRepository repository = new HttpSessionSecurityContextRepository();
SecurityContextPersistenceFilter securityContextPersistenceFilter = new SecurityContextPersistenceFilter(
repository);
UserDetails original = User.withUsername("user").password("password").roles("USER").build();
SecurityContext originalContext = createSecurityContext(original);
UserDetails impersonate = User.withUserDetails(original).username("impersonate").build();
SecurityContext impersonateContext = createSecurityContext(impersonate);
MockHttpServletRequest mockRequest = new MockHttpServletRequest();
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
Filter saveImpersonateContext = (request, response, chain) -> {
SecurityContextHolder.setContext(impersonateContext);
// ensure the response is committed to trigger save
response.flushBuffer();
chain.doFilter(request, response);
};
Filter saveOriginalContext = (request, response, chain) -> {
SecurityContextHolder.setContext(originalContext);
chain.doFilter(request, response);
};
HttpServlet servlet = new HttpServlet() {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.getWriter().write("Hi");
}
};
SecurityContextHolder.setContext(originalContext);
MockFilterChain chain = new MockFilterChain(servlet, saveImpersonateContext, saveOriginalContext);
securityContextPersistenceFilter.doFilter(mockRequest, mockResponse, chain);
assertThat(mockRequest.getSession().getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY))
.isEqualTo(originalContext);
}
@Test
public void nonSecurityContextInSessionIsIgnored() {
HttpSessionSecurityContextRepository repo = new HttpSessionSecurityContextRepository();
@@ -668,6 +719,13 @@ public class HttpSessionSecurityContextRepositoryTests {
assertThat(session).isNull();
}
private SecurityContext createSecurityContext(UserDetails userDetails) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userDetails,
userDetails.getPassword(), userDetails.getAuthorities());
SecurityContext securityContext = new SecurityContextImpl(token);
return securityContext;
}
@Transient
private static class SomeTransientAuthentication extends AbstractAuthenticationToken {
SomeTransientAuthentication() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.security.web.csrf;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import javax.servlet.FilterChain;
@@ -89,6 +90,18 @@ public class CsrfFilterTests {
this.response = new MockHttpServletResponse();
}
@Test
public void nullConstantTimeEquals() throws Exception {
Method method = CsrfFilter.class.getDeclaredMethod("equalsConstantTime", String.class, String.class);
method.setAccessible(true);
assertThat(method.invoke(CsrfFilter.class, null, null)).isEqualTo(true);
String expectedToken = "Hello—World";
String actualToken = new String("Hello—World");
assertThat(method.invoke(CsrfFilter.class, expectedToken, null)).isEqualTo(false);
assertThat(method.invoke(CsrfFilter.class, expectedToken, "hello-world")).isEqualTo(false);
assertThat(method.invoke(CsrfFilter.class, expectedToken, actualToken)).isEqualTo(true);
}
@Test(expected = IllegalArgumentException.class)
public void constructorNullRepository() {
new CsrfFilter(null);
@@ -27,6 +27,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.core.MethodParameter;
import org.springframework.expression.BeanResolver;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.authority.AuthorityUtils;
@@ -35,17 +36,28 @@ import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.ReflectionUtils;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.mock;
import static org.mockito.BDDMockito.verify;
import static org.mockito.BDDMockito.when;
/**
* @author Rob Winch
*
*/
public class AuthenticationPrincipalArgumentResolverTests {
private BeanResolver beanResolver;
private Object expectedPrincipal;
private AuthenticationPrincipalArgumentResolver resolver;
@Before
public void setup() {
beanResolver = mock(BeanResolver.class);
resolver = new AuthenticationPrincipalArgumentResolver();
resolver.setBeanResolver(this.beanResolver);
}
@After
@@ -128,6 +140,17 @@ public class AuthenticationPrincipalArgumentResolverTests {
.isEqualTo(this.expectedPrincipal);
}
@Test
public void resolveArgumentSpelBean() throws Exception {
CustomUserPrincipal principal = new CustomUserPrincipal();
setAuthenticationPrincipal(principal);
when(this.beanResolver.resolve(any(), eq("test"))).thenReturn(principal.property);
this.expectedPrincipal = principal.property;
assertThat(this.resolver.resolveArgument(showUserSpelBean(), null, null, null))
.isEqualTo(this.expectedPrincipal);
verify(this.beanResolver).resolve(any(), eq("test"));
}
@Test
public void resolveArgumentSpelCopy() throws Exception {
CopyUserPrincipal principal = new CopyUserPrincipal("property");
@@ -198,6 +221,10 @@ public class AuthenticationPrincipalArgumentResolverTests {
return getMethodParameter("showUserSpel", String.class);
}
private MethodParameter showUserSpelBean() {
return getMethodParameter("showUserSpelBean", String.class);
}
private MethodParameter showUserSpelCopy() {
return getMethodParameter("showUserSpelCopy", CopyUserPrincipal.class);
}
@@ -255,6 +282,9 @@ public class AuthenticationPrincipalArgumentResolverTests {
@AuthenticationPrincipal(expression = "property") String user) {
}
public void showUserSpelBean(@AuthenticationPrincipal(expression = "@test") String user) {
}
public void showUserSpelCopy(
@AuthenticationPrincipal(expression = "new org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolverTests$CopyUserPrincipal(#this)") CopyUserPrincipal user) {
}
@@ -26,6 +26,7 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.core.MethodParameter;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
@@ -38,6 +39,11 @@ import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.mock;
import static org.mockito.BDDMockito.verify;
import static org.mockito.BDDMockito.when;
/**
* @author Dan Zheng
@@ -45,11 +51,16 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
*
*/
public class CurrentSecurityContextArgumentResolverTests {
private BeanResolver beanResolver;
private CurrentSecurityContextArgumentResolver resolver;
@Before
public void setup() {
this.beanResolver = mock(BeanResolver.class);
this.resolver = new CurrentSecurityContextArgumentResolver();
this.resolver.setBeanResolver(this.beanResolver);
}
@After
@@ -104,6 +115,15 @@ public class CurrentSecurityContextArgumentResolverTests {
assertThat(auth1.getPrincipal()).isEqualTo(principal);
}
@Test
public void resolveArgumentWithAuthenticationWithBean() throws Exception {
String principal = "john";
when(this.beanResolver.resolve(any(), eq("test"))).thenReturn(principal);
assertThat(this.resolver.resolveArgument(showSecurityContextAuthenticationWithBean(), null, null, null))
.isEqualTo(principal);
verify(this.beanResolver).resolve(any(), eq("test"));
}
@Test
public void resolveArgumentWithNullAuthentication() {
SecurityContext context = SecurityContextHolder.getContext();
@@ -217,6 +237,10 @@ public class CurrentSecurityContextArgumentResolverTests {
return getMethodParameter("showSecurityContextAuthenticationAnnotation", Authentication.class);
}
public MethodParameter showSecurityContextAuthenticationWithBean() {
return getMethodParameter("showSecurityContextAuthenticationWithBean", String.class);
}
private MethodParameter showSecurityContextAuthenticationWithOptionalPrincipal() {
return getMethodParameter("showSecurityContextAuthenticationWithOptionalPrincipal", Object.class);
}
@@ -279,6 +303,10 @@ public class CurrentSecurityContextArgumentResolverTests {
public void showSecurityContextAuthenticationAnnotation(@CurrentSecurityContext(expression = "authentication") Authentication authentication) {
}
public void showSecurityContextAuthenticationWithBean(
@CurrentSecurityContext(expression = "@test") String name) {
}
public void showSecurityContextAuthenticationWithOptionalPrincipal(@CurrentSecurityContext(expression = "authentication?.principal") Object principal) {
}
@@ -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,6 +16,8 @@
package org.springframework.security.web.server.csrf;
import java.lang.reflect.Method;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -65,8 +67,19 @@ public class CsrfWebFilterTests {
private MockServerWebExchange get = from(
MockServerHttpRequest.get("/"));
private ServerWebExchange post = from(
MockServerHttpRequest.post("/"));
private MockServerWebExchange post = MockServerWebExchange.from(MockServerHttpRequest.post("/"));
@Test
public void nullConstantTimeEquals() throws Exception {
Method method = CsrfWebFilter.class.getDeclaredMethod("equalsConstantTime", String.class, String.class);
method.setAccessible(true);
assertThat(method.invoke(CsrfWebFilter.class, null, null)).isEqualTo(true);
String expectedToken = "Hello—World";
String actualToken = new String("Hello—World");
assertThat(method.invoke(CsrfWebFilter.class, expectedToken, null)).isEqualTo(false);
assertThat(method.invoke(CsrfWebFilter.class, expectedToken, "hello-world")).isEqualTo(false);
assertThat(method.invoke(CsrfWebFilter.class, expectedToken, actualToken)).isEqualTo(true);
}
@Test
public void filterWhenGetThenSessionNotCreatedAndChainContinues() {
@@ -110,6 +123,8 @@ public class CsrfWebFilterTests {
.verifyComplete();
assertThat(this.post.getResponse().getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
StepVerifier.create(this.post.getResponse().getBodyAsString())
.assertNext(b -> assertThat(b).contains("An expected CSRF token cannot be found"));
}
@Test