Compare commits
195 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 38691e6662 | |||
| a8c7ac9010 | |||
| c4f07b879a | |||
| 7c2e2bf75b | |||
| 1569ae2b75 | |||
| d2bfe1c1fa | |||
| 289ffc3e63 | |||
| ccc1c157a5 | |||
| 7cc0c9fb1b | |||
| 2bb61835a0 | |||
| 4da15eea18 | |||
| f836897190 | |||
| 0c088e278a | |||
| 5f8ded6b5f | |||
| 8a4e640f9d | |||
| de5972a062 | |||
| e1f4ec1137 | |||
| 7309b8846a | |||
| 47087ba9c5 | |||
| a487ef2d74 | |||
| 7fa39c8807 | |||
| 64e9ac995a | |||
| 00084cf986 | |||
| c82722c412 | |||
| faec20bc69 | |||
| 6db58cbf8a | |||
| 88c64b3b7b | |||
| 0299808b05 | |||
| 125d33e3cf | |||
| e3abaf7999 | |||
| 33708e61fb | |||
| fbb7691be4 | |||
| 8daa6ec1fd | |||
| 00f466a431 | |||
| ba8844a67e | |||
| 7b98c2ea95 | |||
| 77399ee2b0 | |||
| 86c24da38b | |||
| ef01124eb9 | |||
| 570092c467 | |||
| 3b564b2026 | |||
| f561499683 | |||
| 9b24f66f1c | |||
| 02b2fcc6f0 | |||
| e2e93887af | |||
| d2e5f2ae0d | |||
| 7d81a52780 | |||
| 7112ee3eaa | |||
| 8c74d6cea5 | |||
| e36e2b2a97 | |||
| cff0bde1a3 | |||
| 0f8fa36b93 | |||
| 5556b821e3 | |||
| 770c57ecc9 | |||
| 64f0102425 | |||
| 4272889dc8 | |||
| 6978f51f19 | |||
| b57caf22af | |||
| 2fb8e66bc8 | |||
| 84d173c310 | |||
| 658aff501c | |||
| 3b64cdfc03 | |||
| 97c949d929 | |||
| 5830fda2fa | |||
| 339a05312e | |||
| 14fd213557 | |||
| 77dc3d1a0e | |||
| ca2bc958df | |||
| ab63ebbbee | |||
| 176e6b6000 | |||
| d6ada7fb18 | |||
| e368734557 | |||
| 0d339aeda6 | |||
| 7537aa5124 | |||
| a1c7a39bd3 | |||
| d52aab88af | |||
| 7f5b008266 | |||
| be9ff16583 | |||
| 871bc1c42c | |||
| 7e2eb6894f | |||
| 52bfae5e27 | |||
| a7f3f54a33 | |||
| 583761f916 | |||
| e4ce7249cc | |||
| a0e9c67482 | |||
| 0b587dcef1 | |||
| 2da377ec88 | |||
| e681e44268 | |||
| a64a694c60 | |||
| 32331185dc | |||
| 29a7669101 | |||
| 1f90df6a14 | |||
| d2affef356 | |||
| f5274926cf | |||
| 88ac7a5d2e | |||
| b8a362a60f | |||
| c3dfb1711d | |||
| 59e7a10732 | |||
| f01a13aa52 | |||
| 7b73b94198 | |||
| 860690491a | |||
| c3ba2332da | |||
| 7b599d4770 | |||
| 4e7c9bee46 | |||
| 131078dcae | |||
| a5e4807912 | |||
| e9449beb5f | |||
| 220de60142 | |||
| f0fd09bf79 | |||
| c58fbf0596 | |||
| f736d4dd76 | |||
| 0de2baeabb | |||
| 7f3dee175c | |||
| e846fb43f8 | |||
| de6b32ce77 | |||
| d3c5c6add3 | |||
| af61fae68a | |||
| 62db842865 | |||
| 86c445f491 | |||
| 7e334f8abc | |||
| 963d40a7cd | |||
| d207d03bf7 | |||
| ab098f171d | |||
| 017c218bbd | |||
| 0364518b69 | |||
| 1e76b11b3c | |||
| 171522ebf2 | |||
| 7ccc915b2b | |||
| e9d5bbba34 | |||
| 729418ad7a | |||
| 95c2403968 | |||
| dd43d9198b | |||
| fe274e7553 | |||
| 31a8f8c4df | |||
| af4cc03dec | |||
| 194993ad1a | |||
| 8cba9fbf9d | |||
| 4f06fc6ed1 | |||
| c63d618b26 | |||
| 6488295cad | |||
| f5a525e740 | |||
| 822e59af45 | |||
| 5da55448f9 | |||
| 3e87ef84ae | |||
| 58d50888df | |||
| 3443eac829 | |||
| f2b2e6002f | |||
| df6ed74303 | |||
| 6fae98a6f4 | |||
| 989c1419d5 | |||
| f1691370d6 | |||
| 1cfe84922c | |||
| 4302a86fad | |||
| 9b4ddd7e0a | |||
| d0fbe6b501 | |||
| be91a78781 | |||
| ac8e912ea1 | |||
| 87ec94a321 | |||
| b3cb7b388c | |||
| 71f1cf1e0b | |||
| 829733896c | |||
| 1d4859a7ee | |||
| 0a40ce6181 | |||
| f8fad9d34d | |||
| f45f0b32b2 | |||
| 0b068b9b81 | |||
| dd8048809b | |||
| ad9da129a8 | |||
| 9677ef17f3 | |||
| 2765cd58f1 | |||
| aed203f367 | |||
| a4c088a3b3 | |||
| cdc902d04d | |||
| 3ff825576b | |||
| b83a4c2985 | |||
| 30a1c1af7c | |||
| 6f3e346b76 | |||
| b8d51725c7 | |||
| 3ab6bee856 | |||
| 6d6dc113d8 | |||
| fc553bf19a | |||
| acca3dba69 | |||
| 044157061f | |||
| 1806cebd64 | |||
| 87687bf03f | |||
| d5c953b106 | |||
| 662ab10416 | |||
| 16e17d242e | |||
| 6b68a6d62b | |||
| c562d56ff4 | |||
| 6370906ead | |||
| d1dfb2b9ee | |||
| bfe94f9a80 | |||
| 913f558ff4 | |||
| 21af328349 |
+1
-1
@@ -1,5 +1,5 @@
|
||||
# EditorConfig for Spring Security
|
||||
# see https://github.com/spring-projects/spring-security/blob/master/CONTRIBUTING.md#mind-the-whitespace
|
||||
# see https://github.com/spring-projects/spring-security/blob/master/CONTRIBUTING.adoc#mind-the-whitespace
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
@@ -14,3 +14,4 @@
|
||||
*.odg binary
|
||||
*.otg binary
|
||||
*.png binary
|
||||
*.hsx binary
|
||||
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
REPOSITORY_REF="$1"
|
||||
TOKEN="$2"
|
||||
|
||||
curl -H "Accept: application/vnd.github.everest-preview+json" -H "Authorization: token ${TOKEN}" --request POST --data '{"event_type": "request-build"}' https://api.github.com/repos/${REPOSITORY_REF}/dispatches
|
||||
echo "Requested Build for $REPOSITORY_REF"
|
||||
@@ -0,0 +1,27 @@
|
||||
name: reference
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'gh-pages'
|
||||
|
||||
env:
|
||||
GH_ACTIONS_REPO_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v2
|
||||
- name: Generate antora.yml
|
||||
run: ./gradlew :spring-security-docs:generateAntora
|
||||
- name: Push generated antora files to the spring-generated-docs
|
||||
uses: JamesIves/github-pages-deploy-action@4.1.4
|
||||
with:
|
||||
branch: "spring-security/main" # The branch the action should deploy to.
|
||||
folder: "docs/build/generateAntora" # The folder the action should deploy.
|
||||
repository-name: "spring-io/spring-generated-docs"
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
- name: Dispatch Build Request
|
||||
run: ${GITHUB_WORKSPACE}/.github/actions/dispatch.sh 'spring-io/spring-reference' "$GH_ACTIONS_REPO_TOKEN"
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Cache Gradle packages
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
@@ -71,21 +71,39 @@ jobs:
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Snapshot Tests
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
./gradlew test --refresh-dependencies -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" -PforceMavenRepositories=snapshot -PspringVersion='5.+' -PreactorVersion='20+' -PspringDataVersion='Neumann-BUILD-SNAPSHOT' -PrsocketVersion=1.1.0-SNAPSHOT -PspringBootVersion=2.4.0-SNAPSHOT -PlocksDisabled --stacktrace
|
||||
sonar_analysis:
|
||||
name: Static Code Analysis
|
||||
check_samples:
|
||||
name: Check Samples project
|
||||
needs: [prerequisites]
|
||||
runs-on: ubuntu-latest
|
||||
if: needs.prerequisites.outputs.runjobs
|
||||
env:
|
||||
SONAR_URL: ${{ secrets.SONAR_URL }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Check samples project
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
./gradlew checkSamples --stacktrace
|
||||
check_tangles:
|
||||
name: Check for Package Tangles
|
||||
needs: [ prerequisites ]
|
||||
runs-on: ubuntu-latest
|
||||
if: needs.prerequisites.outputs.runjobs
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
@@ -96,24 +114,15 @@ jobs:
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
|
||||
- name: Run Sonar on given (non-main) branch
|
||||
if: ${{ github.ref != 'refs/heads/main' }}
|
||||
run: |
|
||||
export BRANCH=${GITHUB_REF#refs/heads/}
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
./gradlew sonarqube -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" -PexcludeProjects='**/samples/**' -Dsonar.projectKey="spring-security-${GITHUB_REF#refs/heads/}" -Dsonar.projectName="spring-security-${GITHUB_REF#refs/heads/}" -Dsonar.host.url="$SONAR_URL" -Dsonar.login="$SONAR_TOKEN" --stacktrace
|
||||
- name: Run Sonar on main
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: Check for package tangles
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
./gradlew sonarqube -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" -PexcludeProjects='**/samples/**' -Dsonar.host.url="$SONAR_URL" -Dsonar.login="$SONAR_TOKEN" --stacktrace
|
||||
./gradlew check s101 --stacktrace
|
||||
deploy_artifacts:
|
||||
name: Deploy Artifacts
|
||||
needs: [build_jdk_11, snapshot_tests, sonar_analysis]
|
||||
needs: [build_jdk_11, snapshot_tests, check_samples, check_tangles]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -124,7 +133,7 @@ jobs:
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Deploy artifacts
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -140,7 +149,7 @@ jobs:
|
||||
ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
deploy_docs:
|
||||
name: Deploy Docs
|
||||
needs: [build_jdk_11, snapshot_tests, sonar_analysis]
|
||||
needs: [build_jdk_11, snapshot_tests, check_samples, check_tangles]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -151,7 +160,7 @@ jobs:
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Deploy Docs
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -164,7 +173,7 @@ jobs:
|
||||
DOCS_HOST: ${{ secrets.DOCS_HOST }}
|
||||
deploy_schema:
|
||||
name: Deploy Schema
|
||||
needs: [build_jdk_11, snapshot_tests, sonar_analysis]
|
||||
needs: [build_jdk_11, snapshot_tests, check_samples, check_tangles]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -175,7 +184,7 @@ jobs:
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds' >> ~/.gradle/gradle.properties
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Deploy Schema
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -188,7 +197,7 @@ jobs:
|
||||
DOCS_HOST: ${{ secrets.DOCS_HOST }}
|
||||
notify_result:
|
||||
name: Check for failures
|
||||
needs: [build_jdk_11, snapshot_tests, sonar_analysis, deploy_artifacts, deploy_docs, deploy_schema]
|
||||
needs: [build_jdk_11, snapshot_tests, check_samples, check_tangles, deploy_artifacts, deploy_docs, deploy_schema]
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
+1
-1
@@ -143,7 +143,7 @@ Generate the Release Notes replacing:
|
||||
* <next-version> - Replace with the milestone you are releasing now (i.e. 5.5.0-RC1)
|
||||
|
||||
----
|
||||
$ ./gradlew generateChangelog -P<next-version>
|
||||
$ ./gradlew generateChangelog -PnextVersion=<next-version>
|
||||
----
|
||||
|
||||
* Copy the release notes to your clipboard (your mileage may vary with
|
||||
|
||||
@@ -37,7 +37,9 @@ import org.springframework.util.Assert;
|
||||
* {@link PermissionGrantingStrategy} and {@link AclAuthorizationStrategy} instances.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated since 5.6. In favor of JCache based implementations
|
||||
*/
|
||||
@Deprecated
|
||||
public class EhCacheBasedAclCache implements AclCache {
|
||||
|
||||
private final Ehcache cache;
|
||||
|
||||
+17
-3
@@ -1,10 +1,10 @@
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath "io.spring.javaformat:spring-javaformat-gradle-plugin:$springJavaformatVersion"
|
||||
classpath 'io.spring.nohttp:nohttp-gradle:0.0.8'
|
||||
classpath 'io.spring.nohttp:nohttp-gradle:0.0.10'
|
||||
classpath "io.freefair.gradle:aspectj-plugin:5.3.3.3"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
classpath "com.netflix.nebula:nebula-project-plugin:8.0.0"
|
||||
classpath "com.netflix.nebula:nebula-project-plugin:8.2.0"
|
||||
}
|
||||
repositories {
|
||||
maven { url 'https://repo.spring.io/plugins-snapshot' }
|
||||
@@ -14,7 +14,9 @@ buildscript {
|
||||
|
||||
apply plugin: 'io.spring.nohttp'
|
||||
apply plugin: 'locks'
|
||||
apply plugin: 's101'
|
||||
apply plugin: 'io.spring.convention.root'
|
||||
apply plugin: 'io.spring.convention.include-check-remote'
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
apply plugin: 'org.springframework.security.update-dependencies'
|
||||
apply plugin: 'org.springframework.security.sagan'
|
||||
@@ -56,7 +58,6 @@ updateDependenciesSettings {
|
||||
}
|
||||
addFiles({
|
||||
return [
|
||||
project.file("buildSrc/src/main/java/io/spring/gradle/convention/AsciidoctorConventionPlugin.java"),
|
||||
project.file("buildSrc/src/main/groovy/io/spring/gradle/convention/CheckstylePlugin.groovy")
|
||||
]
|
||||
})
|
||||
@@ -103,6 +104,7 @@ subprojects {
|
||||
}
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = "UTF-8"
|
||||
options.compilerArgs.add("-parameters")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,3 +147,15 @@ nohttp {
|
||||
source.exclude "buildSrc/build/**"
|
||||
|
||||
}
|
||||
|
||||
tasks.register('checkSamples') {
|
||||
includeCheckRemote {
|
||||
repository = 'spring-projects/spring-security-samples'
|
||||
ref = samplesBranch
|
||||
}
|
||||
dependsOn checkRemote
|
||||
}
|
||||
|
||||
s101 {
|
||||
configurationDirectory = project.file("etc/s101")
|
||||
}
|
||||
|
||||
+12
-11
@@ -56,6 +56,10 @@ gradlePlugin {
|
||||
id = "org.springframework.github.changelog"
|
||||
implementationClass = "org.springframework.gradle.github.changelog.GitHubChangelogPlugin"
|
||||
}
|
||||
s101 {
|
||||
id = "s101"
|
||||
implementationClass = "s101.S101Plugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,29 +76,26 @@ dependencies {
|
||||
implementation localGroovy()
|
||||
|
||||
implementation 'io.github.gradle-nexus:publish-plugin:1.1.0'
|
||||
implementation 'io.projectreactor:reactor-core:3.4.8'
|
||||
implementation 'io.projectreactor:reactor-core:3.4.11'
|
||||
implementation 'gradle.plugin.org.gretty:gretty:3.0.1'
|
||||
implementation 'com.apollographql.apollo:apollo-runtime:2.4.5'
|
||||
implementation 'com.github.ben-manes:gradle-versions-plugin:0.38.0'
|
||||
implementation 'io.spring.gradle:docbook-reference-plugin:0.3.1'
|
||||
implementation 'io.spring.gradle:propdeps-plugin:0.0.10.RELEASE'
|
||||
implementation 'com.github.spullara.mustache.java:compiler:0.9.4'
|
||||
implementation 'io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.15'
|
||||
implementation 'io.spring.nohttp:nohttp-gradle:0.0.8'
|
||||
implementation 'org.aim42:htmlSanityCheck:1.1.6'
|
||||
implementation 'org.asciidoctor:asciidoctor-gradle-jvm:3.1.0'
|
||||
implementation 'org.asciidoctor:asciidoctor-gradle-jvm-pdf:3.1.0'
|
||||
implementation 'io.spring.nohttp:nohttp-gradle:0.0.10'
|
||||
implementation 'net.sourceforge.htmlunit:htmlunit:2.37.0'
|
||||
implementation 'org.hidetake:gradle-ssh-plugin:2.10.1'
|
||||
implementation 'org.jfrog.buildinfo:build-info-extractor-gradle:4.9.10'
|
||||
implementation 'org.jfrog.buildinfo:build-info-extractor-gradle:4.24.20'
|
||||
implementation 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1'
|
||||
|
||||
testImplementation platform('org.junit:junit-bom:5.7.2')
|
||||
testImplementation platform('org.junit:junit-bom:5.8.1')
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-engine"
|
||||
testImplementation 'org.apache.commons:commons-io:1.3.2'
|
||||
testImplementation 'org.assertj:assertj-core:3.13.2'
|
||||
testImplementation 'org.mockito:mockito-core:3.11.2'
|
||||
testImplementation 'org.mockito:mockito-junit-jupiter:3.11.2'
|
||||
testImplementation 'org.mockito:mockito-core:3.12.4'
|
||||
testImplementation 'org.mockito:mockito-junit-jupiter:3.12.4'
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:3.14.9"
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
|
||||
package io.spring.gradle
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.TypeChecked
|
||||
import groovy.transform.TypeCheckingMode
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
/**
|
||||
* Checkout a project template from a git repository.
|
||||
*
|
||||
* @author Marcus Da Coregio
|
||||
*/
|
||||
@CompileStatic
|
||||
abstract class IncludeRepoTask extends DefaultTask {
|
||||
|
||||
private static final String DEFAULT_URI_PREFIX = 'https://github.com/'
|
||||
|
||||
/**
|
||||
* Git repository to use. Will be prefixed with {@link #DEFAULT_URI_PREFIX} if it isn't already
|
||||
* @return
|
||||
*/
|
||||
@Input
|
||||
abstract Property<String> getRepository();
|
||||
|
||||
/**
|
||||
* Git reference to use.
|
||||
*/
|
||||
@Input
|
||||
abstract Property<String> getRef()
|
||||
|
||||
/**
|
||||
* Directory where the project template should be copied.
|
||||
*/
|
||||
@OutputDirectory
|
||||
final File outputDirectory = project.file("$project.buildDir/$name")
|
||||
|
||||
@TaskAction
|
||||
void checkoutAndCopy() {
|
||||
outputDirectory.deleteDir()
|
||||
File checkoutDir = checkout(this, getRemoteUri(), ref.get())
|
||||
moveToOutputDir(checkoutDir, outputDirectory)
|
||||
}
|
||||
|
||||
private static File cleanTemporaryDir(Task task, File tmpDir) {
|
||||
if (tmpDir.exists()) {
|
||||
task.project.delete(tmpDir)
|
||||
}
|
||||
return tmpDir
|
||||
}
|
||||
|
||||
static File checkout(Task task, String remoteUri, String ref) {
|
||||
checkout(task, remoteUri, ref, task.getTemporaryDir())
|
||||
}
|
||||
|
||||
@TypeChecked(TypeCheckingMode.SKIP)
|
||||
static File checkout(Task task, String remoteUri, String ref, File checkoutDir) {
|
||||
cleanTemporaryDir(task, checkoutDir)
|
||||
task.project.exec {
|
||||
commandLine = ["git", "clone", "--no-checkout", remoteUri, checkoutDir.absolutePath]
|
||||
errorOutput = System.err
|
||||
}
|
||||
task.project.exec {
|
||||
commandLine = ["git", "checkout", ref]
|
||||
workingDir = checkoutDir
|
||||
errorOutput = System.err
|
||||
}
|
||||
return checkoutDir
|
||||
}
|
||||
|
||||
private static void moveToOutputDir(File tmpDir, File outputDirectory) {
|
||||
File baseDir = tmpDir
|
||||
baseDir.renameTo(outputDirectory)
|
||||
}
|
||||
|
||||
private String getRemoteUri() {
|
||||
String remoteUri = this.repository.get()
|
||||
if (remoteUri.startsWith(DEFAULT_URI_PREFIX)) {
|
||||
return remoteUri
|
||||
}
|
||||
return DEFAULT_URI_PREFIX + remoteUri
|
||||
}
|
||||
}
|
||||
+6
-12
@@ -16,20 +16,17 @@
|
||||
|
||||
package io.spring.gradle.convention;
|
||||
|
||||
import io.spring.gradle.propdeps.PropDepsMavenPlugin;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.GroovyPlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.MavenPlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin
|
||||
import org.gradle.api.plugins.PluginManager;
|
||||
import org.gradle.internal.impldep.org.apache.maven.Maven;
|
||||
import org.gradle.plugins.ide.eclipse.EclipseWtpPlugin;
|
||||
import org.gradle.plugins.ide.idea.IdeaPlugin;
|
||||
import io.spring.gradle.propdeps.PropDepsEclipsePlugin;
|
||||
import io.spring.gradle.propdeps.PropDepsIdeaPlugin;
|
||||
import io.spring.gradle.propdeps.PropDepsPlugin
|
||||
import org.springframework.gradle.CopyPropertiesPlugin;
|
||||
import org.springframework.gradle.CopyPropertiesPlugin
|
||||
import org.springframework.gradle.propdeps.PropDepsEclipsePlugin
|
||||
import org.springframework.gradle.propdeps.PropDepsIdeaPlugin
|
||||
import org.springframework.gradle.propdeps.PropDepsPlugin;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
@@ -40,7 +37,7 @@ public abstract class AbstractSpringJavaPlugin implements Plugin<Project> {
|
||||
public final void apply(Project project) {
|
||||
PluginManager pluginManager = project.getPluginManager();
|
||||
pluginManager.apply(JavaPlugin.class);
|
||||
pluginManager.apply(ManagementConfigurationPlugin.class);
|
||||
pluginManager.apply(ManagementConfigurationPlugin.class)
|
||||
if (project.file("src/main/groovy").exists()
|
||||
|| project.file("src/test/groovy").exists()
|
||||
|| project.file("src/integration-test/groovy").exists()) {
|
||||
@@ -52,9 +49,6 @@ public abstract class AbstractSpringJavaPlugin implements Plugin<Project> {
|
||||
pluginManager.apply(PropDepsPlugin);
|
||||
pluginManager.apply(PropDepsEclipsePlugin);
|
||||
pluginManager.apply(PropDepsIdeaPlugin);
|
||||
project.getPlugins().withType(MavenPlugin) {
|
||||
pluginManager.apply(PropDepsMavenPlugin);
|
||||
}
|
||||
pluginManager.apply("io.spring.convention.tests-configuration");
|
||||
pluginManager.apply("io.spring.convention.integration-test");
|
||||
pluginManager.apply("io.spring.convention.javadoc-options");
|
||||
|
||||
@@ -36,7 +36,7 @@ class CheckstylePlugin implements Plugin<Project> {
|
||||
if (checkstyleDir.exists() && checkstyleDir.directory) {
|
||||
project.getPluginManager().apply('checkstyle')
|
||||
project.dependencies.add('checkstyle', 'io.spring.javaformat:spring-javaformat-checkstyle:0.0.15')
|
||||
project.dependencies.add('checkstyle', 'io.spring.nohttp:nohttp-checkstyle:0.0.8')
|
||||
project.dependencies.add('checkstyle', 'io.spring.nohttp:nohttp-checkstyle:0.0.10')
|
||||
|
||||
project.checkstyle {
|
||||
configDirectory = checkstyleDir
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package io.spring.gradle.convention
|
||||
|
||||
import org.aim42.htmlsanitycheck.HtmlSanityCheckPlugin
|
||||
import org.aim42.htmlsanitycheck.HtmlSanityCheckTask
|
||||
import org.aim42.htmlsanitycheck.check.BrokenHttpLinksChecker
|
||||
import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.plugins.BasePlugin
|
||||
import org.gradle.api.plugins.PluginManager
|
||||
import org.gradle.api.tasks.bundling.Zip
|
||||
|
||||
@@ -19,54 +16,18 @@ public class DocsPlugin implements Plugin<Project> {
|
||||
public void apply(Project project) {
|
||||
|
||||
PluginManager pluginManager = project.getPluginManager();
|
||||
pluginManager.apply("org.asciidoctor.jvm.convert");
|
||||
pluginManager.apply("org.asciidoctor.jvm.pdf");
|
||||
pluginManager.apply(AsciidoctorConventionPlugin);
|
||||
pluginManager.apply(BasePlugin);
|
||||
pluginManager.apply(DeployDocsPlugin);
|
||||
pluginManager.apply(JavadocApiPlugin);
|
||||
pluginManager.apply(HtmlSanityCheckPlugin)
|
||||
|
||||
String projectName = Utils.getProjectName(project);
|
||||
String pdfFilename = projectName + "-reference.pdf";
|
||||
|
||||
project.tasks.withType(AbstractAsciidoctorTask) { t ->
|
||||
project.configure(t) {
|
||||
sources {
|
||||
include "**/*.adoc"
|
||||
exclude '_*/**'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
project.tasks.withType(HtmlSanityCheckTask) { HtmlSanityCheckTask t ->
|
||||
project.configure(t) {
|
||||
t.dependsOn 'asciidoctor'
|
||||
t.checkerClasses = [BrokenHttpLinksChecker]
|
||||
t.checkingResultsDir = new File(project.getBuildDir(), "/report/htmlchecks")
|
||||
t.failOnErrors = false
|
||||
t.httpConnectionTimeout = 3000
|
||||
t.sourceDir = new File(project.getBuildDir(), "/docs/asciidoc/")
|
||||
t.sourceDocuments = project.files(new File(project.getBuildDir(), "/docs/asciidoc/index.html"))
|
||||
}
|
||||
}
|
||||
|
||||
Task docsZip = project.tasks.create('docsZip', Zip) {
|
||||
dependsOn 'api', 'asciidoctor'
|
||||
dependsOn 'api'
|
||||
group = 'Distribution'
|
||||
archiveBaseName = project.rootProject.name
|
||||
archiveClassifier = 'docs'
|
||||
description = "Builds -${classifier} archive containing all " +
|
||||
"Docs for deployment at docs.spring.io"
|
||||
|
||||
from(project.tasks.asciidoctor.outputs) {
|
||||
into 'reference/html5'
|
||||
include '**'
|
||||
}
|
||||
from(project.tasks.asciidoctorPdf.outputs) {
|
||||
into 'reference/pdf'
|
||||
include '**'
|
||||
rename "index.pdf", pdfFilename
|
||||
}
|
||||
from(project.tasks.api.outputs) {
|
||||
into 'api'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package io.spring.gradle.convention
|
||||
|
||||
import io.spring.gradle.IncludeRepoTask
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.GradleBuild
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
|
||||
/**
|
||||
* Adds a set of tasks that make easy to clone a remote repository and perform some task
|
||||
*
|
||||
* @author Marcus Da Coregio
|
||||
*/
|
||||
class IncludeCheckRemotePlugin implements Plugin<Project> {
|
||||
@Override
|
||||
void apply(Project project) {
|
||||
IncludeCheckRemoteExtension extension = project.extensions.create('includeCheckRemote', IncludeCheckRemoteExtension)
|
||||
TaskProvider<IncludeRepoTask> includeRepoTask = project.tasks.register('includeRepo', IncludeRepoTask) { IncludeRepoTask it ->
|
||||
it.repository = extension.repository
|
||||
it.ref = extension.ref
|
||||
}
|
||||
project.tasks.register('checkRemote', GradleBuild) {
|
||||
it.dependsOn 'includeRepo'
|
||||
it.dir = includeRepoTask.get().outputDirectory
|
||||
it.tasks = extension.getTasks()
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class IncludeCheckRemoteExtension {
|
||||
|
||||
/**
|
||||
* Git repository to clone
|
||||
*/
|
||||
String repository;
|
||||
|
||||
/**
|
||||
* Git ref to checkout
|
||||
*/
|
||||
String ref
|
||||
|
||||
/**
|
||||
* Task to run in the repository
|
||||
*/
|
||||
List<String> tasks = ['check']
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package io.spring.gradle.convention
|
||||
|
||||
import io.spring.gradle.propdeps.PropDepsPlugin
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
@@ -24,6 +23,7 @@ import org.gradle.api.plugins.JavaPlugin
|
||||
import org.gradle.api.tasks.testing.Test
|
||||
import org.gradle.plugins.ide.eclipse.EclipsePlugin
|
||||
import org.gradle.plugins.ide.idea.IdeaPlugin
|
||||
import org.springframework.gradle.propdeps.PropDepsPlugin
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -54,7 +54,7 @@ public class IntegrationTestPlugin implements Plugin<Project> {
|
||||
}
|
||||
project.configurations {
|
||||
integrationTestCompile {
|
||||
extendsFrom testCompile, testImplementation
|
||||
extendsFrom testImplementation
|
||||
}
|
||||
integrationTestRuntime {
|
||||
extendsFrom integrationTestCompile, testRuntime, testRuntimeOnly
|
||||
|
||||
+19
-4
@@ -1,7 +1,21 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.gradle.convention;
|
||||
|
||||
|
||||
import io.spring.gradle.propdeps.PropDepsPlugin;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.ConfigurationContainer;
|
||||
@@ -12,6 +26,8 @@ import org.gradle.api.publish.PublishingExtension;
|
||||
import org.gradle.api.publish.maven.MavenPublication;
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
|
||||
|
||||
import org.springframework.gradle.propdeps.PropDepsPlugin;
|
||||
|
||||
/**
|
||||
* Creates a Management configuration that is appropriate for adding a platform to that is not exposed externally. If
|
||||
* the JavaPlugin is applied, the compileClasspath, runtimeClasspath, testCompileClasspath, and testRuntimeClasspath
|
||||
@@ -55,5 +71,4 @@ public class ManagementConfigurationPlugin implements Plugin<Project> {
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package io.spring.gradle.convention
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
|
||||
public class OssrhPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
if(project.hasProperty('ossrhUsername')) {
|
||||
project.uploadArchives {
|
||||
repositories {
|
||||
mavenDeployer {
|
||||
repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
|
||||
authentication(userName: project.ossrhUsername, password: project.ossrhPassword)
|
||||
}
|
||||
|
||||
snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") {
|
||||
authentication(userName: project.ossrhUsername, password: project.ossrhPassword)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,10 +37,7 @@ class SpringModulePlugin extends AbstractSpringJavaPlugin {
|
||||
def deployArtifacts = project.task("deployArtifacts")
|
||||
deployArtifacts.group = 'Deploy tasks'
|
||||
deployArtifacts.description = "Deploys the artifacts to either Artifactory or Maven Central"
|
||||
if (Utils.isRelease(project)) {
|
||||
deployArtifacts.dependsOn project.tasks.uploadArchives
|
||||
}
|
||||
else {
|
||||
if (!Utils.isRelease(project)) {
|
||||
deployArtifacts.dependsOn project.tasks.artifactoryPublish
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,208 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.spring.gradle.convention;
|
||||
|
||||
import org.asciidoctor.gradle.base.AsciidoctorAttributeProvider;
|
||||
import org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask;
|
||||
import org.asciidoctor.gradle.jvm.AsciidoctorJExtension;
|
||||
import org.asciidoctor.gradle.jvm.AsciidoctorJPlugin;
|
||||
import org.asciidoctor.gradle.jvm.AsciidoctorTask;
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.DependencySet;
|
||||
import org.gradle.api.artifacts.dsl.RepositoryHandler;
|
||||
import org.gradle.api.file.CopySpec;
|
||||
import org.gradle.api.file.FileTree;
|
||||
import org.gradle.api.tasks.Sync;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Conventions that are applied in the presence of the {@link AsciidoctorJPlugin}. When
|
||||
* the plugin is applied:
|
||||
*
|
||||
* <ul>
|
||||
* <li>All warnings are made fatal.
|
||||
* <li>A task is created to resolve and unzip our documentation resources (CSS and
|
||||
* Javascript).
|
||||
* <li>For each {@link AsciidoctorTask} (HTML only):
|
||||
* <ul>
|
||||
* <li>A configuration named asciidoctorExtensions is ued to add the
|
||||
* <a href="https://github.com/spring-io/spring-asciidoctor-extensions#block-switch">block
|
||||
* switch</a> extension
|
||||
* <li>{@code doctype} {@link AsciidoctorTask#options(Map) option} is configured.
|
||||
* <li>{@link AsciidoctorTask#attributes(Map) Attributes} are configured for syntax
|
||||
* highlighting, CSS styling, docinfo, etc.
|
||||
* </ul>
|
||||
* <li>For each {@link AbstractAsciidoctorTask} (HTML and PDF):
|
||||
* <ul>
|
||||
* <li>{@link AsciidoctorTask#attributes(Map) Attributes} are configured to enable
|
||||
* warnings for references to missing attributes, the year is added as @{code today-year},
|
||||
* etc
|
||||
* <li>{@link AbstractAsciidoctorTask#baseDirFollowsSourceDir() baseDirFollowsSourceDir()}
|
||||
* is enabled.
|
||||
* </ul>
|
||||
* </ul>
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class AsciidoctorConventionPlugin implements Plugin<Project> {
|
||||
|
||||
public void apply(Project project) {
|
||||
project.getPlugins().withType(AsciidoctorJPlugin.class, (asciidoctorPlugin) -> {
|
||||
createDefaultAsciidoctorRepository(project);
|
||||
makeAllWarningsFatal(project);
|
||||
Sync unzipResources = createUnzipDocumentationResourcesTask(project);
|
||||
project.getTasks().withType(AbstractAsciidoctorTask.class, (asciidoctorTask) -> {
|
||||
asciidoctorTask.dependsOn(unzipResources);
|
||||
configureExtensions(project, asciidoctorTask);
|
||||
configureCommonAttributes(project, asciidoctorTask);
|
||||
configureOptions(asciidoctorTask);
|
||||
asciidoctorTask.baseDirFollowsSourceDir();
|
||||
asciidoctorTask.useIntermediateWorkDir();
|
||||
asciidoctorTask.resources(new Action<CopySpec>() {
|
||||
@Override
|
||||
public void execute(CopySpec resourcesSpec) {
|
||||
resourcesSpec.from(unzipResources);
|
||||
resourcesSpec.from(asciidoctorTask.getSourceDir(), new Action<CopySpec>() {
|
||||
@Override
|
||||
public void execute(CopySpec resourcesSrcDirSpec) {
|
||||
// https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/523
|
||||
// For now copy the entire sourceDir over so that include files are
|
||||
// available in the intermediateWorkDir
|
||||
// resourcesSrcDirSpec.include("images/**");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if (asciidoctorTask instanceof AsciidoctorTask) {
|
||||
configureHtmlOnlyAttributes(project, asciidoctorTask);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void createDefaultAsciidoctorRepository(Project project) {
|
||||
project.getGradle().afterProject(new Action<Project>() {
|
||||
@Override
|
||||
public void execute(Project project) {
|
||||
RepositoryHandler repositories = project.getRepositories();
|
||||
if (repositories.isEmpty()) {
|
||||
repositories.mavenCentral();
|
||||
repositories.maven(repo -> {
|
||||
repo.setUrl(URI.create("https://repo.spring.io/release"));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void makeAllWarningsFatal(Project project) {
|
||||
project.getExtensions().getByType(AsciidoctorJExtension.class).fatalWarnings(".*");
|
||||
}
|
||||
|
||||
private void configureExtensions(Project project, AbstractAsciidoctorTask asciidoctorTask) {
|
||||
Configuration extensionsConfiguration = project.getConfigurations().maybeCreate("asciidoctorExtensions");
|
||||
extensionsConfiguration.defaultDependencies(new Action<DependencySet>() {
|
||||
@Override
|
||||
public void execute(DependencySet dependencies) {
|
||||
dependencies.add(project.getDependencies().create("io.spring.asciidoctor:spring-asciidoctor-extensions-block-switch:0.4.2.RELEASE"));
|
||||
}
|
||||
});
|
||||
asciidoctorTask.configurations(extensionsConfiguration);
|
||||
}
|
||||
|
||||
private Sync createUnzipDocumentationResourcesTask(Project project) {
|
||||
Configuration documentationResources = project.getConfigurations().maybeCreate("documentationResources");
|
||||
documentationResources.getDependencies()
|
||||
.add(project.getDependencies().create("io.spring.docresources:spring-doc-resources:0.2.5"));
|
||||
Sync unzipResources = project.getTasks().create("unzipDocumentationResources",
|
||||
Sync.class, new Action<Sync>() {
|
||||
@Override
|
||||
public void execute(Sync sync) {
|
||||
sync.dependsOn(documentationResources);
|
||||
sync.from(new Callable<List<FileTree>>() {
|
||||
@Override
|
||||
public List<FileTree> call() throws Exception {
|
||||
List<FileTree> result = new ArrayList<>();
|
||||
documentationResources.getAsFileTree().forEach(new Consumer<File>() {
|
||||
@Override
|
||||
public void accept(File file) {
|
||||
result.add(project.zipTree(file));
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
});
|
||||
File destination = new File(project.getBuildDir(), "docs/resources");
|
||||
sync.into(project.relativePath(destination));
|
||||
}
|
||||
});
|
||||
return unzipResources;
|
||||
}
|
||||
|
||||
private void configureOptions(AbstractAsciidoctorTask asciidoctorTask) {
|
||||
asciidoctorTask.options(Collections.singletonMap("doctype", "book"));
|
||||
}
|
||||
|
||||
private void configureHtmlOnlyAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) {
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("source-highlighter", "highlight.js");
|
||||
attributes.put("highlightjsdir", "js/highlight");
|
||||
attributes.put("highlightjs-theme", "github");
|
||||
attributes.put("linkcss", true);
|
||||
attributes.put("icons", "font");
|
||||
attributes.put("stylesheet", "css/spring.css");
|
||||
asciidoctorTask.getAttributeProviders().add(new AsciidoctorAttributeProvider() {
|
||||
@Override
|
||||
public Map<String, Object> getAttributes() {
|
||||
Object version = project.getVersion();
|
||||
Map<String, Object> attrs = new HashMap<>();
|
||||
if (version != null && version.toString() != Project.DEFAULT_VERSION) {
|
||||
attrs.put("revnumber", version);
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
});
|
||||
asciidoctorTask.attributes(attributes);
|
||||
}
|
||||
|
||||
private void configureCommonAttributes(Project project, AbstractAsciidoctorTask asciidoctorTask) {
|
||||
Map<String, Object> attributes = new HashMap<>();
|
||||
attributes.put("attribute-missing", "warn");
|
||||
attributes.put("icons", "font");
|
||||
attributes.put("idprefix", "");
|
||||
attributes.put("idseparator", "-");
|
||||
attributes.put("docinfo", "shared");
|
||||
attributes.put("sectanchors", "");
|
||||
attributes.put("sectnums", "");
|
||||
attributes.put("today-year", LocalDate.now().getYear());
|
||||
asciidoctorTask.attributes(attributes);
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.propdeps
|
||||
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.plugins.ide.eclipse.EclipsePlugin
|
||||
|
||||
/**
|
||||
* Plugin to allow optional and provided dependency configurations to work with the
|
||||
* standard gradle 'eclipse' plugin
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class PropDepsEclipsePlugin implements Plugin<Project> {
|
||||
|
||||
public void apply(Project project) {
|
||||
project.plugins.apply(PropDepsPlugin)
|
||||
project.plugins.apply(EclipsePlugin)
|
||||
|
||||
project.eclipse {
|
||||
classpath {
|
||||
plusConfigurations += [project.configurations.provided, project.configurations.optional]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.propdeps
|
||||
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.plugins.ide.idea.IdeaPlugin
|
||||
|
||||
/**
|
||||
* Plugin to allow optional and provided dependency configurations to work with the
|
||||
* standard gradle 'idea' plugin
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Brian Clozel
|
||||
* @link https://youtrack.jetbrains.com/issue/IDEA-107046
|
||||
* @link https://youtrack.jetbrains.com/issue/IDEA-117668
|
||||
*/
|
||||
class PropDepsIdeaPlugin implements Plugin<Project> {
|
||||
|
||||
public void apply(Project project) {
|
||||
project.plugins.apply(PropDepsPlugin)
|
||||
project.plugins.apply(IdeaPlugin)
|
||||
project.idea.module {
|
||||
// IDEA internally deals with 4 scopes : COMPILE, TEST, PROVIDED, RUNTIME
|
||||
// but only PROVIDED seems to be picked up
|
||||
scopes.PROVIDED.plus += [project.configurations.provided]
|
||||
scopes.PROVIDED.plus += [project.configurations.optional]
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.propdeps
|
||||
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.plugins.JavaLibraryPlugin
|
||||
import org.gradle.api.plugins.JavaPlugin
|
||||
import org.gradle.api.tasks.javadoc.Javadoc
|
||||
|
||||
/**
|
||||
* Plugin to allow 'optional' and 'provided' dependency configurations
|
||||
*
|
||||
* As stated in the maven documentation, provided scope "is only available on the compilation and test classpath,
|
||||
* and is not transitive".
|
||||
*
|
||||
* This plugin creates two new configurations, and each one:
|
||||
* <ul>
|
||||
* <li>is a parent of the compile configuration</li>
|
||||
* <li>is not visible, not transitive</li>
|
||||
* <li>all dependencies are excluded from the default configuration</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Brian Clozel
|
||||
* @author Rob Winch
|
||||
*
|
||||
* @see <a href="https://www.gradle.org/docs/current/userguide/java_plugin.html#N121CF">Maven documentation</a>
|
||||
* @see <a href="https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope">Gradle configurations</a>
|
||||
* @see PropDepsEclipsePlugin
|
||||
* @see PropDepsIdeaPlugin
|
||||
*/
|
||||
class PropDepsPlugin implements Plugin<Project> {
|
||||
|
||||
public void apply(Project project) {
|
||||
project.plugins.apply(JavaPlugin)
|
||||
|
||||
Configuration provided = addConfiguration(project, "provided")
|
||||
Configuration optional = addConfiguration(project, "optional")
|
||||
|
||||
Javadoc javadoc = project.tasks.getByName(JavaPlugin.JAVADOC_TASK_NAME)
|
||||
javadoc.classpath = javadoc.classpath.plus(provided).plus(optional)
|
||||
}
|
||||
|
||||
private Configuration addConfiguration(Project project, String name) {
|
||||
Configuration configuration = project.configurations.create(name)
|
||||
configuration.extendsFrom(project.configurations.implementation)
|
||||
project.plugins.withType(JavaLibraryPlugin, {
|
||||
configuration.extendsFrom(project.configurations.api)
|
||||
})
|
||||
|
||||
project.sourceSets.all {
|
||||
compileClasspath += configuration
|
||||
runtimeClasspath += configuration
|
||||
}
|
||||
|
||||
return configuration
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -166,7 +166,7 @@ public class UpdateDependenciesExtension {
|
||||
}
|
||||
|
||||
public DependencyExcludes releaseCandidatesVersions() {
|
||||
this.actions.add(excludeVersionWithRegex("(?i).*?rc\\d+.*", "a release candidate version"));
|
||||
this.actions.add(excludeVersionWithRegex("(?i).*?rc.*", "a release candidate version"));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package s101;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
public class S101Configure extends DefaultTask {
|
||||
@TaskAction
|
||||
public void configure() throws Exception {
|
||||
S101PluginExtension extension = getProject().getExtensions().getByType(S101PluginExtension.class);
|
||||
File buildDirectory = extension.getInstallationDirectory().get();
|
||||
File projectDirectory = extension.getConfigurationDirectory().get();
|
||||
S101Configurer configurer = new S101Configurer(getProject());
|
||||
configurer.configure(buildDirectory, projectDirectory);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package s101;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.WebClient;
|
||||
import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
|
||||
import com.gargoylesoftware.htmlunit.html.HtmlPage;
|
||||
import com.github.mustachejava.DefaultMustacheFactory;
|
||||
import com.github.mustachejava.Mustache;
|
||||
import com.github.mustachejava.MustacheFactory;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.logging.Logger;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.SourceSetContainer;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
public class S101Configurer {
|
||||
private static final Pattern VERSION = Pattern.compile("<local-project .* version=\"(.*?)\"");
|
||||
|
||||
private static final int BUFFER = 1024;
|
||||
private static final long TOOBIG = 0x10000000; // ~268M
|
||||
private static final int TOOMANY = 200;
|
||||
|
||||
private final MustacheFactory mustache = new DefaultMustacheFactory();
|
||||
private final Mustache hspTemplate;
|
||||
private final Mustache repositoryTemplate;
|
||||
|
||||
private final Project project;
|
||||
private final Logger logger;
|
||||
|
||||
public S101Configurer(Project project) {
|
||||
this.project = project;
|
||||
this.logger = project.getLogger();
|
||||
Resource template = new ClassPathResource("s101/project.java.hsp");
|
||||
try (InputStream is = template.getInputStream()) {
|
||||
this.hspTemplate = this.mustache.compile(new InputStreamReader(is), "project");
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
template = new ClassPathResource("s101/repository.xml");
|
||||
try (InputStream is = template.getInputStream()) {
|
||||
this.repositoryTemplate = this.mustache.compile(new InputStreamReader(is), "repository");
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void install(File installationDirectory, File configurationDirectory) {
|
||||
deleteDirectory(installationDirectory);
|
||||
installBuildTool(installationDirectory, configurationDirectory);
|
||||
}
|
||||
|
||||
public void configure(File installationDirectory, File configurationDirectory) {
|
||||
deleteDirectory(configurationDirectory);
|
||||
String version = computeVersionFromInstallation(installationDirectory);
|
||||
configureProject(version, configurationDirectory);
|
||||
}
|
||||
|
||||
private String computeVersionFromInstallation(File installationDirectory) {
|
||||
File buildJar = new File(installationDirectory, "structure101-java-build.jar");
|
||||
try (JarInputStream input = new JarInputStream(new FileInputStream(buildJar))) {
|
||||
JarEntry entry;
|
||||
while ((entry = input.getNextJarEntry()) != null) {
|
||||
if (entry.getName().contains("structure101-build.properties")) {
|
||||
Properties properties = new Properties();
|
||||
properties.load(input);
|
||||
return properties.getProperty("s101-build");
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
throw new IllegalStateException("Unable to determine Structure101 version");
|
||||
}
|
||||
|
||||
private boolean deleteDirectory(File directoryToBeDeleted) {
|
||||
File[] allContents = directoryToBeDeleted.listFiles();
|
||||
if (allContents != null) {
|
||||
for (File file : allContents) {
|
||||
deleteDirectory(file);
|
||||
}
|
||||
}
|
||||
return directoryToBeDeleted.delete();
|
||||
}
|
||||
|
||||
private String installBuildTool(File installationDirectory, File configurationDirectory) {
|
||||
String source = "https://structure101.com/binaries/v6";
|
||||
try (final WebClient webClient = new WebClient()) {
|
||||
HtmlPage page = webClient.getPage(source);
|
||||
for (HtmlAnchor anchor : page.getAnchors()) {
|
||||
Matcher matcher = Pattern.compile("(structure101-build-java-all-)(.*).zip").matcher(anchor.getHrefAttribute());
|
||||
if (matcher.find()) {
|
||||
copyZipToFilesystem(source, installationDirectory, matcher.group(1) + matcher.group(2));
|
||||
return matcher.group(2);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyZipToFilesystem(String source, File destination, String name) {
|
||||
try (ZipInputStream in = new ZipInputStream(new URL(source + "/" + name + ".zip").openStream())) {
|
||||
ZipEntry entry;
|
||||
String build = destination.getName();
|
||||
int entries = 0;
|
||||
long size = 0;
|
||||
while ((entry = in.getNextEntry()) != null) {
|
||||
if (entry.getName().equals(name + "/")) {
|
||||
destination.mkdirs();
|
||||
} else if (entry.getName().startsWith(name)) {
|
||||
if (entries++ > TOOMANY) {
|
||||
throw new IllegalArgumentException("Zip file has more entries than expected");
|
||||
}
|
||||
if (size + BUFFER > TOOBIG) {
|
||||
throw new IllegalArgumentException("Zip file is larger than expected");
|
||||
}
|
||||
String filename = entry.getName().replace(name, build);
|
||||
if (filename.contains("maven")) {
|
||||
continue;
|
||||
}
|
||||
if (filename.contains("jxbrowser")) {
|
||||
continue;
|
||||
}
|
||||
if (filename.contains("jetty")) {
|
||||
continue;
|
||||
}
|
||||
if (filename.contains("jfreechart")) {
|
||||
continue;
|
||||
}
|
||||
if (filename.contains("piccolo2d")) {
|
||||
continue;
|
||||
}
|
||||
if (filename.contains("plexus")) {
|
||||
continue;
|
||||
}
|
||||
if (filename.contains("websocket")) {
|
||||
continue;
|
||||
}
|
||||
validateFilename(filename, build);
|
||||
this.logger.info("Downloading " + filename);
|
||||
try (OutputStream out = new FileOutputStream(new File(destination.getParentFile(), filename))) {
|
||||
byte[] data = new byte[BUFFER];
|
||||
int read;
|
||||
while ((read = in.read(data, 0, BUFFER)) != -1 && TOOBIG - size >= read) {
|
||||
out.write(data, 0, read);
|
||||
size += read;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String validateFilename(String filename, String intendedDir)
|
||||
throws java.io.IOException {
|
||||
File f = new File(filename);
|
||||
String canonicalPath = f.getCanonicalPath();
|
||||
|
||||
File iD = new File(intendedDir);
|
||||
String canonicalID = iD.getCanonicalPath();
|
||||
|
||||
if (canonicalPath.startsWith(canonicalID)) {
|
||||
return canonicalPath;
|
||||
} else {
|
||||
throw new IllegalArgumentException("File is outside extraction target directory.");
|
||||
}
|
||||
}
|
||||
|
||||
private void configureProject(String version, File configurationDirectory) {
|
||||
configurationDirectory.mkdirs();
|
||||
Map<String, Object> model = hspTemplateValues(version, configurationDirectory);
|
||||
copyToProject(this.hspTemplate, model, new File(configurationDirectory, "project.java.hsp"));
|
||||
copyToProject("s101/config.xml", new File(configurationDirectory, "config.xml"));
|
||||
File repository = new File(configurationDirectory, "repository");
|
||||
File snapshots = new File(repository, "snapshots");
|
||||
if (!snapshots.exists() && !snapshots.mkdirs()) {
|
||||
throw new IllegalStateException("Unable to create snapshots directory");
|
||||
}
|
||||
copyToProject(this.repositoryTemplate, model, new File(repository, "repository.xml"));
|
||||
}
|
||||
|
||||
private void copyToProject(String location, File destination) {
|
||||
Resource resource = new ClassPathResource(location);
|
||||
try (InputStream is = resource.getInputStream();
|
||||
OutputStream os = new FileOutputStream(destination)) {
|
||||
IOUtils.copy(is, os);
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyToProject(Mustache view, Map<String, Object> model, File destination) {
|
||||
try (OutputStream os = new FileOutputStream(destination)) {
|
||||
view.execute(new OutputStreamWriter(os), model).flush();
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> hspTemplateValues(String version, File configurationDirectory) {
|
||||
Map<String, Object> values = new LinkedHashMap<>();
|
||||
values.put("version", version);
|
||||
values.put("patchVersion", version.split("\\.")[2]);
|
||||
values.put("relativeTo", "const(THIS_FILE)/" + configurationDirectory.toPath().relativize(this.project.getProjectDir().toPath()));
|
||||
|
||||
List<Map<String, Object>> entries = new ArrayList<>();
|
||||
Set<Project> projects = this.project.getAllprojects();
|
||||
for (Project p : projects) {
|
||||
SourceSetContainer sourceSets = (SourceSetContainer) p.getExtensions().findByName("sourceSets");
|
||||
if (sourceSets == null) {
|
||||
continue;
|
||||
}
|
||||
for (SourceSet source : sourceSets) {
|
||||
Set<File> classDirs = source.getOutput().getClassesDirs().getFiles();
|
||||
for (File directory : classDirs) {
|
||||
Map<String, Object> entry = new HashMap<>();
|
||||
entry.put("path", this.project.getProjectDir().toPath().relativize(directory.toPath()));
|
||||
entry.put("module", p.getName());
|
||||
entries.add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
values.put("entries", entries);
|
||||
return values;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package s101;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
public class S101Install extends DefaultTask {
|
||||
@TaskAction
|
||||
public void install() throws Exception {
|
||||
S101PluginExtension extension = getProject().getExtensions().getByType(S101PluginExtension.class);
|
||||
File installationDirectory = extension.getInstallationDirectory().get();
|
||||
File configurationDirectory = extension.getConfigurationDirectory().get();
|
||||
S101Configurer configurer = new S101Configurer(getProject());
|
||||
configurer.install(installationDirectory, configurationDirectory);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package s101;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.JavaExec;
|
||||
|
||||
public class S101Plugin implements Plugin<Project> {
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getExtensions().add("s101", new S101PluginExtension(project));
|
||||
project.getTasks().register("s101Install", S101Install.class, this::configure);
|
||||
project.getTasks().register("s101Configure", S101Configure.class, this::configure);
|
||||
project.getTasks().register("s101", JavaExec.class, this::configure);
|
||||
}
|
||||
|
||||
private void configure(S101Install install) {
|
||||
install.setDescription("Installs Structure101 to your filesystem");
|
||||
}
|
||||
|
||||
private void configure(S101Configure configure) {
|
||||
configure.setDescription("Applies a default Structure101 configuration to the project");
|
||||
}
|
||||
|
||||
private void configure(JavaExec exec) {
|
||||
exec.setDescription("Runs Structure101 headless analysis, installing and configuring if necessary");
|
||||
exec.dependsOn("check");
|
||||
Project project = exec.getProject();
|
||||
S101PluginExtension extension = project.getExtensions().getByType(S101PluginExtension.class);
|
||||
exec
|
||||
.workingDir(extension.getInstallationDirectory())
|
||||
.classpath(new File(extension.getInstallationDirectory().get(), "structure101-java-build.jar"))
|
||||
.args(new File(new File(project.getBuildDir(), "s101"), "config.xml"))
|
||||
.args("-licensedirectory=" + extension.getLicenseDirectory().get())
|
||||
.systemProperty("s101.label", computeLabel(extension).get())
|
||||
.doFirst((task) -> {
|
||||
installAndConfigureIfNeeded(project);
|
||||
copyConfigurationToBuildDirectory(extension, project);
|
||||
})
|
||||
.doLast((task) -> {
|
||||
copyResultsBackToConfigurationDirectory(extension, project);
|
||||
});
|
||||
}
|
||||
|
||||
private Property<String> computeLabel(S101PluginExtension extension) {
|
||||
boolean hasBaseline = extension.getConfigurationDirectory().get().toPath()
|
||||
.resolve("repository").resolve("snapshots").resolve("baseline").toFile().exists();
|
||||
if (!hasBaseline) {
|
||||
return extension.getLabel().convention("baseline");
|
||||
}
|
||||
return extension.getLabel().convention("recent");
|
||||
}
|
||||
|
||||
private void installAndConfigureIfNeeded(Project project) {
|
||||
S101Configurer configurer = new S101Configurer(project);
|
||||
S101PluginExtension extension = project.getExtensions().getByType(S101PluginExtension.class);
|
||||
File installationDirectory = extension.getInstallationDirectory().get();
|
||||
File configurationDirectory = extension.getConfigurationDirectory().get();
|
||||
if (!installationDirectory.exists()) {
|
||||
configurer.install(installationDirectory, configurationDirectory);
|
||||
}
|
||||
if (!configurationDirectory.exists()) {
|
||||
configurer.configure(installationDirectory, configurationDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
private void copyConfigurationToBuildDirectory(S101PluginExtension extension, Project project) {
|
||||
Path configurationDirectory = extension.getConfigurationDirectory().get().toPath();
|
||||
Path buildDirectory = project.getBuildDir().toPath();
|
||||
copyDirectory(project, configurationDirectory, buildDirectory);
|
||||
}
|
||||
|
||||
private void copyResultsBackToConfigurationDirectory(S101PluginExtension extension, Project project) {
|
||||
Path buildConfigurationDirectory = project.getBuildDir().toPath().resolve("s101");
|
||||
String label = extension.getLabel().get();
|
||||
if ("baseline".equals(label)) { // a new baseline was created
|
||||
copyDirectory(project, buildConfigurationDirectory.resolve("repository").resolve("snapshots"),
|
||||
extension.getConfigurationDirectory().get().toPath().resolve("repository"));
|
||||
copyDirectory(project, buildConfigurationDirectory.resolve("repository"),
|
||||
extension.getConfigurationDirectory().get().toPath());
|
||||
}
|
||||
}
|
||||
|
||||
private void copyDirectory(Project project, Path source, Path destination) {
|
||||
try {
|
||||
Files.walk(source)
|
||||
.forEach(each -> {
|
||||
Path relativeToSource = source.getParent().relativize(each);
|
||||
Path resolvedDestination = destination.resolve(relativeToSource);
|
||||
if (each.toFile().isDirectory()) {
|
||||
resolvedDestination.toFile().mkdirs();
|
||||
return;
|
||||
}
|
||||
InputStream input;
|
||||
if ("project.java.hsp".equals(each.toFile().getName())) {
|
||||
Path relativeTo = project.getBuildDir().toPath().resolve("s101").relativize(project.getProjectDir().toPath());
|
||||
String value = "const(THIS_FILE)/" + relativeTo;
|
||||
input = replace(each, "<property name=\"relative-to\" value=\"(.*)\" />", "<property name=\"relative-to\" value=\"" + value + "\" />");
|
||||
} else if (each.toFile().toString().endsWith(".xml")) {
|
||||
input = replace(each, "\\r\\n", "\n");
|
||||
} else {
|
||||
input = input(each);
|
||||
}
|
||||
try {
|
||||
Files.copy(input, resolvedDestination, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream replace(Path file, String search, String replace) {
|
||||
try {
|
||||
byte[] b = Files.readAllBytes(file);
|
||||
String contents = new String(b).replaceAll(search, replace);
|
||||
return new ByteArrayInputStream(contents.getBytes(StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private InputStream input(Path file) {
|
||||
try {
|
||||
return new FileInputStream(file.toFile());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package s101;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputDirectory;
|
||||
|
||||
public class S101PluginExtension {
|
||||
private final Property<File> licenseDirectory;
|
||||
private final Property<File> installationDirectory;
|
||||
private final Property<File> configurationDirectory;
|
||||
private final Property<String> label;
|
||||
|
||||
@InputDirectory
|
||||
public Property<File> getLicenseDirectory() {
|
||||
return this.licenseDirectory;
|
||||
}
|
||||
|
||||
public void setLicenseDirectory(String licenseDirectory) {
|
||||
this.licenseDirectory.set(new File(licenseDirectory));
|
||||
}
|
||||
|
||||
@InputDirectory
|
||||
public Property<File> getInstallationDirectory() {
|
||||
return this.installationDirectory;
|
||||
}
|
||||
|
||||
public void setInstallationDirectory(String installationDirectory) {
|
||||
this.installationDirectory.set(new File(installationDirectory));
|
||||
}
|
||||
|
||||
@InputDirectory
|
||||
public Property<File> getConfigurationDirectory() {
|
||||
return this.configurationDirectory;
|
||||
}
|
||||
|
||||
public void setConfigurationDirectory(String configurationDirectory) {
|
||||
this.configurationDirectory.set(new File(configurationDirectory));
|
||||
}
|
||||
|
||||
@Input
|
||||
public Property<String> getLabel() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label.set(label);
|
||||
}
|
||||
|
||||
public S101PluginExtension(Project project) {
|
||||
this.licenseDirectory = project.getObjects().property(File.class)
|
||||
.convention(new File(System.getProperty("user.home") + "/.Structure101/java"));
|
||||
this.installationDirectory = project.getObjects().property(File.class)
|
||||
.convention(new File(project.getBuildDir(), "s101"));
|
||||
this.configurationDirectory = project.getObjects().property(File.class)
|
||||
.convention(new File(project.getProjectDir(), "s101"));
|
||||
this.label = project.getObjects().property(String.class);
|
||||
if (project.hasProperty("s101.label")) {
|
||||
setLabel((String) project.findProperty("s101.label"));
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
implementation-class=io.spring.gradle.convention.IncludeCheckRemotePlugin
|
||||
-1
@@ -1 +0,0 @@
|
||||
implementation-class=io.spring.gradle.convention.OssrhPlugin
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<headless version="1.0">
|
||||
<operations>
|
||||
<operation type="publish">
|
||||
<argument name="overwrite" value="true"/>
|
||||
<argument name="diagrams" value="true"/>
|
||||
</operation>
|
||||
<operation type="check-key-measures">
|
||||
<argument name="baseline" value="baseline"/>
|
||||
<argument name="useProjectFileSpec" value="true"/>
|
||||
<argument name="useProjectFileDiagrams" value="true"/>
|
||||
<argument name="fail-on-architecture-violations" value="false"/>
|
||||
<argument name="fail-on-fat-package" value="false"/>
|
||||
<argument name="fail-on-fat-class" value="false"/>
|
||||
<argument name="fail-on-fat-method" value="false"/>
|
||||
<argument name="fail-on-feedback-dependencies" value="true"/>
|
||||
<argument name="fail-on-spec-violation-dependencies" value="false"/>
|
||||
<argument name="fail-on-total-problem-dependencies" value="false"/>
|
||||
<argument name="fail-on-spec-item-violations" value="false"/>
|
||||
<argument name="fail-on-biggest-class-tangle" value="true"/>
|
||||
<argument name="fail-on-tangled-package" value="true"/>
|
||||
<argument name="fail-on-architecture-violations" value="false"/>
|
||||
<argument name="fail-on-total-problem-dependencies" value="true"/>
|
||||
<argument name="identifier-on-violation" value="S101 key measure violation"/>
|
||||
</operation>
|
||||
</operations>
|
||||
<arguments>
|
||||
<argument name="local-project" value="const(THIS_FILE)/project.java.hsp"/>
|
||||
<argument name="repository" value="const(THIS_FILE)/repository"/>
|
||||
<argument name="project" value="snapshots"/>
|
||||
</arguments>
|
||||
</headless>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<local-project language="java" version="{{version}}" xml-version="3" flavor="j2se">
|
||||
<property name="show-as-module" value="false" />
|
||||
<property name="publish-architecture-artifacts" value="true" />
|
||||
<property name="force-classpath" value="false" />
|
||||
<property name="project-type" value="classpath" />
|
||||
<property name="hide-externals" value="true" />
|
||||
<property name="parse-archive-in-archive" value="false" />
|
||||
<property name="include-injected-dependency" value="false" />
|
||||
<property name="relative-to" value="{{relativeTo}}" />
|
||||
<property name="action-set-mod" value="1" />
|
||||
<property name="detail-mode" value="true" />
|
||||
<property name="hide-deprecated" value="false" />
|
||||
<property name="resolve-name-clashes" value="true" />
|
||||
<property name="project-excluded" />
|
||||
<property name="show-needs-to-compile" value="false" />
|
||||
<classpath>
|
||||
{{#entries}}
|
||||
<classpathentry kind="lib" path="{{path}}" module="{{module}}" />
|
||||
{{/entries}}
|
||||
</classpath>
|
||||
<pom-root-files />
|
||||
<modules-in-scope />
|
||||
<restructuring>
|
||||
<set version="3" name="Action list 1" hiview="Codemap" active="true" todo="false" list="0" />
|
||||
</restructuring>
|
||||
<grid-set sep="." version="{{version}}" />
|
||||
</local-project>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<structure101-repository language="java" version="{{patchVersion}}">
|
||||
<xs-configuration>
|
||||
<entry metric="Tangled" scope="design" threshold="0" color="153,53,0" />
|
||||
<entry metric="Fat" scope="design" threshold="120" color="255,153,0" />
|
||||
<entry metric="Fat" scope="leaf package" threshold="120" color="0,153,153" />
|
||||
<entry metric="Fat" scope="class" threshold="120" color="255,153,153" />
|
||||
<entry metric="Fat" scope="method" threshold="15" color="51,255,51" />
|
||||
</xs-configuration>
|
||||
<!--Note: All date strings are stored in short US format e.g. 2/1/06 for 1st Feb 2006-->
|
||||
<project name="snapshots" dir="snapshots" baselineSnapshot="default" version="{{patchVersion}}">
|
||||
<snapshot label="baseline" location="baseline" timestamp="3/16/21, 4:42 PM" version="{{patchVersion}}" detail="true" good="true" size="20" />
|
||||
</project>
|
||||
</structure101-repository>
|
||||
@@ -1,87 +0,0 @@
|
||||
package io.spring.gradle.convention;
|
||||
|
||||
import io.spring.gradle.TestKit;
|
||||
import org.codehaus.groovy.runtime.ResourceGroovyMethods;
|
||||
import org.gradle.testkit.runner.BuildResult;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.gradle.testkit.runner.TaskOutcome.FAILED;
|
||||
import static org.gradle.testkit.runner.TaskOutcome.SUCCESS;
|
||||
|
||||
public class DocsPluginITest {
|
||||
private TestKit testKit;
|
||||
|
||||
@BeforeEach
|
||||
void setup(@TempDir Path tempDir) {
|
||||
this.testKit = new TestKit(tempDir.toFile());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildTriggersDocs() throws Exception {
|
||||
BuildResult result = testKit.withProjectResource("samples/docs/simple/")
|
||||
.withArguments("build")
|
||||
.build();
|
||||
assertThat(result.task(":build").getOutcome()).isEqualTo(SUCCESS);
|
||||
assertThat(result.task(":docs").getOutcome()).isEqualTo(SUCCESS);
|
||||
assertThat(result.task(":docsZip").getOutcome()).isEqualTo(SUCCESS);
|
||||
File zip = new File(testKit.getRootDir(), "build/distributions/simple-1.0.0.BUILD-SNAPSHOT-docs.zip");
|
||||
try (ZipFile file = new ZipFile(zip)) {
|
||||
List<? extends ZipEntry> entries = Collections.list(file.entries());
|
||||
assertThat(entries)
|
||||
.extracting(ZipEntry::getName)
|
||||
.contains("docs/reference/html5/index.html")
|
||||
.contains("docs/reference/pdf/simple-reference.pdf");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asciidocCopiesImages() throws Exception {
|
||||
BuildResult result = testKit.withProjectResource("samples/docs/simple/").withArguments("asciidoctor").build();
|
||||
assertThat(result.task(":asciidoctor").getOutcome()).isEqualTo(SUCCESS);
|
||||
assertThat(new File(testKit.getRootDir(), "build/docs/asciidoc/images")).exists();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asciidocDocInfoFromResourcesUsed() throws Exception {
|
||||
BuildResult result = this.testKit.withProjectResource("samples/docs/simple/")
|
||||
.withArguments("asciidoctor")
|
||||
.build();
|
||||
assertThat(result.task(":asciidoctor").getOutcome()).isEqualTo(SUCCESS);
|
||||
assertThat(ResourceGroovyMethods.getText(new File(testKit.getRootDir(), "build/docs/asciidoc/index.html")))
|
||||
.contains("<script type=\"text/javascript\" src=\"js/tocbot/tocbot.min.js\"></script>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingAttributeFails() throws Exception {
|
||||
BuildResult result = this.testKit.withProjectResource("samples/docs/missing-attribute/")
|
||||
.withArguments(":asciidoctor")
|
||||
.buildAndFail();
|
||||
assertThat(result.task(":asciidoctor").getOutcome()).isEqualTo(FAILED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingInclude() throws Exception {
|
||||
BuildResult result = this.testKit.withProjectResource("samples/docs/missing-include/")
|
||||
.withArguments(":asciidoctor")
|
||||
.buildAndFail();
|
||||
assertThat(result.task(":asciidoctor").getOutcome()).isEqualTo(FAILED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingCrossReference() throws Exception {
|
||||
BuildResult result = this.testKit.withProjectResource("samples/docs/missing-cross-reference/")
|
||||
.withArguments(":asciidoctor")
|
||||
.buildAndFail();
|
||||
assertThat(result.task(":asciidoctor").getOutcome()).isEqualTo(FAILED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package io.spring.gradle.convention;
|
||||
|
||||
import io.spring.gradle.IncludeRepoTask;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.GradleBuild;
|
||||
import org.gradle.testfixtures.ProjectBuilder;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class IncludeCheckRemotePluginTest {
|
||||
|
||||
Project rootProject;
|
||||
|
||||
@AfterEach
|
||||
public void cleanup() throws Exception {
|
||||
if (rootProject != null) {
|
||||
FileUtils.deleteDirectory(rootProject.getProjectDir());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyWhenExtensionPropertiesNoTasksThenCreateCheckRemoteTaskWithDefaultTask() {
|
||||
this.rootProject = ProjectBuilder.builder().build();
|
||||
this.rootProject.getPluginManager().apply(IncludeCheckRemotePlugin.class);
|
||||
this.rootProject.getExtensions().configure(IncludeCheckRemotePlugin.IncludeCheckRemoteExtension.class,
|
||||
(includeCheckRemoteExtension) -> {
|
||||
includeCheckRemoteExtension.setProperty("repository", "my-project/my-repository");
|
||||
includeCheckRemoteExtension.setProperty("ref", "main");
|
||||
});
|
||||
|
||||
GradleBuild checkRemote = (GradleBuild) this.rootProject.getTasks().named("checkRemote").get();
|
||||
assertThat(checkRemote.getTasks()).containsExactly("check");
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyWhenExtensionPropertiesTasksThenCreateCheckRemoteWithProvidedTasks() {
|
||||
this.rootProject = ProjectBuilder.builder().build();
|
||||
this.rootProject.getPluginManager().apply(IncludeCheckRemotePlugin.class);
|
||||
this.rootProject.getExtensions().configure(IncludeCheckRemotePlugin.IncludeCheckRemoteExtension.class,
|
||||
(includeCheckRemoteExtension) -> {
|
||||
includeCheckRemoteExtension.setProperty("repository", "my-project/my-repository");
|
||||
includeCheckRemoteExtension.setProperty("ref", "main");
|
||||
includeCheckRemoteExtension.setProperty("tasks", Arrays.asList("clean", "build", "test"));
|
||||
});
|
||||
|
||||
GradleBuild checkRemote = (GradleBuild) this.rootProject.getTasks().named("checkRemote").get();
|
||||
assertThat(checkRemote.getTasks()).containsExactly("clean", "build", "test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyWhenExtensionPropertiesThenRegisterIncludeRepoTaskWithExtensionProperties() {
|
||||
this.rootProject = ProjectBuilder.builder().build();
|
||||
this.rootProject.getPluginManager().apply(IncludeCheckRemotePlugin.class);
|
||||
this.rootProject.getExtensions().configure(IncludeCheckRemotePlugin.IncludeCheckRemoteExtension.class,
|
||||
(includeCheckRemoteExtension) -> {
|
||||
includeCheckRemoteExtension.setProperty("repository", "my-project/my-repository");
|
||||
includeCheckRemoteExtension.setProperty("ref", "main");
|
||||
});
|
||||
|
||||
IncludeRepoTask includeRepo = (IncludeRepoTask) this.rootProject.getTasks().named("includeRepo").get();
|
||||
assertThat(includeRepo).isNotNull();
|
||||
assertThat(includeRepo.getRepository().get()).isEqualTo("my-project/my-repository");
|
||||
assertThat(includeRepo.getRef().get()).isEqualTo("main");
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyWhenRegisterTasksThenCheckRemoteDirSameAsIncludeRepoOutputDir() {
|
||||
this.rootProject = ProjectBuilder.builder().build();
|
||||
this.rootProject.getPluginManager().apply(IncludeCheckRemotePlugin.class);
|
||||
this.rootProject.getExtensions().configure(IncludeCheckRemotePlugin.IncludeCheckRemoteExtension.class,
|
||||
(includeCheckRemoteExtension) -> {
|
||||
includeCheckRemoteExtension.setProperty("repository", "my-project/my-repository");
|
||||
includeCheckRemoteExtension.setProperty("ref", "main");
|
||||
});
|
||||
IncludeRepoTask includeRepo = (IncludeRepoTask) this.rootProject.getTasks().named("includeRepo").get();
|
||||
GradleBuild checkRemote = (GradleBuild) this.rootProject.getTasks().named("checkRemote").get();
|
||||
assertThat(checkRemote.getDir()).isEqualTo(includeRepo.getOutputDirectory());
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyWhenNoExtensionPropertiesThenRegisterTasks() {
|
||||
this.rootProject = ProjectBuilder.builder().build();
|
||||
this.rootProject.getPluginManager().apply(IncludeCheckRemotePlugin.class);
|
||||
IncludeRepoTask includeRepo = (IncludeRepoTask) this.rootProject.getTasks().named("includeRepo").get();
|
||||
GradleBuild checkRemote = (GradleBuild) this.rootProject.getTasks().named("checkRemote").get();
|
||||
assertThat(includeRepo).isNotNull();
|
||||
assertThat(checkRemote).isNotNull();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -55,19 +55,6 @@ public class SpringMavenPluginITest {
|
||||
assertThat(signature).exists();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upload() throws Exception {
|
||||
BuildResult result = this.testKit.withProjectResource("samples/maven/upload")
|
||||
.withArguments("uploadArchives")
|
||||
.forwardOutput()
|
||||
.build();
|
||||
assertThat(result.getOutput()).contains("SUCCESS");
|
||||
File pom = new File(testKit.getRootDir(), "build/poms/pom-default.xml");
|
||||
assertThat(pom).exists();
|
||||
String pomText = new String(Files.readAllBytes(pom.toPath()));
|
||||
assertThat(pomText.replaceAll("\\s", "")).contains("<dependency>\n <groupId>aopalliance</groupId>\n <artifactId>aopalliance</artifactId>\n <version>1.0</version>\n <scope>compile</scope>\n <optional>true</optional>\n </dependency>".replaceAll("\\s", ""));
|
||||
}
|
||||
|
||||
public String getSigningKey() throws Exception {
|
||||
return IOUtils.toString(getClass().getResource("/test-private.pgp"));
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
plugins {
|
||||
id 'io.spring.convention.docs'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
version = '1.0.0.BUILD-SNAPSHOT'
|
||||
@@ -1 +0,0 @@
|
||||
rootProject.name = 'simple'
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
= Example Manual
|
||||
|
||||
This will fail due to {missing} attribute
|
||||
@@ -1,6 +0,0 @@
|
||||
plugins {
|
||||
id 'io.spring.convention.docs'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
version = '1.0.0.BUILD-SNAPSHOT'
|
||||
@@ -1 +0,0 @@
|
||||
rootProject.name = 'missing-include'
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
= Example Manual
|
||||
|
||||
This will fail due to <<missing>> cross reference
|
||||
@@ -1,6 +0,0 @@
|
||||
plugins {
|
||||
id 'io.spring.convention.docs'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
version = '1.0.0.BUILD-SNAPSHOT'
|
||||
@@ -1 +0,0 @@
|
||||
rootProject.name = 'missing-include'
|
||||
@@ -1,5 +0,0 @@
|
||||
= Example Manual
|
||||
|
||||
This will fail due to missing include
|
||||
|
||||
include::missing.adoc[]
|
||||
@@ -1,13 +0,0 @@
|
||||
plugins {
|
||||
id 'io.spring.convention.docs'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
version = '1.0.0.BUILD-SNAPSHOT'
|
||||
|
||||
asciidoctorj {
|
||||
attributes \
|
||||
'build-gradle': project.buildFile,
|
||||
'sourcedir': project.sourceSets.main.java.srcDirs[0],
|
||||
'endpoint-url': 'https://example.org'
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
rootProject.name = 'simple'
|
||||
@@ -1 +0,0 @@
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.js"></script>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 120 KiB |
@@ -1,60 +0,0 @@
|
||||
= Example Manual
|
||||
Doc Writer <doc.writer@example.org>
|
||||
2014-09-09
|
||||
:example-caption!:
|
||||
ifndef::imagesdir[:imagesdir: images]
|
||||
ifndef::sourcedir[:sourcedir: ../java]
|
||||
|
||||
This is a user manual for an example project.
|
||||
|
||||
== Introduction
|
||||
|
||||
This project does something.
|
||||
We just haven't decided what that is yet.
|
||||
|
||||
== Source Code
|
||||
|
||||
[source,java]
|
||||
.Java code from project
|
||||
----
|
||||
include::{sourcedir}/example/StringUtils.java[tags=contains,indent=0]
|
||||
----
|
||||
|
||||
This page was built by the following command:
|
||||
|
||||
$ ./gradlew asciidoctor
|
||||
|
||||
== Images
|
||||
|
||||
[.thumb]
|
||||
image::sunset.jpg[scaledwidth=75%]
|
||||
|
||||
== Attributes
|
||||
|
||||
.Built-in
|
||||
asciidoctor-version:: {asciidoctor-version}
|
||||
safe-mode-name:: {safe-mode-name}
|
||||
docdir:: {docdir}
|
||||
docfile:: {docfile}
|
||||
imagesdir:: {imagesdir}
|
||||
revnumber:: {revnumber}
|
||||
|
||||
.Custom
|
||||
sourcedir:: {sourcedir}
|
||||
endpoint-url:: {endpoint-url}
|
||||
|
||||
== Includes
|
||||
|
||||
.include::subdir/_b.adoc[]
|
||||
====
|
||||
include::subdir/_b.adoc[]
|
||||
====
|
||||
|
||||
WARNING: Includes can be tricky!
|
||||
|
||||
== build.gradle
|
||||
|
||||
[source,groovy]
|
||||
----
|
||||
include::{build-gradle}[]
|
||||
----
|
||||
@@ -1,7 +0,0 @@
|
||||
content from _src/docs/asciidoc/subdir/_b.adoc_.
|
||||
|
||||
.include::_c.adoc[]
|
||||
[example]
|
||||
--
|
||||
include::_c.adoc[]
|
||||
--
|
||||
@@ -1 +0,0 @@
|
||||
content from _src/docs/asciidoc/subdir/c.adoc_.
|
||||
@@ -1,9 +0,0 @@
|
||||
package example;
|
||||
|
||||
public class StringUtils {
|
||||
// tag::contains[]
|
||||
public boolean contains(String haystack, String needle) {
|
||||
return haystack.contains(needle);
|
||||
}
|
||||
// end::contains[]
|
||||
}
|
||||
@@ -3,7 +3,6 @@ plugins {
|
||||
}
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'propdeps'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
||||
@@ -2,7 +2,6 @@ plugins {
|
||||
id 'io.spring.convention.root'
|
||||
}
|
||||
|
||||
apply plugin: 'propdeps-maven'
|
||||
apply plugin: 'io.spring.convention.maven'
|
||||
|
||||
repositories {
|
||||
|
||||
@@ -4,7 +4,6 @@ plugins {
|
||||
|
||||
version = "1.0.0.RELEASE"
|
||||
|
||||
apply plugin: 'propdeps-maven'
|
||||
apply plugin: 'io.spring.convention.maven'
|
||||
|
||||
repositories {
|
||||
|
||||
@@ -2,8 +2,6 @@ plugins {
|
||||
id 'io.spring.convention.root'
|
||||
}
|
||||
|
||||
apply plugin: 'propdeps-maven'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
apply plugin: 'io.spring.convention.spring-module'
|
||||
|
||||
dependencies {
|
||||
management platform('org.springframework.boot:spring-boot-dependencies:2.5.2')
|
||||
compile 'org.springframework:spring-web'
|
||||
compile 'org.springframework:spring-core'
|
||||
api platform('org.springframework.boot:spring-boot-dependencies:2.5.2')
|
||||
implementation 'org.springframework:spring-web'
|
||||
implementation 'org.springframework:spring-core'
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-engine"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
apply plugin: 'io.spring.convention.spring-module'
|
||||
|
||||
dependencies {
|
||||
management platform('org.springframework.boot:spring-boot-dependencies:2.5.2')
|
||||
api platform('org.springframework.boot:spring-boot-dependencies:2.5.2')
|
||||
optional 'ch.qos.logback:logback-classic'
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-engine"
|
||||
|
||||
@@ -2,10 +2,3 @@ apply plugin: 'java'
|
||||
apply plugin: 'io.spring.convention.docs'
|
||||
|
||||
version = "1.0.0.BUILD-SNAPSHOT"
|
||||
|
||||
asciidoctorj {
|
||||
attributes \
|
||||
'build-gradle': project.buildFile,
|
||||
'sourcedir': project.sourceSets.main.java.srcDirs[0],
|
||||
'endpoint-url': 'https://example.org'
|
||||
}
|
||||
@@ -5,6 +5,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testCompile project(path: ':core', configuration: 'tests')
|
||||
testCompile 'junit:junit:4.12'
|
||||
testImplementation project(path: ':core', configuration: 'tests')
|
||||
testImplementation 'junit:junit:4.12'
|
||||
}
|
||||
+2
@@ -30,7 +30,9 @@ import org.springframework.util.Assert;
|
||||
* <a href="https://www.ehcache.org/">EHCACHE</a>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated since 5.6. In favor of JCache based implementations
|
||||
*/
|
||||
@Deprecated
|
||||
public class EhCacheBasedTicketCache implements StatelessTicketCache, InitializingBean {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(EhCacheBasedTicketCache.class);
|
||||
|
||||
@@ -40,9 +40,9 @@ import org.jasig.cas.client.authentication.AttributePrincipal;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see CasJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
|
||||
+1
-1
@@ -37,9 +37,9 @@ import org.jasig.cas.client.proxy.ProxyRetriever;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see CasJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
|
||||
+1
-1
@@ -47,9 +47,9 @@ import org.springframework.security.core.userdetails.UserDetails;
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see CasJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
* @since 4.2
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.security.cas.web.authentication.ServiceAuthentication
|
||||
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
@@ -219,7 +220,9 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
||||
}
|
||||
this.logger.debug(
|
||||
LogMessage.format("Authentication success. Updating SecurityContextHolder to contain: %s", authResult));
|
||||
SecurityContextHolder.getContext().setAuthentication(authResult);
|
||||
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||
context.setAuthentication(authResult);
|
||||
SecurityContextHolder.setContext(context);
|
||||
if (this.eventPublisher != null) {
|
||||
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ dependencies {
|
||||
testImplementation project(path : ':spring-security-oauth2-client', configuration : 'tests')
|
||||
testImplementation project(path : ':spring-security-oauth2-resource-server', configuration : 'tests')
|
||||
testImplementation project(path : ':spring-security-saml2-service-provider', configuration : 'tests')
|
||||
testImplementation project(path: ':spring-security-saml2-service-provider', configuration: 'opensaml4MainCompile')
|
||||
testImplementation project(path : ':spring-security-saml2-service-provider', configuration : 'opensaml4MainImplementation')
|
||||
testImplementation project(path : ':spring-security-web', configuration : 'tests')
|
||||
testImplementation "org.assertj:assertj-core"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||
|
||||
@@ -68,16 +68,6 @@ sn: Mouse
|
||||
uid: jerry
|
||||
userPassword: jerryspassword
|
||||
|
||||
dn: uid=bcrypt,ou=people,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: person
|
||||
objectclass: organizationalPerson
|
||||
objectclass: inetOrgPerson
|
||||
cn: BCrypt user
|
||||
sn: BCrypt
|
||||
uid: bcrypt
|
||||
userPassword: $2a$10$lDa0YFNHAt63MjIzK/wUqeM0qjIhzPhp3RNI/MLUQEAUbzhB/SnnS
|
||||
|
||||
dn: cn=developers,ou=groups,dc=springframework,dc=org
|
||||
objectclass: top
|
||||
objectclass: groupOfNames
|
||||
|
||||
@@ -19,8 +19,8 @@ package org.springframework.security.config;
|
||||
/**
|
||||
* Callback interface that accepts a single input argument and returns no result.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @param <T> the type of the input to the operation
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.2
|
||||
*/
|
||||
@FunctionalInterface
|
||||
|
||||
+2
-2
@@ -94,7 +94,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
if (!namespaceMatchesVersion(element)) {
|
||||
pc.getReaderContext().fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or "
|
||||
+ "spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema "
|
||||
+ "with Spring Security 5.4. Please update your schema declarations to the 5.4 schema.", element);
|
||||
+ "with Spring Security 5.6. Please update your schema declarations to the 5.6 schema.", element);
|
||||
}
|
||||
String name = pc.getDelegate().getLocalName(element);
|
||||
BeanDefinitionParser parser = this.parsers.get(name);
|
||||
@@ -215,7 +215,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
|
||||
private boolean matchesVersionInternal(Element element) {
|
||||
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
|
||||
return schemaLocation.matches("(?m).*spring-security-5\\.4.*.xsd.*")
|
||||
return schemaLocation.matches("(?m).*spring-security-5\\.6.*.xsd.*")
|
||||
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|
||||
|| !schemaLocation.matches("(?m).*spring-security.*");
|
||||
}
|
||||
|
||||
+1
-1
@@ -27,8 +27,8 @@ import org.springframework.security.config.annotation.authentication.builders.Au
|
||||
* {@link AuthenticationConfiguration} to configure the global
|
||||
* {@link AuthenticationManagerBuilder}.
|
||||
*
|
||||
* @since 5.0
|
||||
* @author Rob Winch
|
||||
* @since 5.0
|
||||
*/
|
||||
@Order(100)
|
||||
public abstract class GlobalAuthenticationConfigurerAdapter
|
||||
|
||||
+2
-2
@@ -30,10 +30,10 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||
* class is not intended to be imported manually rather it is imported automatically when
|
||||
* using {@link EnableWebSecurity} or {@link EnableGlobalMethodSecurity}.
|
||||
*
|
||||
* @see EnableWebSecurity
|
||||
* @see EnableGlobalMethodSecurity
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @see EnableWebSecurity
|
||||
* @see EnableGlobalMethodSecurity
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
|
||||
+1
-1
@@ -31,8 +31,8 @@ import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
* @author Josh Cummings
|
||||
* @see EnableMethodSecurity
|
||||
* @since 5.6
|
||||
* @see EnableMethodSecurity
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
|
||||
+25
-11
@@ -17,8 +17,11 @@
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
@@ -37,12 +40,12 @@ import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
* @author Josh Cummings
|
||||
* @see EnableMethodSecurity
|
||||
* @since 5.6
|
||||
* @see EnableMethodSecurity
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
final class PrePostMethodSecurityConfiguration {
|
||||
final class PrePostMethodSecurityConfiguration implements ApplicationContextAware {
|
||||
|
||||
private final PreFilterAuthorizationMethodInterceptor preFilterAuthorizationMethodInterceptor = new PreFilterAuthorizationMethodInterceptor();
|
||||
|
||||
@@ -52,29 +55,43 @@ final class PrePostMethodSecurityConfiguration {
|
||||
|
||||
private final PostFilterAuthorizationMethodInterceptor postFilterAuthorizationMethodInterceptor = new PostFilterAuthorizationMethodInterceptor();
|
||||
|
||||
private final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||
|
||||
private boolean customMethodSecurityExpressionHandler = false;
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
Advisor preFilterAuthorizationMethodInterceptor() {
|
||||
if (!this.customMethodSecurityExpressionHandler) {
|
||||
this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
|
||||
}
|
||||
return this.preFilterAuthorizationMethodInterceptor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
Advisor preAuthorizeAuthorizationMethodInterceptor() {
|
||||
if (!this.customMethodSecurityExpressionHandler) {
|
||||
this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
|
||||
}
|
||||
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(this.preAuthorizeAuthorizationManager);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
Advisor postAuthorizeAuthorizationMethodInterceptor() {
|
||||
if (!this.customMethodSecurityExpressionHandler) {
|
||||
this.postAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
|
||||
}
|
||||
return AuthorizationManagerAfterMethodInterceptor.postAuthorize(this.postAuthorizeAuthorizationManager);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
Advisor postFilterAuthorizationMethodInterceptor() {
|
||||
if (!this.customMethodSecurityExpressionHandler) {
|
||||
this.postFilterAuthorizationMethodInterceptor.setExpressionHandler(this.expressionHandler);
|
||||
}
|
||||
return this.postFilterAuthorizationMethodInterceptor;
|
||||
}
|
||||
|
||||
@@ -89,15 +106,12 @@ final class PrePostMethodSecurityConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) {
|
||||
if (this.customMethodSecurityExpressionHandler) {
|
||||
return;
|
||||
}
|
||||
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||
expressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
|
||||
this.preFilterAuthorizationMethodInterceptor.setExpressionHandler(expressionHandler);
|
||||
this.preAuthorizeAuthorizationManager.setExpressionHandler(expressionHandler);
|
||||
this.postAuthorizeAuthorizationManager.setExpressionHandler(expressionHandler);
|
||||
this.postFilterAuthorizationMethodInterceptor.setExpressionHandler(expressionHandler);
|
||||
this.expressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.expressionHandler.setApplicationContext(context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -29,8 +29,8 @@ import org.springframework.security.authorization.method.AuthorizationManagerBef
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
* @author Josh Cummings
|
||||
* @see EnableMethodSecurity
|
||||
* @since 5.6
|
||||
* @see EnableMethodSecurity
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
|
||||
+138
@@ -72,6 +72,7 @@ import org.springframework.security.config.annotation.web.configurers.oauth2.cli
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.openid.OpenIDLoginConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2LoginConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2LogoutConfigurer;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
@@ -2209,6 +2210,143 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
return HttpSecurity.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures logout support for an SAML 2.0 Relying Party. <br>
|
||||
* <br>
|
||||
*
|
||||
* Implements the <b>Single Logout Profile, using POST and REDIRECT bindings</b>, as
|
||||
* documented in the
|
||||
* <a target="_blank" href="https://docs.oasis-open.org/security/saml/">SAML V2.0
|
||||
* Core, Profiles and Bindings</a> specifications. <br>
|
||||
* <br>
|
||||
*
|
||||
* As a prerequisite to using this feature, is that you have a SAML v2.0 Asserting
|
||||
* Party to sent a logout request to. The representation of the relying party and the
|
||||
* asserting party is contained within {@link RelyingPartyRegistration}. <br>
|
||||
* <br>
|
||||
*
|
||||
* {@link RelyingPartyRegistration}(s) are composed within a
|
||||
* {@link RelyingPartyRegistrationRepository}, which is <b>required</b> and must be
|
||||
* registered with the {@link ApplicationContext} or configured via
|
||||
* {@link #saml2Login(Customizer)}.<br>
|
||||
* <br>
|
||||
*
|
||||
* The default configuration provides an auto-generated logout endpoint at
|
||||
* <code>"/logout"</code> and redirects to <code>/login?logout</code> when
|
||||
* logout completes. <br>
|
||||
* <br>
|
||||
*
|
||||
* <p>
|
||||
* <h2>Example Configuration</h2>
|
||||
*
|
||||
* The following example shows the minimal configuration required, using a
|
||||
* hypothetical asserting party.
|
||||
*
|
||||
* <pre>
|
||||
* @EnableWebSecurity
|
||||
* @Configuration
|
||||
* public class Saml2LogoutSecurityConfig {
|
||||
* @Bean
|
||||
* public SecurityFilterChain web(HttpSecurity http) throws Exception {
|
||||
* http
|
||||
* .authorizeRequests((authorize) -> authorize
|
||||
* .anyRequest().authenticated()
|
||||
* )
|
||||
* .saml2Login(withDefaults())
|
||||
* .saml2Logout(withDefaults());
|
||||
* return http.build();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {
|
||||
* RelyingPartyRegistration registration = RelyingPartyRegistrations
|
||||
* .withMetadataLocation("https://ap.example.org/metadata")
|
||||
* .registrationId("simple")
|
||||
* .build();
|
||||
* return new InMemoryRelyingPartyRegistrationRepository(registration);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* @return the {@link HttpSecurity} for further customizations
|
||||
* @throws Exception
|
||||
* @since 5.6
|
||||
*/
|
||||
public HttpSecurity saml2Logout(Customizer<Saml2LogoutConfigurer<HttpSecurity>> saml2LogoutCustomizer)
|
||||
throws Exception {
|
||||
saml2LogoutCustomizer.customize(getOrApply(new Saml2LogoutConfigurer<>(getContext())));
|
||||
return HttpSecurity.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures logout support for an SAML 2.0 Relying Party. <br>
|
||||
* <br>
|
||||
*
|
||||
* Implements the <b>Single Logout Profile, using POST and REDIRECT bindings</b>, as
|
||||
* documented in the
|
||||
* <a target="_blank" href="https://docs.oasis-open.org/security/saml/">SAML V2.0
|
||||
* Core, Profiles and Bindings</a> specifications. <br>
|
||||
* <br>
|
||||
*
|
||||
* As a prerequisite to using this feature, is that you have a SAML v2.0 Asserting
|
||||
* Party to sent a logout request to. The representation of the relying party and the
|
||||
* asserting party is contained within {@link RelyingPartyRegistration}. <br>
|
||||
* <br>
|
||||
*
|
||||
* {@link RelyingPartyRegistration}(s) are composed within a
|
||||
* {@link RelyingPartyRegistrationRepository}, which is <b>required</b> and must be
|
||||
* registered with the {@link ApplicationContext} or configured via
|
||||
* {@link #saml2Login()}.<br>
|
||||
* <br>
|
||||
*
|
||||
* The default configuration provides an auto-generated logout endpoint at
|
||||
* <code>"/logout"</code> and redirects to <code>/login?logout</code> when
|
||||
* logout completes. <br>
|
||||
* <br>
|
||||
*
|
||||
* <p>
|
||||
* <h2>Example Configuration</h2>
|
||||
*
|
||||
* The following example shows the minimal configuration required, using a
|
||||
* hypothetical asserting party.
|
||||
*
|
||||
* <pre>
|
||||
* @EnableWebSecurity
|
||||
* @Configuration
|
||||
* public class Saml2LogoutSecurityConfig {
|
||||
* @Bean
|
||||
* public SecurityFilterChain web(HttpSecurity http) throws Exception {
|
||||
* http
|
||||
* .authorizeRequests()
|
||||
* .anyRequest().authenticated()
|
||||
* .and()
|
||||
* .saml2Login()
|
||||
* .and()
|
||||
* .saml2Logout();
|
||||
* return http.build();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public RelyingPartyRegistrationRepository relyingPartyRegistrationRepository() {
|
||||
* RelyingPartyRegistration registration = RelyingPartyRegistrations
|
||||
* .withMetadataLocation("https://ap.example.org/metadata")
|
||||
* .registrationId("simple")
|
||||
* .build();
|
||||
* return new InMemoryRelyingPartyRegistrationRepository(registration);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* @return the {@link Saml2LoginConfigurer} for further customizations
|
||||
* @throws Exception
|
||||
* @since 5.6
|
||||
*/
|
||||
public Saml2LogoutConfigurer<HttpSecurity> saml2Logout() throws Exception {
|
||||
return getOrApply(new Saml2LogoutConfigurer<>(getContext()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures authentication support using an OAuth 2.0 and/or OpenID Connect 1.0
|
||||
* Provider. <br>
|
||||
|
||||
+2
-2
@@ -60,11 +60,11 @@ import org.springframework.util.Assert;
|
||||
* {@link WebSecurityConfigurer} and exposing it as a {@link Configuration}. This
|
||||
* configuration is imported when using {@link EnableWebSecurity}.
|
||||
*
|
||||
* @see EnableWebSecurity
|
||||
* @see WebSecurity
|
||||
* @author Rob Winch
|
||||
* @author Keesun Baik
|
||||
* @since 3.2
|
||||
* @see EnableWebSecurity
|
||||
* @see WebSecurity
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
|
||||
|
||||
+1
-1
@@ -87,8 +87,8 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||
* org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyClassThatExtendsAbstractHttpConfigurer, sample.OtherThatExtendsAbstractHttpConfigurer
|
||||
* </pre>
|
||||
*
|
||||
* @see EnableWebSecurity
|
||||
* @author Rob Winch
|
||||
* @see EnableWebSecurity
|
||||
*/
|
||||
@Order(100)
|
||||
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {
|
||||
|
||||
+29
-14
@@ -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.
|
||||
@@ -76,6 +76,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
|
||||
* @author Rob Winch
|
||||
* @author Yanming Zhou
|
||||
* @since 3.2
|
||||
* @see org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeRequests()
|
||||
*/
|
||||
@@ -94,6 +95,8 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
|
||||
|
||||
private static final String rememberMe = "rememberMe";
|
||||
|
||||
private final String rolePrefix;
|
||||
|
||||
private final ExpressionInterceptUrlRegistry REGISTRY;
|
||||
|
||||
private SecurityExpressionHandler<FilterInvocation> expressionHandler;
|
||||
@@ -103,6 +106,15 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
|
||||
* @see HttpSecurity#authorizeRequests()
|
||||
*/
|
||||
public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) {
|
||||
String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class);
|
||||
if (grantedAuthorityDefaultsBeanNames.length == 1) {
|
||||
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBean(grantedAuthorityDefaultsBeanNames[0],
|
||||
GrantedAuthorityDefaults.class);
|
||||
this.rolePrefix = grantedAuthorityDefaults.getRolePrefix();
|
||||
}
|
||||
else {
|
||||
this.rolePrefix = "ROLE_";
|
||||
}
|
||||
this.REGISTRY = new ExpressionInterceptUrlRegistry(context);
|
||||
}
|
||||
|
||||
@@ -176,16 +188,16 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
|
||||
return this.expressionHandler;
|
||||
}
|
||||
|
||||
private static String hasAnyRole(String... authorities) {
|
||||
String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','ROLE_");
|
||||
return "hasAnyRole('ROLE_" + anyAuthorities + "')";
|
||||
private static String hasAnyRole(String rolePrefix, String... authorities) {
|
||||
String anyAuthorities = StringUtils.arrayToDelimitedString(authorities, "','" + rolePrefix);
|
||||
return "hasAnyRole('" + rolePrefix + anyAuthorities + "')";
|
||||
}
|
||||
|
||||
private static String hasRole(String role) {
|
||||
private static String hasRole(String rolePrefix, String role) {
|
||||
Assert.notNull(role, "role cannot be null");
|
||||
Assert.isTrue(!role.startsWith("ROLE_"),
|
||||
() -> "role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'");
|
||||
return "hasRole('ROLE_" + role + "')";
|
||||
Assert.isTrue(rolePrefix.isEmpty() || !role.startsWith(rolePrefix), () -> "role should not start with '"
|
||||
+ rolePrefix + "' since it is automatically inserted. Got '" + role + "'");
|
||||
return "hasRole('" + rolePrefix + role + "')";
|
||||
}
|
||||
|
||||
private static String hasAuthority(String authority) {
|
||||
@@ -308,27 +320,30 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
|
||||
|
||||
/**
|
||||
* Shortcut for specifying URLs require a particular role. If you do not want to
|
||||
* have "ROLE_" automatically inserted see {@link #hasAuthority(String)}.
|
||||
* have role prefix (default "ROLE_") automatically inserted see
|
||||
* {@link #hasAuthority(String)}.
|
||||
* @param role the role to require (i.e. USER, ADMIN, etc). Note, it should not
|
||||
* start with "ROLE_" as this is automatically inserted.
|
||||
* start with role prefix as this is automatically inserted.
|
||||
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
|
||||
* customization
|
||||
*/
|
||||
public ExpressionInterceptUrlRegistry hasRole(String role) {
|
||||
return access(ExpressionUrlAuthorizationConfigurer.hasRole(role));
|
||||
return access(ExpressionUrlAuthorizationConfigurer
|
||||
.hasRole(ExpressionUrlAuthorizationConfigurer.this.rolePrefix, role));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut for specifying URLs require any of a number of roles. If you do not
|
||||
* want to have "ROLE_" automatically inserted see
|
||||
* want to have role prefix (default "ROLE_") automatically inserted see
|
||||
* {@link #hasAnyAuthority(String...)}
|
||||
* @param roles the roles to require (i.e. USER, ADMIN, etc). Note, it should not
|
||||
* start with "ROLE_" as this is automatically inserted.
|
||||
* start with role prefix as this is automatically inserted.
|
||||
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
|
||||
* customization
|
||||
*/
|
||||
public ExpressionInterceptUrlRegistry hasAnyRole(String... roles) {
|
||||
return access(ExpressionUrlAuthorizationConfigurer.hasAnyRole(roles));
|
||||
return access(ExpressionUrlAuthorizationConfigurer
|
||||
.hasAnyRole(ExpressionUrlAuthorizationConfigurer.this.rolePrefix, roles));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -490,8 +490,8 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* @return the {@link FeaturePolicyConfig} for additional configuration
|
||||
* @throws IllegalArgumentException if policyDirectives is {@code null} or empty
|
||||
* @since 5.1
|
||||
* @see FeaturePolicyHeaderWriter
|
||||
* @deprecated Use {@link #permissionsPolicy(Customizer)} instead.
|
||||
* @seeObjectPostProcessorConfiguration FeaturePolicyHeaderWriter
|
||||
*/
|
||||
@Deprecated
|
||||
public FeaturePolicyConfig featurePolicy(String policyDirectives) {
|
||||
|
||||
+3
-2
@@ -250,10 +250,11 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* {@link SimpleUrlLogoutSuccessHandler} using the {@link #logoutSuccessUrl(String)}.
|
||||
* @return the {@link LogoutSuccessHandler} to use
|
||||
*/
|
||||
private LogoutSuccessHandler getLogoutSuccessHandler() {
|
||||
public LogoutSuccessHandler getLogoutSuccessHandler() {
|
||||
LogoutSuccessHandler handler = this.logoutSuccessHandler;
|
||||
if (handler == null) {
|
||||
handler = createDefaultSuccessHandler();
|
||||
this.logoutSuccessHandler = handler;
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
@@ -312,7 +313,7 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* Gets the {@link LogoutHandler} instances that will be used.
|
||||
* @return the {@link LogoutHandler} instances. Cannot be null.
|
||||
*/
|
||||
List<LogoutHandler> getLogoutHandlers() {
|
||||
public List<LogoutHandler> getLogoutHandlers() {
|
||||
return this.logoutHandlers;
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -49,15 +49,15 @@ import org.springframework.util.Assert;
|
||||
* <li>{@link ClientRegistrationRepository}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see OAuth2AuthorizationRequestRedirectFilter
|
||||
* @see ClientRegistrationRepository
|
||||
* @deprecated It is not recommended to use the implicit flow due to the inherent risks of
|
||||
* returning access tokens in an HTTP redirect without any confirmation that it has been
|
||||
* received by the client. See reference
|
||||
* <a target="_blank" href="https://oauth.net/2/grant-types/implicit/">OAuth 2.0 Implicit
|
||||
* Grant</a>.
|
||||
* @author Joe Grandja
|
||||
* @since 5.0
|
||||
* @see OAuth2AuthorizationRequestRedirectFilter
|
||||
* @see ClientRegistrationRepository
|
||||
*/
|
||||
@Deprecated
|
||||
public final class ImplicitGrantConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
|
||||
+1
-1
@@ -683,10 +683,10 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
/**
|
||||
* Sets a custom {@link OAuth2User} type and associates it to the provided client
|
||||
* {@link ClientRegistration#getRegistrationId() registration identifier}.
|
||||
* @deprecated See {@link CustomUserTypesOAuth2UserService} for alternative usage.
|
||||
* @param customUserType a custom {@link OAuth2User} type
|
||||
* @param clientRegistrationId the client registration identifier
|
||||
* @return the {@link UserInfoEndpointConfig} for further configuration
|
||||
* @deprecated See {@link CustomUserTypesOAuth2UserService} for alternative usage.
|
||||
*/
|
||||
@Deprecated
|
||||
public UserInfoEndpointConfig customUserType(Class<? extends OAuth2User> customUserType,
|
||||
|
||||
+3
-3
@@ -42,8 +42,8 @@ import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.resource.authentication.OpaqueTokenAuthenticationProvider;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.SpringOpaqueTokenIntrospector;
|
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
|
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter;
|
||||
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
|
||||
@@ -454,7 +454,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
||||
public OpaqueTokenConfigurer introspectionUri(String introspectionUri) {
|
||||
Assert.notNull(introspectionUri, "introspectionUri cannot be null");
|
||||
this.introspectionUri = introspectionUri;
|
||||
this.introspector = () -> new NimbusOpaqueTokenIntrospector(this.introspectionUri, this.clientId,
|
||||
this.introspector = () -> new SpringOpaqueTokenIntrospector(this.introspectionUri, this.clientId,
|
||||
this.clientSecret);
|
||||
return this;
|
||||
}
|
||||
@@ -464,7 +464,7 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
||||
Assert.notNull(clientSecret, "clientSecret cannot be null");
|
||||
this.clientId = clientId;
|
||||
this.clientSecret = clientSecret;
|
||||
this.introspector = () -> new NimbusOpaqueTokenIntrospector(this.introspectionUri, this.clientId,
|
||||
this.introspector = () -> new SpringOpaqueTokenIntrospector(this.introspectionUri, this.clientId,
|
||||
this.clientSecret);
|
||||
return this;
|
||||
}
|
||||
|
||||
+73
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -33,6 +33,7 @@ import org.springframework.security.config.annotation.web.configurers.AbstractAu
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
|
||||
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
|
||||
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationRequestFactory;
|
||||
import org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider;
|
||||
@@ -44,7 +45,10 @@ import org.springframework.security.saml2.provider.service.servlet.filter.Saml2W
|
||||
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter;
|
||||
import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.DefaultSaml2AuthenticationRequestContextResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;
|
||||
import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestContextResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
|
||||
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
@@ -160,7 +164,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
* @param repo the repository of relying parties
|
||||
* @return the {@link Saml2LoginConfigurer} for further configuration
|
||||
*/
|
||||
public Saml2LoginConfigurer relyingPartyRegistrationRepository(RelyingPartyRegistrationRepository repo) {
|
||||
public Saml2LoginConfigurer<B> relyingPartyRegistrationRepository(RelyingPartyRegistrationRepository repo) {
|
||||
this.relyingPartyRegistrationRepository = repo;
|
||||
return this;
|
||||
}
|
||||
@@ -172,10 +176,19 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the URL to validate the credentials. If specified a custom URL, consider
|
||||
* specifying a custom {@link AuthenticationConverter} via
|
||||
* {@link #authenticationConverter(AuthenticationConverter)}, since the default
|
||||
* {@link AuthenticationConverter} implementation relies on the
|
||||
* <code>{registrationId}</code> path variable to be present in the URL
|
||||
* @param loginProcessingUrl the URL to validate the credentials
|
||||
* @return the {@link Saml2LoginConfigurer} for additional customization
|
||||
* @see Saml2WebSsoAuthenticationFilter#DEFAULT_FILTER_PROCESSES_URI
|
||||
*/
|
||||
@Override
|
||||
public Saml2LoginConfigurer<B> loginProcessingUrl(String loginProcessingUrl) {
|
||||
Assert.hasText(loginProcessingUrl, "loginProcessingUrl cannot be empty");
|
||||
Assert.state(loginProcessingUrl.contains("{registrationId}"), "{registrationId} path variable is required");
|
||||
this.loginProcessingUrl = loginProcessingUrl;
|
||||
return this;
|
||||
}
|
||||
@@ -201,11 +214,10 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
@Override
|
||||
public void init(B http) throws Exception {
|
||||
registerDefaultCsrfOverride(http);
|
||||
if (this.relyingPartyRegistrationRepository == null) {
|
||||
this.relyingPartyRegistrationRepository = getSharedOrBean(http, RelyingPartyRegistrationRepository.class);
|
||||
}
|
||||
relyingPartyRegistrationRepository(http);
|
||||
this.saml2WebSsoAuthenticationFilter = new Saml2WebSsoAuthenticationFilter(getAuthenticationConverter(http),
|
||||
this.loginProcessingUrl);
|
||||
setAuthenticationRequestRepository(http, this.saml2WebSsoAuthenticationFilter);
|
||||
setAuthenticationFilter(this.saml2WebSsoAuthenticationFilter);
|
||||
super.loginProcessingUrl(this.loginProcessingUrl);
|
||||
if (StringUtils.hasText(this.loginPage)) {
|
||||
@@ -252,16 +264,45 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
}
|
||||
}
|
||||
|
||||
private AuthenticationConverter getAuthenticationConverter(B http) {
|
||||
if (this.authenticationConverter == null) {
|
||||
return new Saml2AuthenticationTokenConverter(
|
||||
new DefaultRelyingPartyRegistrationResolver(this.relyingPartyRegistrationRepository));
|
||||
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository(B http) {
|
||||
if (this.relyingPartyRegistrationRepository == null) {
|
||||
this.relyingPartyRegistrationRepository = getSharedOrBean(http, RelyingPartyRegistrationRepository.class);
|
||||
}
|
||||
return this.authenticationConverter;
|
||||
return this.relyingPartyRegistrationRepository;
|
||||
}
|
||||
|
||||
private void setAuthenticationRequestRepository(B http,
|
||||
Saml2WebSsoAuthenticationFilter saml2WebSsoAuthenticationFilter) {
|
||||
saml2WebSsoAuthenticationFilter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));
|
||||
}
|
||||
|
||||
private AuthenticationConverter getAuthenticationConverter(B http) {
|
||||
if (this.authenticationConverter != null) {
|
||||
return this.authenticationConverter;
|
||||
}
|
||||
AuthenticationConverter authenticationConverterBean = getBeanOrNull(http,
|
||||
Saml2AuthenticationTokenConverter.class);
|
||||
if (authenticationConverterBean == null) {
|
||||
Assert.state(this.loginProcessingUrl.contains("{registrationId}"),
|
||||
"loginProcessingUrl must contain {registrationId} path variable");
|
||||
return new Saml2AuthenticationTokenConverter(
|
||||
(RelyingPartyRegistrationResolver) new DefaultRelyingPartyRegistrationResolver(
|
||||
this.relyingPartyRegistrationRepository));
|
||||
}
|
||||
return authenticationConverterBean;
|
||||
}
|
||||
|
||||
private String version() {
|
||||
String version = Version.getVersion();
|
||||
if (version != null) {
|
||||
return version;
|
||||
}
|
||||
return Version.class.getModule().getDescriptor().version().map(Object::toString)
|
||||
.orElseThrow(() -> new IllegalStateException("cannot determine OpenSAML version"));
|
||||
}
|
||||
|
||||
private void registerDefaultAuthenticationProvider(B http) {
|
||||
if (Version.getVersion().startsWith("4")) {
|
||||
if (version().startsWith("4")) {
|
||||
http.authenticationProvider(postProcess(new OpenSaml4AuthenticationProvider()));
|
||||
}
|
||||
else {
|
||||
@@ -302,6 +343,16 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
return idps;
|
||||
}
|
||||
|
||||
private Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> getAuthenticationRequestRepository(
|
||||
B http) {
|
||||
Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> repository = getBeanOrNull(http,
|
||||
Saml2AuthenticationRequestRepository.class);
|
||||
if (repository == null) {
|
||||
return new HttpSessionSaml2AuthenticationRequestRepository();
|
||||
}
|
||||
return repository;
|
||||
}
|
||||
|
||||
private <C> C getSharedOrBean(B http, Class<C> clazz) {
|
||||
C shared = http.getSharedObject(clazz);
|
||||
if (shared != null) {
|
||||
@@ -339,14 +390,18 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
private Filter build(B http) {
|
||||
Saml2AuthenticationRequestFactory authenticationRequestResolver = getResolver(http);
|
||||
Saml2AuthenticationRequestContextResolver contextResolver = getContextResolver(http);
|
||||
return postProcess(
|
||||
new Saml2WebSsoAuthenticationRequestFilter(contextResolver, authenticationRequestResolver));
|
||||
Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> repository = getAuthenticationRequestRepository(
|
||||
http);
|
||||
Saml2WebSsoAuthenticationRequestFilter filter = new Saml2WebSsoAuthenticationRequestFilter(contextResolver,
|
||||
authenticationRequestResolver);
|
||||
filter.setAuthenticationRequestRepository(repository);
|
||||
return postProcess(filter);
|
||||
}
|
||||
|
||||
private Saml2AuthenticationRequestFactory getResolver(B http) {
|
||||
Saml2AuthenticationRequestFactory resolver = getSharedOrBean(http, Saml2AuthenticationRequestFactory.class);
|
||||
if (resolver == null) {
|
||||
if (Version.getVersion().startsWith("4")) {
|
||||
if (version().startsWith("4")) {
|
||||
return new OpenSaml4AuthenticationRequestFactory();
|
||||
}
|
||||
return new OpenSamlAuthenticationRequestFactory();
|
||||
@@ -358,8 +413,9 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
Saml2AuthenticationRequestContextResolver resolver = getBeanOrNull(http,
|
||||
Saml2AuthenticationRequestContextResolver.class);
|
||||
if (resolver == null) {
|
||||
return new DefaultSaml2AuthenticationRequestContextResolver(new DefaultRelyingPartyRegistrationResolver(
|
||||
Saml2LoginConfigurer.this.relyingPartyRegistrationRepository));
|
||||
RelyingPartyRegistrationResolver relyingPartyRegistrationResolver = new DefaultRelyingPartyRegistrationResolver(
|
||||
Saml2LoginConfigurer.this.relyingPartyRegistrationRepository);
|
||||
return new DefaultSaml2AuthenticationRequestContextResolver(relyingPartyRegistrationResolver);
|
||||
}
|
||||
return resolver;
|
||||
}
|
||||
|
||||
+523
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.web.configurers.saml2;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.opensaml.core.Version;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.LogoutConfigurer;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlLogoutRequestValidator;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSamlLogoutResponseValidator;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator;
|
||||
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidator;
|
||||
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.web.DefaultRelyingPartyRegistrationResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.HttpSessionLogoutRequestRepository;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml3LogoutRequestResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml3LogoutResponseResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutRequestResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutResponseResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestRepository;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2RelyingPartyInitiatedLogoutSuccessHandler;
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutHandler;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessEventPublishingLogoutHandler;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||
import org.springframework.security.web.csrf.CsrfFilter;
|
||||
import org.springframework.security.web.csrf.CsrfLogoutHandler;
|
||||
import org.springframework.security.web.csrf.CsrfTokenRepository;
|
||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
|
||||
/**
|
||||
* Adds SAML 2.0 logout support.
|
||||
*
|
||||
* <h2>Security Filters</h2>
|
||||
*
|
||||
* The following Filters are populated
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link LogoutFilter}</li>
|
||||
* <li>{@link Saml2LogoutRequestFilter}</li>
|
||||
* <li>{@link Saml2LogoutResponseFilter}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* The following configuration options are available:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link #logoutUrl} - The URL to to process SAML 2.0 Logout</li>
|
||||
* <li>{@link LogoutRequestConfigurer#logoutRequestValidator} - The
|
||||
* {@link AuthenticationManager} for authenticating SAML 2.0 Logout Requests</li>
|
||||
* <li>{@link LogoutRequestConfigurer#logoutRequestResolver} - The
|
||||
* {@link Saml2LogoutRequestResolver} for creating SAML 2.0 Logout Requests</li>
|
||||
* <li>{@link LogoutRequestConfigurer#logoutRequestRepository} - The
|
||||
* {@link Saml2LogoutRequestRepository} for storing SAML 2.0 Logout Requests</li>
|
||||
* <li>{@link LogoutResponseConfigurer#logoutResponseValidator} - The
|
||||
* {@link AuthenticationManager} for authenticating SAML 2.0 Logout Responses</li>
|
||||
* <li>{@link LogoutResponseConfigurer#logoutResponseResolver} - The
|
||||
* {@link Saml2LogoutResponseResolver} for creating SAML 2.0 Logout Responses</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Shared Objects Created</h2>
|
||||
*
|
||||
* No shared Objects are created
|
||||
*
|
||||
* <h2>Shared Objects Used</h2>
|
||||
*
|
||||
* Uses {@link CsrfTokenRepository} to add the {@link CsrfLogoutHandler}.
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 5.6
|
||||
* @see Saml2LogoutConfigurer
|
||||
*/
|
||||
public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
extends AbstractHttpConfigurer<Saml2LogoutConfigurer<H>, H> {
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
private RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;
|
||||
|
||||
private String logoutUrl = "/logout";
|
||||
|
||||
private List<LogoutHandler> logoutHandlers = new ArrayList<>();
|
||||
|
||||
private LogoutSuccessHandler logoutSuccessHandler;
|
||||
|
||||
private LogoutRequestConfigurer logoutRequestConfigurer;
|
||||
|
||||
private LogoutResponseConfigurer logoutResponseConfigurer;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @see HttpSecurity#logout()
|
||||
*/
|
||||
public Saml2LogoutConfigurer(ApplicationContext context) {
|
||||
this.context = context;
|
||||
this.logoutHandlers.add(new SecurityContextLogoutHandler());
|
||||
this.logoutHandlers.add(new LogoutSuccessEventPublishingLogoutHandler());
|
||||
SimpleUrlLogoutSuccessHandler logoutSuccessHandler = new SimpleUrlLogoutSuccessHandler();
|
||||
logoutSuccessHandler.setDefaultTargetUrl("/login?logout");
|
||||
this.logoutSuccessHandler = logoutSuccessHandler;
|
||||
this.logoutRequestConfigurer = new LogoutRequestConfigurer();
|
||||
this.logoutResponseConfigurer = new LogoutResponseConfigurer();
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL by which the relying or asserting party can trigger logout.
|
||||
*
|
||||
* <p>
|
||||
* The Relying Party triggers logout by POSTing to the endpoint. The Asserting Party
|
||||
* triggers logout based on what is specified by
|
||||
* {@link RelyingPartyRegistration#getSingleLogoutServiceBinding()}.
|
||||
* @param logoutUrl the URL that will invoke logout
|
||||
* @return the {@link LogoutConfigurer} for further customizations
|
||||
* @see LogoutConfigurer#logoutUrl(String)
|
||||
* @see HttpSecurity#csrf()
|
||||
*/
|
||||
public Saml2LogoutConfigurer<H> logoutUrl(String logoutUrl) {
|
||||
this.logoutUrl = logoutUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RelyingPartyRegistrationRepository} of relying parties, each party
|
||||
* representing a service provider, SP and this host, and identity provider, IDP pair
|
||||
* that communicate with each other.
|
||||
* @param repo the repository of relying parties
|
||||
* @return the {@link Saml2LogoutConfigurer} for further customizations
|
||||
*/
|
||||
public Saml2LogoutConfigurer<H> relyingPartyRegistrationRepository(RelyingPartyRegistrationRepository repo) {
|
||||
this.relyingPartyRegistrationRepository = repo;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configurer for SAML 2.0 Logout Request components
|
||||
* @return the {@link LogoutRequestConfigurer} for further customizations
|
||||
*/
|
||||
public LogoutRequestConfigurer logoutRequest() {
|
||||
return this.logoutRequestConfigurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures SAML 2.0 Logout Request components
|
||||
* @param logoutRequestConfigurerCustomizer the {@link Customizer} to provide more
|
||||
* options for the {@link LogoutRequestConfigurer}
|
||||
* @return the {@link Saml2LogoutConfigurer} for further customizations
|
||||
*/
|
||||
public Saml2LogoutConfigurer<H> logoutRequest(
|
||||
Customizer<LogoutRequestConfigurer> logoutRequestConfigurerCustomizer) {
|
||||
logoutRequestConfigurerCustomizer.customize(this.logoutRequestConfigurer);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configurer for SAML 2.0 Logout Response components
|
||||
* @return the {@link LogoutResponseConfigurer} for further customizations
|
||||
*/
|
||||
public LogoutResponseConfigurer logoutResponse() {
|
||||
return this.logoutResponseConfigurer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures SAML 2.0 Logout Request components
|
||||
* @param logoutResponseConfigurerCustomizer the {@link Customizer} to provide more
|
||||
* options for the {@link LogoutResponseConfigurer}
|
||||
* @return the {@link Saml2LogoutConfigurer} for further customizations
|
||||
*/
|
||||
public Saml2LogoutConfigurer<H> logoutResponse(
|
||||
Customizer<LogoutResponseConfigurer> logoutResponseConfigurerCustomizer) {
|
||||
logoutResponseConfigurerCustomizer.customize(this.logoutResponseConfigurer);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void configure(H http) throws Exception {
|
||||
LogoutConfigurer<H> logout = http.getConfigurer(LogoutConfigurer.class);
|
||||
if (logout != null) {
|
||||
this.logoutHandlers = logout.getLogoutHandlers();
|
||||
this.logoutSuccessHandler = logout.getLogoutSuccessHandler();
|
||||
}
|
||||
RelyingPartyRegistrationResolver registrations = relyingPartyRegistrationResolver(http);
|
||||
http.addFilterBefore(createLogoutRequestProcessingFilter(registrations), CsrfFilter.class);
|
||||
http.addFilterBefore(createLogoutResponseProcessingFilter(registrations), CsrfFilter.class);
|
||||
http.addFilterBefore(createRelyingPartyLogoutFilter(registrations), LogoutFilter.class);
|
||||
}
|
||||
|
||||
private RelyingPartyRegistrationResolver relyingPartyRegistrationResolver(H http) {
|
||||
RelyingPartyRegistrationRepository registrations = getRelyingPartyRegistrationRepository(http);
|
||||
return new DefaultRelyingPartyRegistrationResolver(registrations);
|
||||
}
|
||||
|
||||
private RelyingPartyRegistrationRepository getRelyingPartyRegistrationRepository(H http) {
|
||||
if (this.relyingPartyRegistrationRepository != null) {
|
||||
return this.relyingPartyRegistrationRepository;
|
||||
}
|
||||
Saml2LoginConfigurer<H> login = http.getConfigurer(Saml2LoginConfigurer.class);
|
||||
if (login != null) {
|
||||
this.relyingPartyRegistrationRepository = login.relyingPartyRegistrationRepository(http);
|
||||
}
|
||||
else {
|
||||
this.relyingPartyRegistrationRepository = getBeanOrNull(RelyingPartyRegistrationRepository.class);
|
||||
}
|
||||
return this.relyingPartyRegistrationRepository;
|
||||
}
|
||||
|
||||
private Saml2LogoutRequestFilter createLogoutRequestProcessingFilter(
|
||||
RelyingPartyRegistrationResolver registrations) {
|
||||
LogoutHandler[] logoutHandlers = this.logoutHandlers.toArray(new LogoutHandler[0]);
|
||||
Saml2LogoutResponseResolver logoutResponseResolver = createSaml2LogoutResponseResolver(registrations);
|
||||
Saml2LogoutRequestFilter filter = new Saml2LogoutRequestFilter(registrations,
|
||||
this.logoutRequestConfigurer.logoutRequestValidator(), logoutResponseResolver, logoutHandlers);
|
||||
filter.setLogoutRequestMatcher(createLogoutRequestMatcher());
|
||||
return postProcess(filter);
|
||||
}
|
||||
|
||||
private Saml2LogoutResponseFilter createLogoutResponseProcessingFilter(
|
||||
RelyingPartyRegistrationResolver registrations) {
|
||||
Saml2LogoutResponseFilter logoutResponseFilter = new Saml2LogoutResponseFilter(registrations,
|
||||
this.logoutResponseConfigurer.logoutResponseValidator(), this.logoutSuccessHandler);
|
||||
logoutResponseFilter.setLogoutRequestMatcher(createLogoutResponseMatcher());
|
||||
logoutResponseFilter.setLogoutRequestRepository(this.logoutRequestConfigurer.logoutRequestRepository);
|
||||
return postProcess(logoutResponseFilter);
|
||||
}
|
||||
|
||||
private LogoutFilter createRelyingPartyLogoutFilter(RelyingPartyRegistrationResolver registrations) {
|
||||
LogoutHandler[] logoutHandlers = this.logoutHandlers.toArray(new LogoutHandler[0]);
|
||||
Saml2RelyingPartyInitiatedLogoutSuccessHandler logoutRequestSuccessHandler = createSaml2LogoutRequestSuccessHandler(
|
||||
registrations);
|
||||
LogoutFilter logoutFilter = new LogoutFilter(logoutRequestSuccessHandler, logoutHandlers);
|
||||
logoutFilter.setLogoutRequestMatcher(createLogoutMatcher());
|
||||
return postProcess(logoutFilter);
|
||||
}
|
||||
|
||||
private RequestMatcher createLogoutMatcher() {
|
||||
RequestMatcher logout = new AntPathRequestMatcher(this.logoutUrl, "POST");
|
||||
RequestMatcher saml2 = new Saml2RequestMatcher();
|
||||
return new AndRequestMatcher(logout, saml2);
|
||||
}
|
||||
|
||||
private RequestMatcher createLogoutRequestMatcher() {
|
||||
RequestMatcher logout = new AntPathRequestMatcher(this.logoutRequestConfigurer.logoutUrl);
|
||||
RequestMatcher samlRequest = new ParameterRequestMatcher("SAMLRequest");
|
||||
return new AndRequestMatcher(logout, samlRequest);
|
||||
}
|
||||
|
||||
private RequestMatcher createLogoutResponseMatcher() {
|
||||
RequestMatcher logout = new AntPathRequestMatcher(this.logoutResponseConfigurer.logoutUrl);
|
||||
RequestMatcher samlResponse = new ParameterRequestMatcher("SAMLResponse");
|
||||
return new AndRequestMatcher(logout, samlResponse);
|
||||
}
|
||||
|
||||
private Saml2RelyingPartyInitiatedLogoutSuccessHandler createSaml2LogoutRequestSuccessHandler(
|
||||
RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) {
|
||||
Saml2LogoutRequestResolver logoutRequestResolver = this.logoutRequestConfigurer
|
||||
.logoutRequestResolver(relyingPartyRegistrationResolver);
|
||||
return new Saml2RelyingPartyInitiatedLogoutSuccessHandler(logoutRequestResolver);
|
||||
}
|
||||
|
||||
private Saml2LogoutResponseResolver createSaml2LogoutResponseResolver(
|
||||
RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) {
|
||||
return this.logoutResponseConfigurer.logoutResponseResolver(relyingPartyRegistrationResolver);
|
||||
}
|
||||
|
||||
private <C> C getBeanOrNull(Class<C> clazz) {
|
||||
if (this.context == null) {
|
||||
return null;
|
||||
}
|
||||
if (this.context.getBeanNamesForType(clazz).length == 0) {
|
||||
return null;
|
||||
}
|
||||
return this.context.getBean(clazz);
|
||||
}
|
||||
|
||||
private String version() {
|
||||
String version = Version.getVersion();
|
||||
if (version != null) {
|
||||
return version;
|
||||
}
|
||||
return Version.class.getModule().getDescriptor().version().map(Object::toString)
|
||||
.orElseThrow(() -> new IllegalStateException("cannot determine OpenSAML version"));
|
||||
}
|
||||
|
||||
/**
|
||||
* A configurer for SAML 2.0 LogoutRequest components
|
||||
*/
|
||||
public final class LogoutRequestConfigurer {
|
||||
|
||||
private String logoutUrl = "/logout/saml2/slo";
|
||||
|
||||
private Saml2LogoutRequestValidator logoutRequestValidator;
|
||||
|
||||
private Saml2LogoutRequestResolver logoutRequestResolver;
|
||||
|
||||
private Saml2LogoutRequestRepository logoutRequestRepository = new HttpSessionLogoutRequestRepository();
|
||||
|
||||
LogoutRequestConfigurer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL by which the asserting party can send a SAML 2.0 Logout Request
|
||||
*
|
||||
* <p>
|
||||
* The Asserting Party should use whatever HTTP method specified in
|
||||
* {@link RelyingPartyRegistration#getSingleLogoutServiceBinding()}.
|
||||
* @param logoutUrl the URL that will receive the SAML 2.0 Logout Request
|
||||
* @return the {@link LogoutRequestConfigurer} for further customizations
|
||||
* @see Saml2LogoutConfigurer#logoutUrl(String)
|
||||
*/
|
||||
public LogoutRequestConfigurer logoutUrl(String logoutUrl) {
|
||||
this.logoutUrl = logoutUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@link LogoutHandler} for processing a logout request from the
|
||||
* asserting party
|
||||
* @param authenticator the {@link Saml2LogoutRequestValidator} to use
|
||||
* @return the {@link LogoutRequestConfigurer} for further customizations
|
||||
*/
|
||||
public LogoutRequestConfigurer logoutRequestValidator(Saml2LogoutRequestValidator authenticator) {
|
||||
this.logoutRequestValidator = authenticator;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@link Saml2LogoutRequestResolver} for producing a logout request to
|
||||
* send to the asserting party
|
||||
* @param logoutRequestResolver the {@link Saml2LogoutRequestResolver} to use
|
||||
* @return the {@link LogoutRequestConfigurer} for further customizations
|
||||
*/
|
||||
public LogoutRequestConfigurer logoutRequestResolver(Saml2LogoutRequestResolver logoutRequestResolver) {
|
||||
this.logoutRequestResolver = logoutRequestResolver;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@link Saml2LogoutRequestRepository} for storing logout requests
|
||||
* @param logoutRequestRepository the {@link Saml2LogoutRequestRepository} to use
|
||||
* @return the {@link LogoutRequestConfigurer} for further customizations
|
||||
*/
|
||||
public LogoutRequestConfigurer logoutRequestRepository(Saml2LogoutRequestRepository logoutRequestRepository) {
|
||||
this.logoutRequestRepository = logoutRequestRepository;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Saml2LogoutConfigurer<H> and() {
|
||||
return Saml2LogoutConfigurer.this;
|
||||
}
|
||||
|
||||
private Saml2LogoutRequestValidator logoutRequestValidator() {
|
||||
if (this.logoutRequestValidator == null) {
|
||||
return new OpenSamlLogoutRequestValidator();
|
||||
}
|
||||
return this.logoutRequestValidator;
|
||||
}
|
||||
|
||||
private Saml2LogoutRequestResolver logoutRequestResolver(
|
||||
RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) {
|
||||
if (this.logoutRequestResolver != null) {
|
||||
return this.logoutRequestResolver;
|
||||
}
|
||||
if (version().startsWith("4")) {
|
||||
return new OpenSaml4LogoutRequestResolver(relyingPartyRegistrationResolver);
|
||||
}
|
||||
return new OpenSaml3LogoutRequestResolver(relyingPartyRegistrationResolver);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public final class LogoutResponseConfigurer {
|
||||
|
||||
private String logoutUrl = "/logout/saml2/slo";
|
||||
|
||||
private Saml2LogoutResponseValidator logoutResponseValidator;
|
||||
|
||||
private Saml2LogoutResponseResolver logoutResponseResolver;
|
||||
|
||||
LogoutResponseConfigurer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* The URL by which the asserting party can send a SAML 2.0 Logout Response
|
||||
*
|
||||
* <p>
|
||||
* The Asserting Party should use whatever HTTP method specified in
|
||||
* {@link RelyingPartyRegistration#getSingleLogoutServiceBinding()}.
|
||||
* @param logoutUrl the URL that will receive the SAML 2.0 Logout Response
|
||||
* @return the {@link LogoutResponseConfigurer} for further customizations
|
||||
* @see Saml2LogoutConfigurer#logoutUrl(String)
|
||||
*/
|
||||
public LogoutResponseConfigurer logoutUrl(String logoutUrl) {
|
||||
this.logoutUrl = logoutUrl;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@link LogoutHandler} for processing a logout response from the
|
||||
* asserting party
|
||||
* @param authenticator the {@link AuthenticationManager} to use
|
||||
* @return the {@link LogoutRequestConfigurer} for further customizations
|
||||
*/
|
||||
public LogoutResponseConfigurer logoutResponseValidator(Saml2LogoutResponseValidator authenticator) {
|
||||
this.logoutResponseValidator = authenticator;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@link Saml2LogoutRequestResolver} for producing a logout response to
|
||||
* send to the asserting party
|
||||
* @param logoutResponseResolver the {@link Saml2LogoutResponseResolver} to use
|
||||
* @return the {@link LogoutRequestConfigurer} for further customizations
|
||||
*/
|
||||
public LogoutResponseConfigurer logoutResponseResolver(Saml2LogoutResponseResolver logoutResponseResolver) {
|
||||
this.logoutResponseResolver = logoutResponseResolver;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Saml2LogoutConfigurer<H> and() {
|
||||
return Saml2LogoutConfigurer.this;
|
||||
}
|
||||
|
||||
private Saml2LogoutResponseValidator logoutResponseValidator() {
|
||||
if (this.logoutResponseValidator == null) {
|
||||
return new OpenSamlLogoutResponseValidator();
|
||||
}
|
||||
return this.logoutResponseValidator;
|
||||
}
|
||||
|
||||
private Saml2LogoutResponseResolver logoutResponseResolver(
|
||||
RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) {
|
||||
if (this.logoutResponseResolver == null) {
|
||||
if (version().startsWith("4")) {
|
||||
return new OpenSaml4LogoutResponseResolver(relyingPartyRegistrationResolver);
|
||||
}
|
||||
return new OpenSaml3LogoutResponseResolver(relyingPartyRegistrationResolver);
|
||||
}
|
||||
return this.logoutResponseResolver;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class Saml2RequestMatcher implements RequestMatcher {
|
||||
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication == null) {
|
||||
return false;
|
||||
}
|
||||
return authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ParameterRequestMatcher implements RequestMatcher {
|
||||
|
||||
Predicate<String> test = Objects::nonNull;
|
||||
|
||||
String name;
|
||||
|
||||
ParameterRequestMatcher(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
return this.test.test(request.getParameter(this.name));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class NoopLogoutHandler implements LogoutHandler {
|
||||
|
||||
@Override
|
||||
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -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.
|
||||
@@ -89,7 +89,7 @@ class ServerHttpSecurityConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebFluxConfigurer authenticationPrincipalArgumentResolverConfigurer(
|
||||
static WebFluxConfigurer authenticationPrincipalArgumentResolverConfigurer(
|
||||
ObjectProvider<AuthenticationPrincipalArgumentResolver> authenticationPrincipalArgumentResolver) {
|
||||
return new WebFluxConfigurer() {
|
||||
|
||||
|
||||
+1
-1
@@ -37,9 +37,9 @@ import org.springframework.web.servlet.support.RequestDataValueProcessor;
|
||||
* {@link AuthenticationPrincipalArgumentResolver} as a
|
||||
* {@link HandlerMethodArgumentResolver}.
|
||||
*
|
||||
* @deprecated This is applied internally using SpringWebMvcImportSelector
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @deprecated This is applied internally using SpringWebMvcImportSelector
|
||||
*/
|
||||
@Deprecated
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
||||
+1
-1
@@ -79,8 +79,8 @@ import org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsSe
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @since 4.0
|
||||
* @author Rob Winch
|
||||
* @since 4.0
|
||||
*/
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 100)
|
||||
@Import(ObjectPostProcessorConfiguration.class)
|
||||
|
||||
+6
@@ -725,6 +725,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
|
||||
* @param atFilter the location of another [Filter] that is already registered
|
||||
* (i.e. known) with Spring Security.
|
||||
*/
|
||||
@Deprecated("Use 'addFilterAt<T>(filter)' instead.")
|
||||
fun addFilterAt(filter: Filter, atFilter: Class<out Filter>) {
|
||||
this.http.addFilterAt(filter, atFilter)
|
||||
}
|
||||
@@ -751,6 +752,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
|
||||
* @param T the location of another [Filter] that is already registered
|
||||
* (i.e. known) with Spring Security.
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
inline fun <reified T: Filter> addFilterAt(filter: Filter) {
|
||||
this.addFilterAt(filter, T::class.java)
|
||||
}
|
||||
@@ -776,6 +778,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
|
||||
* @param afterFilter the location of another [Filter] that is already registered
|
||||
* (i.e. known) with Spring Security.
|
||||
*/
|
||||
@Deprecated("Use 'addFilterAfter<T>(filter)' instead.")
|
||||
fun addFilterAfter(filter: Filter, afterFilter: Class<out Filter>) {
|
||||
this.http.addFilterAfter(filter, afterFilter)
|
||||
}
|
||||
@@ -802,6 +805,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
|
||||
* @param T the location of another [Filter] that is already registered
|
||||
* (i.e. known) with Spring Security.
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
inline fun <reified T: Filter> addFilterAfter(filter: Filter) {
|
||||
this.addFilterAfter(filter, T::class.java)
|
||||
}
|
||||
@@ -827,6 +831,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
|
||||
* @param beforeFilter the location of another [Filter] that is already registered
|
||||
* (i.e. known) with Spring Security.
|
||||
*/
|
||||
@Deprecated("Use 'addFilterBefore<T>(filter)' instead.")
|
||||
fun addFilterBefore(filter: Filter, beforeFilter: Class<out Filter>) {
|
||||
this.http.addFilterBefore(filter, beforeFilter)
|
||||
}
|
||||
@@ -853,6 +858,7 @@ class HttpSecurityDsl(private val http: HttpSecurity, private val init: HttpSecu
|
||||
* @param T the location of another [Filter] that is already registered
|
||||
* (i.e. known) with Spring Security.
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
inline fun <reified T: Filter> addFilterBefore(filter: Filter) {
|
||||
this.addFilterBefore(filter, T::class.java)
|
||||
}
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -53,6 +53,7 @@ class UserInfoEndpointDsl {
|
||||
* @param customUserType a custom [OAuth2User] type
|
||||
* @param clientRegistrationId the client registration identifier
|
||||
*/
|
||||
@Deprecated("Use 'customUserType<T>(clientRegistrationId)' instead.")
|
||||
fun customUserType(customUserType: Class<out OAuth2User>, clientRegistrationId: String) {
|
||||
customUserTypePair = Pair(customUserType, clientRegistrationId)
|
||||
}
|
||||
@@ -65,6 +66,7 @@ class UserInfoEndpointDsl {
|
||||
* @param T a custom [OAuth2User] type
|
||||
* @param clientRegistrationId the client registration identifier
|
||||
*/
|
||||
@Suppress("DEPRECATION")
|
||||
inline fun <reified T: OAuth2User> customUserType(clientRegistrationId: String) {
|
||||
customUserType(T::class.java, clientRegistrationId)
|
||||
}
|
||||
|
||||
+1
-1
@@ -1127,4 +1127,4 @@ position =
|
||||
## The explicit position at which the custom-filter should be placed in the chain. Use if you are replacing a standard filter.
|
||||
attribute position {named-security-filter}
|
||||
|
||||
named-security-filter = "FIRST" | "CHANNEL_FILTER" | "SECURITY_CONTEXT_FILTER" | "CONCURRENT_SESSION_FILTER" | "WEB_ASYNC_MANAGER_FILTER" | "HEADERS_FILTER" | "CORS_FILTER" | "CSRF_FILTER" | "LOGOUT_FILTER" | "OAUTH2_AUTHORIZATION_REQUEST_FILTER" | "X509_FILTER" | "PRE_AUTH_FILTER" | "CAS_FILTER" | "OAUTH2_LOGIN_FILTER" | "FORM_LOGIN_FILTER" | "OPENID_FILTER" | "LOGIN_PAGE_FILTER" |"LOGOUT_PAGE_FILTER" | "DIGEST_AUTH_FILTER" | "BEARER_TOKEN_AUTH_FILTER" | "BASIC_AUTH_FILTER" | "REQUEST_CACHE_FILTER" | "SERVLET_API_SUPPORT_FILTER" | "JAAS_API_SUPPORT_FILTER" | "REMEMBER_ME_FILTER" | "ANONYMOUS_FILTER" | "OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER" | "SESSION_MANAGEMENT_FILTER" | "EXCEPTION_TRANSLATION_FILTER" | "FILTER_SECURITY_INTERCEPTOR" | "SWITCH_USER_FILTER" | "LAST"
|
||||
named-security-filter = "FIRST" | "CHANNEL_FILTER" | "SECURITY_CONTEXT_FILTER" | "CONCURRENT_SESSION_FILTER" | "WEB_ASYNC_MANAGER_FILTER" | "HEADERS_FILTER" | "CORS_FILTER" | "CSRF_FILTER" | "LOGOUT_FILTER" | "OAUTH2_AUTHORIZATION_REQUEST_FILTER" | "X509_FILTER" | "PRE_AUTH_FILTER" | "CAS_FILTER" | "OAUTH2_LOGIN_FILTER" | "FORM_LOGIN_FILTER" | "OPENID_FILTER" | "LOGIN_PAGE_FILTER" |"LOGOUT_PAGE_FILTER" | "DIGEST_AUTH_FILTER" | "BEARER_TOKEN_AUTH_FILTER" | "BASIC_AUTH_FILTER" | "REQUEST_CACHE_FILTER" | "SERVLET_API_SUPPORT_FILTER" | "JAAS_API_SUPPORT_FILTER" | "REMEMBER_ME_FILTER" | "ANONYMOUS_FILTER" | "OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER" | "WELL_KNOWN_CHANGE_PASSWORD_REDIRECT_FILTER" | "SESSION_MANAGEMENT_FILTER" | "EXCEPTION_TRANSLATION_FILTER" | "FILTER_SECURITY_INTERCEPTOR" | "SWITCH_USER_FILTER" | "LAST"
|
||||
|
||||
+10
-10
@@ -1066,7 +1066,7 @@
|
||||
<xs:attributeGroup ref="security:logout.attlist"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element ref="security:password-management"/>
|
||||
<xs:element ref="security:password-management"/>
|
||||
<xs:element name="session-management">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Session-management related functionality is implemented by the addition of a
|
||||
@@ -2161,16 +2161,15 @@
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
<xs:element name="password-management">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Adds support for the password management.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:attributeGroup ref="security:password-management.attlist"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:annotation>
|
||||
<xs:documentation>Adds support for the password management.
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:complexType>
|
||||
<xs:attributeGroup ref="security:password-management.attlist"/>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:attributeGroup name="password-management.attlist">
|
||||
<xs:attribute name="change-password-page" type="xs:string">
|
||||
<xs:annotation>
|
||||
@@ -2179,6 +2178,7 @@
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
</xs:attributeGroup>
|
||||
|
||||
<xs:attributeGroup name="session-management.attlist">
|
||||
<xs:attribute name="session-fixation-protection">
|
||||
<xs:annotation>
|
||||
|
||||
+9
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
|
||||
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
|
||||
import org.springframework.security.config.util.SpringSecurityVersions;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
@@ -133,6 +134,13 @@ public class SecurityNamespaceHandlerTests {
|
||||
// should load just fine since no websocket block
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenOldVersionThenErrorMessageContainsCorrectVersion() {
|
||||
assertThatExceptionOfType(BeanDefinitionParsingException.class)
|
||||
.isThrownBy(() -> new InMemoryXmlApplicationContext(XML_AUTHENTICATION_MANAGER, "3.0", null))
|
||||
.withMessageContaining(SpringSecurityVersions.getCurrentXsdVersionFromSpringSchemas());
|
||||
}
|
||||
|
||||
private void expectClassUtilsForNameThrowsNoClassDefFoundError(String className) {
|
||||
this.classUtils.when(() -> ClassUtils.forName(eq(FILTER_CHAIN_PROXY_CLASSNAME), any()))
|
||||
.thenThrow(new NoClassDefFoundError(className));
|
||||
|
||||
+13
@@ -350,6 +350,14 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||
.isThrownBy(() -> this.businessService.repeatedAnnotations());
|
||||
}
|
||||
|
||||
// gh-10305
|
||||
@WithMockUser
|
||||
@Test
|
||||
public void beanInSpelWhenEvaluatedThenLooksUpBean() {
|
||||
this.spring.register(MethodSecurityServiceConfig.class).autowire();
|
||||
this.methodSecurityService.preAuthorizeBean(true);
|
||||
}
|
||||
|
||||
@EnableMethodSecurity
|
||||
static class MethodSecurityServiceConfig {
|
||||
|
||||
@@ -358,6 +366,11 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||
return new MethodSecurityServiceImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Authz authz() {
|
||||
return new Authz();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableMethodSecurity(jsr250Enabled = true)
|
||||
|
||||
+123
-1
@@ -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.
|
||||
@@ -41,6 +41,7 @@ import org.springframework.security.config.annotation.authentication.builders.Au
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||
import org.springframework.security.config.test.SpringTestContext;
|
||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -75,6 +76,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Eleftheria Stein
|
||||
* @author Yanming Zhou
|
||||
*/
|
||||
@ExtendWith(SpringTestContextExtension.class)
|
||||
public class ExpressionUrlAuthorizationConfigurerTests {
|
||||
@@ -232,6 +234,28 @@ public class ExpressionUrlAuthorizationConfigurerTests {
|
||||
this.mvc.perform(requestWithAdmin).andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenHasAnyRoleUserWithTestRolePrefixConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(RoleUserWithTestRolePrefixConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||
.with(user("user")
|
||||
.authorities(new SimpleGrantedAuthority("TEST_USER")));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenHasAnyRoleUserWithEmptyRolePrefixConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(RoleUserWithEmptyRolePrefixConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||
.with(user("user")
|
||||
.authorities(new SimpleGrantedAuthority("USER")));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenRoleUserOrAdminConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(RoleUserOrAdminConfig.class, BasicController.class).autowire();
|
||||
@@ -263,6 +287,28 @@ public class ExpressionUrlAuthorizationConfigurerTests {
|
||||
this.mvc.perform(requestWithRoleOther).andExpect(status().isForbidden());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenRoleUserOrAdminWithTestRolePrefixConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(RoleUserOrAdminWithTestRolePrefixConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||
.with(user("user")
|
||||
.authorities(new SimpleGrantedAuthority("TEST_USER")));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenRoleUserOrAdminWithEmptyRolePrefixConfiguredAndRoleIsUserThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(RoleUserOrAdminWithEmptyRolePrefixConfig.class, BasicController.class).autowire();
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder requestWithUser = get("/")
|
||||
.with(user("user")
|
||||
.authorities(new SimpleGrantedAuthority("USER")));
|
||||
// @formatter:on
|
||||
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenHasIpAddressConfiguredAndIpAddressMatchesThenRespondsWithOk() throws Exception {
|
||||
this.spring.register(HasIpAddressConfig.class, BasicController.class).autowire();
|
||||
@@ -628,6 +674,44 @@ public class ExpressionUrlAuthorizationConfigurerTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RoleUserWithTestRolePrefixConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasAnyRole("USER");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
GrantedAuthorityDefaults grantedAuthorityDefaults() {
|
||||
return new GrantedAuthorityDefaults("TEST_");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RoleUserWithEmptyRolePrefixConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasAnyRole("USER");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
GrantedAuthorityDefaults grantedAuthorityDefaults() {
|
||||
return new GrantedAuthorityDefaults("");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RoleUserOrAdminConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@@ -642,6 +726,44 @@ public class ExpressionUrlAuthorizationConfigurerTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RoleUserOrAdminWithTestRolePrefixConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasAnyRole("USER", "ADMIN");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
GrantedAuthorityDefaults grantedAuthorityDefaults() {
|
||||
return new GrantedAuthorityDefaults("TEST_");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class RoleUserOrAdminWithEmptyRolePrefixConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeRequests()
|
||||
.anyRequest().hasAnyRole("USER", "ADMIN");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
GrantedAuthorityDefaults grantedAuthorityDefaults() {
|
||||
return new GrantedAuthorityDefaults("");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class HasIpAddressConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
|
||||
+6
-4
@@ -97,7 +97,9 @@ public class LogoutConfigurerTests {
|
||||
@Test
|
||||
public void configureWhenRegisteringObjectPostProcessorThenInvokedOnLogoutFilter() {
|
||||
this.spring.register(ObjectPostProcessorConfig.class).autowire();
|
||||
verify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(LogoutFilter.class));
|
||||
ObjectPostProcessor<LogoutFilter> objectPostProcessor = this.spring.getContext()
|
||||
.getBean(ObjectPostProcessor.class);
|
||||
verify(objectPostProcessor).postProcess(any(LogoutFilter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -361,7 +363,7 @@ public class LogoutConfigurerTests {
|
||||
@EnableWebSecurity
|
||||
static class ObjectPostProcessorConfig extends WebSecurityConfigurerAdapter {
|
||||
|
||||
static ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
|
||||
ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
@@ -372,8 +374,8 @@ public class LogoutConfigurerTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
static ObjectPostProcessor<Object> objectPostProcessor() {
|
||||
return objectPostProcessor;
|
||||
ObjectPostProcessor<Object> objectPostProcessor() {
|
||||
return this.objectPostProcessor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-9
@@ -360,7 +360,7 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
this.spring.register(JwkSetUriConfig.class).autowire();
|
||||
// engage csrf
|
||||
// @formatter:off
|
||||
this.mvc.perform(post("/").with(bearerToken("token").asParam()))
|
||||
this.mvc.perform(post("/").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE).with(bearerToken("token").asParam()))
|
||||
.andExpect(status().isForbidden())
|
||||
.andExpect(header().doesNotExist(HttpHeaders.WWW_AUTHENTICATE));
|
||||
// @formatter:on
|
||||
@@ -370,7 +370,7 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
public void postWhenCsrfDisabledWithBearerTokenAsFormParameterThenIgnoresToken() throws Exception {
|
||||
this.spring.register(CsrfDisabledConfig.class).autowire();
|
||||
// @formatter:off
|
||||
this.mvc.perform(post("/").with(bearerToken("token").asParam()))
|
||||
this.mvc.perform(post("/").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE).with(bearerToken("token").asParam()))
|
||||
.andExpect(status().isUnauthorized())
|
||||
.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, "Bearer"));
|
||||
// @formatter:on
|
||||
@@ -536,7 +536,7 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
mockRestOperations(jwks("Default"));
|
||||
String token = this.token("ValidNoScopes");
|
||||
// @formatter:off
|
||||
this.mvc.perform(post("/authenticated").with(bearerToken(token)))
|
||||
this.mvc.perform(post("/authenticated").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE).with(bearerToken(token)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string("test-subject"));
|
||||
// @formatter:on
|
||||
@@ -558,7 +558,7 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
mockRestOperations(jwks("Default"));
|
||||
String token = this.token("Expired");
|
||||
// @formatter:off
|
||||
this.mvc.perform(post("/authenticated").with(bearerToken(token)))
|
||||
this.mvc.perform(post("/authenticated").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE).with(bearerToken(token)))
|
||||
.andExpect(status().isUnauthorized())
|
||||
.andExpect(invalidTokenHeader("An error occurred while attempting to decode the Jwt"));
|
||||
// @formatter:on
|
||||
@@ -626,7 +626,7 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
this.mvc.perform(get("/authenticated").with(bearerToken(JWT_TOKEN)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(JWT_SUBJECT));
|
||||
this.mvc.perform(post("/authenticated").param("access_token", JWT_TOKEN))
|
||||
this.mvc.perform(post("/authenticated").header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE).param("access_token", JWT_TOKEN))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string(JWT_SUBJECT));
|
||||
// @formatter:on
|
||||
@@ -659,6 +659,7 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
given(decoder.decode(anyString())).willReturn(JWT);
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder request = post("/authenticated")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||
.param("access_token", JWT_TOKEN)
|
||||
.with(bearerToken(JWT_TOKEN))
|
||||
.with(csrf());
|
||||
@@ -1124,7 +1125,7 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
opaqueTokenConfigurer.introspector(client);
|
||||
opaqueTokenConfigurer.introspectionUri(INTROSPECTION_URI);
|
||||
opaqueTokenConfigurer.introspectionClientCredentials(CLIENT_ID, CLIENT_SECRET);
|
||||
assertThat(opaqueTokenConfigurer.getIntrospector()).isInstanceOf(NimbusOpaqueTokenIntrospector.class);
|
||||
assertThat(opaqueTokenConfigurer.getIntrospector()).isNotSameAs(client);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1250,7 +1251,6 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
String jwtThree = jwtFromIssuer(issuerThree);
|
||||
mockWebServer(String.format(metadata, issuerOne, issuerOne));
|
||||
mockWebServer(jwkSet);
|
||||
mockWebServer(jwkSet);
|
||||
// @formatter:off
|
||||
this.mvc.perform(get("/authenticated").with(bearerToken(jwtOne)))
|
||||
.andExpect(status().isOk())
|
||||
@@ -1258,7 +1258,6 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
// @formatter:on
|
||||
mockWebServer(String.format(metadata, issuerTwo, issuerTwo));
|
||||
mockWebServer(jwkSet);
|
||||
mockWebServer(jwkSet);
|
||||
// @formatter:off
|
||||
this.mvc.perform(get("/authenticated").with(bearerToken(jwtTwo)))
|
||||
.andExpect(status().isOk())
|
||||
@@ -1266,7 +1265,6 @@ public class OAuth2ResourceServerConfigurerTests {
|
||||
// @formatter:on
|
||||
mockWebServer(String.format(metadata, issuerThree, issuerThree));
|
||||
mockWebServer(jwkSet);
|
||||
mockWebServer(jwkSet);
|
||||
// @formatter:off
|
||||
this.mvc.perform(get("/authenticated").with(bearerToken(jwtThree)))
|
||||
.andExpect(status().isUnauthorized())
|
||||
|
||||
+204
-3
@@ -37,6 +37,7 @@ import org.mockito.ArgumentCaptor;
|
||||
import org.opensaml.saml.saml2.core.Assertion;
|
||||
import org.opensaml.saml.saml2.core.AuthnRequest;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -49,6 +50,7 @@ import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
@@ -63,6 +65,7 @@ import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMap
|
||||
import org.springframework.security.saml2.core.Saml2ErrorCodes;
|
||||
import org.springframework.security.saml2.core.Saml2Utils;
|
||||
import org.springframework.security.saml2.core.TestSaml2X509Credentials;
|
||||
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
|
||||
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
|
||||
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationRequestFactory;
|
||||
import org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider;
|
||||
@@ -78,7 +81,10 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
|
||||
import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;
|
||||
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
|
||||
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestContextResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
|
||||
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.context.HttpRequestResponseHolder;
|
||||
@@ -92,6 +98,7 @@ import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
@@ -119,6 +126,8 @@ public class Saml2LoginConfigurerTests {
|
||||
|
||||
private static final String SIGNED_RESPONSE = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9ycC5leGFtcGxlLm9yZy9hY3MiIElEPSJfYzE3MzM2YTAtNTM1My00MTQ5LWI3MmMtMDNkOWY5YWYzMDdlIiBJc3N1ZUluc3RhbnQ9IjIwMjAtMDgtMDRUMjI6MDQ6NDUuMDE2WiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5hcC1lbnRpdHktaWQ8L3NhbWwyOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KPGRzOlNpZ25lZEluZm8+CjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CjxkczpSZWZlcmVuY2UgVVJJPSIjX2MxNzMzNmEwLTUzNTMtNDE0OS1iNzJjLTAzZDlmOWFmMzA3ZSI+CjxkczpUcmFuc2Zvcm1zPgo8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz4KPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgo8L2RzOlRyYW5zZm9ybXM+CjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz4KPGRzOkRpZ2VzdFZhbHVlPjYzTmlyenFzaDVVa0h1a3NuRWUrM0hWWU5aYWFsQW1OQXFMc1lGMlRuRDA9PC9kczpEaWdlc3RWYWx1ZT4KPC9kczpSZWZlcmVuY2U+CjwvZHM6U2lnbmVkSW5mbz4KPGRzOlNpZ25hdHVyZVZhbHVlPgpLMVlvWWJVUjBTclY4RTdVMkhxTTIvZUNTOTNoV25mOExnNnozeGZWMUlyalgzSXhWYkNvMVlYcnRBSGRwRVdvYTJKKzVOMmFNbFBHJiMxMzsKN2VpbDBZRC9xdUVRamRYbTNwQTBjZmEvY25pa2RuKzVhbnM0ZWQwanU1amo2dkpvZ2w2Smt4Q25LWUpwTU9HNzhtampmb0phengrWCYjMTM7CkM2NktQVStBYUdxeGVwUEQ1ZlhRdTFKSy9Jb3lBaitaa3k4Z2Jwc3VyZHFCSEJLRWxjdnVOWS92UGY0OGtBeFZBKzdtRGhNNUMvL1AmIzEzOwp0L084Y3NZYXB2UjZjdjZrdk45QXZ1N3FRdm9qVk1McHVxZWNJZDJwTUVYb0NSSnE2Nkd4MStNTUVPeHVpMWZZQlRoMEhhYjRmK3JyJiMxMzsKOEY2V1NFRC8xZllVeHliRkJqZ1Q4d2lEWHFBRU8wSVY4ZWRQeEE9PQo8L2RzOlNpZ25hdHVyZVZhbHVlPgo8L2RzOlNpZ25hdHVyZT48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iQWUzZjQ5OGI4LTliMTctNDA3OC05ZDM1LTg2YTA4NDA4NDk5NSIgSXNzdWVJbnN0YW50PSIyMDIwLTA4LTA0VDIyOjA0OjQ1LjA3N1oiIFZlcnNpb249IjIuMCI+PHNhbWwyOklzc3Vlcj5hcC1lbnRpdHktaWQ8L3NhbWwyOklzc3Vlcj48c2FtbDI6U3ViamVjdD48c2FtbDI6TmFtZUlEPnRlc3RAc2FtbC51c2VyPC9zYW1sMjpOYW1lSUQ+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90QmVmb3JlPSIyMDIwLTA4LTA0VDIxOjU5OjQ1LjA5MFoiIE5vdE9uT3JBZnRlcj0iMjA0MC0wNy0zMFQyMjowNTowNi4wODhaIiBSZWNpcGllbnQ9Imh0dHBzOi8vcnAuZXhhbXBsZS5vcmcvYWNzIi8+PC9zYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDI6U3ViamVjdD48c2FtbDI6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMjAtMDgtMDRUMjE6NTk6NDUuMDgwWiIgTm90T25PckFmdGVyPSIyMDQwLTA3LTMwVDIyOjA1OjA2LjA4N1oiLz48L3NhbWwyOkFzc2VydGlvbj48L3NhbWwycDpSZXNwb25zZT4=";
|
||||
|
||||
private static final AuthenticationConverter AUTHENTICATION_CONVERTER = mock(AuthenticationConverter.class);
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@@ -183,7 +192,8 @@ public class Saml2LoginConfigurerTests {
|
||||
this.spring.register(CustomAuthenticationRequestContextResolver.class).autowire();
|
||||
Saml2AuthenticationRequestContext context = TestSaml2AuthenticationRequestContexts
|
||||
.authenticationRequestContext().build();
|
||||
Saml2AuthenticationRequestContextResolver resolver = CustomAuthenticationRequestContextResolver.resolver;
|
||||
Saml2AuthenticationRequestContextResolver resolver = this.spring.getContext()
|
||||
.getBean(Saml2AuthenticationRequestContextResolver.class);
|
||||
given(resolver.resolve(any(HttpServletRequest.class))).willReturn(context);
|
||||
this.mvc.perform(get("/saml2/authenticate/registration-id")).andExpect(status().isFound());
|
||||
verify(resolver).resolve(any(HttpServletRequest.class));
|
||||
@@ -219,6 +229,26 @@ public class Saml2LoginConfigurerTests {
|
||||
verify(CustomAuthenticationConverter.authenticationConverter).convert(any(HttpServletRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenCustomAuthenticationConverterBeanThenUses() throws Exception {
|
||||
this.spring.register(CustomAuthenticationConverterBean.class).autowire();
|
||||
Saml2AuthenticationTokenConverter authenticationConverter = this.spring.getContext()
|
||||
.getBean(Saml2AuthenticationTokenConverter.class);
|
||||
RelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.noCredentials()
|
||||
.assertingPartyDetails((party) -> party.verificationX509Credentials(
|
||||
(c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())))
|
||||
.build();
|
||||
String response = new String(Saml2Utils.samlDecode(SIGNED_RESPONSE));
|
||||
given(authenticationConverter.convert(any(HttpServletRequest.class)))
|
||||
.willReturn(new Saml2AuthenticationToken(relyingPartyRegistration, response));
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder request = post("/login/saml2/sso/" + relyingPartyRegistration.getRegistrationId())
|
||||
.param("SAMLResponse", SIGNED_RESPONSE);
|
||||
// @formatter:on
|
||||
this.mvc.perform(request).andExpect(redirectedUrl("/"));
|
||||
verify(authenticationConverter).convert(any(HttpServletRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWithInvalidDeflatedSAMLResponseThenFailureHandlerUses() throws Exception {
|
||||
this.spring.register(CustomAuthenticationFailureHandler.class).autowire();
|
||||
@@ -237,6 +267,76 @@ public class Saml2LoginConfigurerTests {
|
||||
assertThat(exception.getCause()).isInstanceOf(IOException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticationRequestWhenCustomAuthnRequestRepositoryThenUses() throws Exception {
|
||||
this.spring.register(CustomAuthenticationRequestRepository.class).autowire();
|
||||
MockHttpServletRequestBuilder request = get("/saml2/authenticate/registration-id");
|
||||
this.mvc.perform(request).andExpect(status().isFound());
|
||||
Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> repository = this.spring.getContext()
|
||||
.getBean(Saml2AuthenticationRequestRepository.class);
|
||||
verify(repository).saveAuthenticationRequest(any(AbstractSaml2AuthenticationRequest.class),
|
||||
any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenCustomAuthnRequestRepositoryThenUses() throws Exception {
|
||||
this.spring.register(CustomAuthenticationRequestRepository.class).autowire();
|
||||
MockHttpServletRequestBuilder request = post("/login/saml2/sso/registration-id").param("SAMLResponse",
|
||||
SIGNED_RESPONSE);
|
||||
Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> repository = this.spring.getContext()
|
||||
.getBean(Saml2AuthenticationRequestRepository.class);
|
||||
this.mvc.perform(request);
|
||||
verify(repository).loadAuthenticationRequest(any(HttpServletRequest.class));
|
||||
verify(repository).removeAuthenticationRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void saml2LoginWhenLoginProcessingUrlWithoutRegistrationIdAndDefaultAuthenticationConverterThenValidates() {
|
||||
assertThatExceptionOfType(BeanCreationException.class)
|
||||
.isThrownBy(() -> this.spring.register(CustomLoginProcessingUrlDefaultAuthenticationConverter.class)
|
||||
.autowire())
|
||||
.havingRootCause().isInstanceOf(IllegalStateException.class)
|
||||
.withMessage("loginProcessingUrl must contain {registrationId} path variable");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenCustomLoginProcessingUrlAndCustomAuthenticationConverterThenAuthenticate()
|
||||
throws Exception {
|
||||
this.spring.register(CustomLoginProcessingUrlCustomAuthenticationConverter.class).autowire();
|
||||
RelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.noCredentials()
|
||||
.assertingPartyDetails((party) -> party.verificationX509Credentials(
|
||||
(c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())))
|
||||
.build();
|
||||
String response = new String(Saml2Utils.samlDecode(SIGNED_RESPONSE));
|
||||
given(AUTHENTICATION_CONVERTER.convert(any(HttpServletRequest.class)))
|
||||
.willReturn(new Saml2AuthenticationToken(relyingPartyRegistration, response));
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder request = post("/my/custom/url").param("SAMLResponse", SIGNED_RESPONSE);
|
||||
// @formatter:on
|
||||
this.mvc.perform(request).andExpect(redirectedUrl("/"));
|
||||
verify(AUTHENTICATION_CONVERTER).convert(any(HttpServletRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticateWhenCustomLoginProcessingUrlAndSaml2AuthenticationTokenConverterBeanThenAuthenticate()
|
||||
throws Exception {
|
||||
this.spring.register(CustomLoginProcessingUrlSaml2AuthenticationTokenConverterBean.class).autowire();
|
||||
Saml2AuthenticationTokenConverter authenticationConverter = this.spring.getContext()
|
||||
.getBean(Saml2AuthenticationTokenConverter.class);
|
||||
RelyingPartyRegistration relyingPartyRegistration = TestRelyingPartyRegistrations.noCredentials()
|
||||
.assertingPartyDetails((party) -> party.verificationX509Credentials(
|
||||
(c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential())))
|
||||
.build();
|
||||
String response = new String(Saml2Utils.samlDecode(SIGNED_RESPONSE));
|
||||
given(authenticationConverter.convert(any(HttpServletRequest.class)))
|
||||
.willReturn(new Saml2AuthenticationToken(relyingPartyRegistration, response));
|
||||
// @formatter:off
|
||||
MockHttpServletRequestBuilder request = post("/my/custom/url").param("SAMLResponse", SIGNED_RESPONSE);
|
||||
// @formatter:on
|
||||
this.mvc.perform(request).andExpect(redirectedUrl("/"));
|
||||
verify(authenticationConverter).convert(any(HttpServletRequest.class));
|
||||
}
|
||||
|
||||
private void validateSaml2WebSsoAuthenticationFilterConfiguration() {
|
||||
// get the OpenSamlAuthenticationProvider
|
||||
Saml2WebSsoAuthenticationFilter filter = getSaml2SsoFilter(this.springSecurityFilterChain);
|
||||
@@ -355,7 +455,7 @@ public class Saml2LoginConfigurerTests {
|
||||
@Import(Saml2LoginConfigBeans.class)
|
||||
static class CustomAuthenticationRequestContextResolver extends WebSecurityConfigurerAdapter {
|
||||
|
||||
private static final Saml2AuthenticationRequestContextResolver resolver = mock(
|
||||
private final Saml2AuthenticationRequestContextResolver resolver = mock(
|
||||
Saml2AuthenticationRequestContextResolver.class);
|
||||
|
||||
@Override
|
||||
@@ -371,7 +471,7 @@ public class Saml2LoginConfigurerTests {
|
||||
|
||||
@Bean
|
||||
Saml2AuthenticationRequestContextResolver resolver() {
|
||||
return resolver;
|
||||
return this.resolver;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -420,6 +520,107 @@ public class Saml2LoginConfigurerTests {
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(Saml2LoginConfigBeans.class)
|
||||
static class CustomAuthenticationConverterBean {
|
||||
|
||||
private final Saml2AuthenticationTokenConverter authenticationConverter = mock(
|
||||
Saml2AuthenticationTokenConverter.class);
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain app(HttpSecurity http) throws Exception {
|
||||
http.authorizeHttpRequests((authz) -> authz.anyRequest().authenticated())
|
||||
.saml2Login(Customizer.withDefaults());
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Saml2AuthenticationTokenConverter authenticationConverter() {
|
||||
return this.authenticationConverter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(Saml2LoginConfigBeans.class)
|
||||
static class CustomAuthenticationRequestRepository {
|
||||
|
||||
private final Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> repository = mock(
|
||||
Saml2AuthenticationRequestRepository.class);
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests((authz) -> authz.anyRequest().authenticated());
|
||||
http.saml2Login(withDefaults());
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(Saml2LoginConfigBeans.class)
|
||||
static class CustomLoginProcessingUrlDefaultAuthenticationConverter {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeRequests((authz) -> authz.anyRequest().authenticated())
|
||||
.saml2Login((saml2) -> saml2.loginProcessingUrl("/my/custom/url"));
|
||||
// @formatter:on
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(Saml2LoginConfigBeans.class)
|
||||
static class CustomLoginProcessingUrlCustomAuthenticationConverter {
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeRequests((authz) -> authz.anyRequest().authenticated())
|
||||
.saml2Login((saml2) -> saml2
|
||||
.loginProcessingUrl("/my/custom/url")
|
||||
.authenticationConverter(AUTHENTICATION_CONVERTER)
|
||||
);
|
||||
// @formatter:on
|
||||
return http.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
@Import(Saml2LoginConfigBeans.class)
|
||||
static class CustomLoginProcessingUrlSaml2AuthenticationTokenConverterBean {
|
||||
|
||||
private final Saml2AuthenticationTokenConverter authenticationConverter = mock(
|
||||
Saml2AuthenticationTokenConverter.class);
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeRequests((authz) -> authz.anyRequest().authenticated())
|
||||
.saml2Login((saml2) -> saml2.loginProcessingUrl("/my/custom/url"));
|
||||
// @formatter:on
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Saml2AuthenticationTokenConverter authenticationTokenConverter() {
|
||||
return this.authenticationConverter;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class Saml2LoginConfigBeans {
|
||||
|
||||
@Bean
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user