Compare commits
170 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4559d269e0 | |||
| 627809d2dc | |||
| 4b1d7e9479 | |||
| d9980a4dfe | |||
| 8eb7e589eb | |||
| 0d7dce9d71 | |||
| da09788be9 | |||
| ead587c597 | |||
| 02459919cc | |||
| 57d212ddca | |||
| 539b17f6da | |||
| 37e1ad27fe | |||
| 043fdd6f03 | |||
| 3234e05085 | |||
| f957e3c051 | |||
| d66ad22652 | |||
| 7a860e1568 | |||
| 0d3c3c676d | |||
| 0f64d4c091 | |||
| 56a6133b20 | |||
| aaf20e7b61 | |||
| 148756076c | |||
| 6f321a27c4 | |||
| 539443b4be | |||
| 28424f8ae5 | |||
| a7d21f1b34 | |||
| d3a024786b | |||
| fa4c5449e7 | |||
| 2a3845a7ed | |||
| ff96a7b5ad | |||
| c37ff42234 | |||
| 0a00d84838 | |||
| 96c6751a1d | |||
| 8ee9c32788 | |||
| 7a5fb9eaf7 | |||
| 8cbb972cef | |||
| 539a11d0a4 | |||
| e97c5a533b | |||
| e0fa644b08 | |||
| 592db9180d | |||
| aca3fc2412 | |||
| 07f9afe057 | |||
| d7077b441a | |||
| 101f11ba94 | |||
| 18b903f6e3 | |||
| 038266a94f | |||
| cf559ab224 | |||
| 649428b49a | |||
| 0355e960d7 | |||
| fdad14af63 | |||
| 077c9e0b3e | |||
| dc648cf79f | |||
| c2d2914a4f | |||
| a40f73521c | |||
| 70863952ae | |||
| af95be34c6 | |||
| 71478c2534 | |||
| b289d12aad | |||
| e2361c5c79 | |||
| 8b9c8ca96c | |||
| 44e672d969 | |||
| 330f0f050d | |||
| e45dcb3ab2 | |||
| d3a451fffb | |||
| 9d378103b0 | |||
| 2031f101dc | |||
| 2a2c2dd209 | |||
| fa0e06ebdc | |||
| 6724627b50 | |||
| 4aab7b06a9 | |||
| d0d4d58c48 | |||
| 29be60d035 | |||
| 5024103b07 | |||
| d0327807a7 | |||
| f880f1ba26 | |||
| 71dafc0c06 | |||
| 7d9357b871 | |||
| 0b02bbecd1 | |||
| 932a5cc8b4 | |||
| e4a321511a | |||
| 4ee5800ec9 | |||
| c67632225d | |||
| 6e45a376cd | |||
| 70b67cd2f1 | |||
| 304e89041c | |||
| f1a76efc2d | |||
| 963251314b | |||
| ee061f3659 | |||
| 440ffce2eb | |||
| 47871562ca | |||
| f0c548cee7 | |||
| 1a32b399e6 | |||
| e9f3da94fc | |||
| 4fb6f936a7 | |||
| c431b5479e | |||
| fc03950afd | |||
| 516b5fed4a | |||
| 165e29e585 | |||
| 0be3e86b21 | |||
| 96d229a6d1 | |||
| 5098cad9cf | |||
| 883c480af0 | |||
| 0f83e40427 | |||
| ad6ef6e4c7 | |||
| 94201e46f1 | |||
| 910e198cee | |||
| 45a88fc391 | |||
| 8f01efb9e3 | |||
| f53c65b3a0 | |||
| 0be772ff5b | |||
| ac990afa5d | |||
| 55cccbf727 | |||
| a095ea75a2 | |||
| a041e7c943 | |||
| 4f3072b3d9 | |||
| 13c467734a | |||
| c01b2b946b | |||
| 549f9dab64 | |||
| 58090c37ea | |||
| 38344e8de6 | |||
| 6f319f0b13 | |||
| 4e71b26ea4 | |||
| 04f3bbcefa | |||
| c67ee6f2a8 | |||
| 4b590ce31f | |||
| 5902b46e9b | |||
| 62449d6fa2 | |||
| aa06088077 | |||
| 2a60776c8a | |||
| f411ecada2 | |||
| 8eeacf4a36 | |||
| 20c252982e | |||
| 6c5ac0d8ec | |||
| e1cb375fbf | |||
| e7e3f06044 | |||
| 994e93741b | |||
| 04e1a11e35 | |||
| 547056d5cc | |||
| 1fa35fb9a8 | |||
| a49914e631 | |||
| 783a43ea35 | |||
| 07a176d11e | |||
| c664fbc1a3 | |||
| af412c4d65 | |||
| e38bf6e554 | |||
| 624e0da669 | |||
| b28aa6c647 | |||
| 9e83b4be25 | |||
| e9854c91ef | |||
| 0345e29afb | |||
| 8bd5795f8e | |||
| 4fbc98dcd2 | |||
| 44cdbd6cf3 | |||
| a9af8c4128 | |||
| 28c81af788 | |||
| 3a956daf0c | |||
| 9c245865d7 | |||
| 86ed937a47 | |||
| 32ec8c3ae4 | |||
| c7ffd2513a | |||
| b3e0f167ff | |||
| 8c063f8ccb | |||
| f1ca42e501 | |||
| 719149f01d | |||
| ccd30607a9 | |||
| aa1ef46d84 | |||
| 16a21264d0 | |||
| 1c0f092176 | |||
| 32d79f3f4e | |||
| 088a24cf59 |
@@ -5,10 +5,14 @@ on:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch: # Manual trigger
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update:
|
||||
name: Update Algolia Index
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -10,12 +10,21 @@ on:
|
||||
env:
|
||||
GH_ACTIONS_REPO_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- name: Generate antora.yml
|
||||
run: ./gradlew :spring-security-docs:generateAntora
|
||||
- name: Extract Branch Name
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
name: Backport Bot
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
pull_request:
|
||||
types: [labeled]
|
||||
push:
|
||||
branches:
|
||||
- '*.x'
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '17'
|
||||
- name: Download BackportBot
|
||||
run: wget https://github.com/spring-io/backport-bot/releases/download/latest/backport-bot-0.0.1-SNAPSHOT.jar
|
||||
- name: Backport
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_EVENT: ${{ toJSON(github.event) }}
|
||||
run: java -jar backport-bot-0.0.1-SNAPSHOT.jar --github.accessToken="$GITHUB_TOKEN" --github.event_name "$GITHUB_EVENT_NAME" --github.event "$GITHUB_EVENT"
|
||||
@@ -3,9 +3,15 @@ on:
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
permissions:
|
||||
contents: none
|
||||
steps:
|
||||
- name: Delete artifacts in cron job
|
||||
env:
|
||||
|
||||
@@ -2,6 +2,8 @@ name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch: # Manual trigger
|
||||
@@ -16,19 +18,28 @@ env:
|
||||
STRUCTURE101_LICENSEID: ${{ secrets.STRUCTURE101_LICENSEID }}
|
||||
ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
RUN_JOBS: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
prerequisites:
|
||||
name: Pre-requisites for building
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
outputs:
|
||||
runjobs: ${{ steps.continue.outputs.runjobs }}
|
||||
project_version: ${{ steps.continue.outputs.project_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- id: continue
|
||||
name: Determine if should continue
|
||||
if: env.RUN_JOBS == 'true'
|
||||
run: echo "::set-output name=runjobs::true"
|
||||
run: |
|
||||
# Run jobs if in upstream repository
|
||||
echo "::set-output name=runjobs::true"
|
||||
# Extract version from gradle.properties
|
||||
version=$(cat gradle.properties | grep "version=" | awk -F'=' '{print $2}')
|
||||
echo "::set-output name=project_version::$version"
|
||||
build_jdk_11:
|
||||
name: Build JDK 11
|
||||
needs: [prerequisites]
|
||||
@@ -40,18 +51,14 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
- name: Cache Gradle packages
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.gradle/caches
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
|
||||
distribution: 'adopt'
|
||||
- name: Set up Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
- name: Set up gradle user name
|
||||
run: echo 'systemProp.user.name=spring-builds+github' >> gradle.properties
|
||||
- name: Build with Gradle
|
||||
env:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
|
||||
@@ -65,14 +72,11 @@ jobs:
|
||||
if: needs.prerequisites.outputs.runjobs
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
distribution: 'adopt'
|
||||
- name: Snapshot Tests
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -86,14 +90,11 @@ jobs:
|
||||
if: needs.prerequisites.outputs.runjobs
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
distribution: 'adopt'
|
||||
- name: Check samples project
|
||||
env:
|
||||
LOCAL_REPOSITORY_PATH: ${{ github.workspace }}/build/publications/repos
|
||||
@@ -111,14 +112,11 @@ jobs:
|
||||
if: needs.prerequisites.outputs.runjobs
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
distribution: 'adopt'
|
||||
- name: Check for package tangles
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -131,14 +129,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
distribution: 'adopt'
|
||||
- name: Deploy artifacts
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -158,14 +153,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
distribution: 'adopt'
|
||||
- name: Deploy Docs
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -182,14 +174,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Setup gradle user name
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo 'systemProp.user.name=spring-builds+github' >> ~/.gradle/gradle.properties
|
||||
distribution: 'adopt'
|
||||
- name: Deploy Schema
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
@@ -200,14 +189,122 @@ jobs:
|
||||
DOCS_USERNAME: ${{ secrets.DOCS_USERNAME }}
|
||||
DOCS_SSH_KEY: ${{ secrets.DOCS_SSH_KEY }}
|
||||
DOCS_HOST: ${{ secrets.DOCS_HOST }}
|
||||
perform_release:
|
||||
name: Perform release
|
||||
needs: [prerequisites, deploy_artifacts, deploy_docs, deploy_schema]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
timeout-minutes: 90
|
||||
if: ${{ !endsWith(needs.prerequisites.outputs.project_version, '-SNAPSHOT') }}
|
||||
env:
|
||||
REPO: ${{ github.repository }}
|
||||
BRANCH: ${{ github.ref_name }}
|
||||
TOKEN: ${{ github.token }}
|
||||
VERSION: ${{ needs.prerequisites.outputs.project_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- name: Wait for Artifactory Artifacts
|
||||
if: ${{ contains(needs.prerequisites.outputs.project_version, '-RC') || contains(needs.prerequisites.outputs.project_version, '-M') }}
|
||||
run: |
|
||||
echo "Wait for artifacts of $REPO@$VERSION to appear on Artifactory."
|
||||
until curl -f -s https://repo.spring.io/artifactory/milestone/org/springframework/security/spring-security-core/$VERSION/ > /dev/null
|
||||
do
|
||||
sleep 30
|
||||
echo "."
|
||||
done
|
||||
echo "Artifacts for $REPO@$VERSION have been released to Artifactory."
|
||||
- name: Wait for Maven Central Artifacts
|
||||
if: ${{ !contains(needs.prerequisites.outputs.project_version, '-RC') && !contains(needs.prerequisites.outputs.project_version, '-M') }}
|
||||
run: |
|
||||
echo "Wait for artifacts of $REPO@$VERSION to appear on Maven Central."
|
||||
until curl -f -s https://repo1.maven.org/maven2/org/springframework/security/spring-security-core/$VERSION/ > /dev/null
|
||||
do
|
||||
sleep 30
|
||||
echo "."
|
||||
done
|
||||
echo "Artifacts for $REPO@$VERSION have been released to Maven Central."
|
||||
- name: Create GitHub Release
|
||||
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"
|
||||
echo "Tagging and publishing $REPO@$VERSION release on GitHub."
|
||||
./gradlew createGitHubRelease -PnextVersion=$VERSION -Pbranch=$BRANCH -PcreateRelease=true -PgitHubAccessToken=$TOKEN
|
||||
- name: Announce Release on Slack
|
||||
id: spring-security-announcing
|
||||
uses: slackapi/slack-github-action@v1.19.0
|
||||
with:
|
||||
payload: |
|
||||
{
|
||||
"text": "spring-security-announcing `${{ env.VERSION }}` is available now",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "spring-security-announcing `${{ env.VERSION }}` is available now"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SPRING_RELEASE_SLACK_WEBHOOK_URL }}
|
||||
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
|
||||
- name: Setup git config
|
||||
run: |
|
||||
git config user.name 'github-actions[bot]'
|
||||
git config user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
- name: Update to next Snapshot Version
|
||||
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"
|
||||
echo "Updating $REPO@$VERSION to next snapshot version."
|
||||
./gradlew :updateToSnapshotVersion
|
||||
./gradlew :spring-security-docs:antoraUpdateVersion
|
||||
git commit -am "Next development version"
|
||||
git push
|
||||
perform_post_release:
|
||||
name: Perform post-release
|
||||
needs: [prerequisites, deploy_artifacts, deploy_docs, deploy_schema]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
timeout-minutes: 90
|
||||
if: ${{ endsWith(needs.prerequisites.outputs.project_version, '-SNAPSHOT') }}
|
||||
env:
|
||||
TOKEN: ${{ github.token }}
|
||||
VERSION: ${{ needs.prerequisites.outputs.project_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- name: Schedule next release (if not already scheduled)
|
||||
run: ./gradlew scheduleNextRelease -PnextVersion=$VERSION -PgitHubAccessToken=$TOKEN
|
||||
notify_result:
|
||||
name: Check for failures
|
||||
needs: [build_jdk_11, snapshot_tests, check_samples, check_tangles, deploy_artifacts, deploy_docs, deploy_schema]
|
||||
needs: [perform_release, perform_post_release]
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
steps:
|
||||
- name: Send Slack message
|
||||
uses: Gamesight/slack-workflow-status@v1.0.1
|
||||
# Workaround while waiting for Gamesight/slack-workflow-status#38 to be fixed
|
||||
# See https://github.com/Gamesight/slack-workflow-status/issues/38
|
||||
uses: sjohnr/slack-workflow-status@v1-beta
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
|
||||
@@ -7,27 +7,28 @@ on:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch: # Manual trigger
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: deploy
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
cache: gradle
|
||||
- name: Validate Gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@e6e38bacfdf1a337459f332974bb2327a31aaf4b
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew :spring-security-docs:antora --stacktrace
|
||||
- name: Cleanup Gradle Cache
|
||||
# Remove some files from the Gradle cache, so they aren't cached by GitHub Actions.
|
||||
# Restoring these files from a GitHub Actions cache might cause problems for future builds.
|
||||
run: |
|
||||
rm -f ~/.gradle/caches/modules-2/modules-2.lock
|
||||
rm -f ~/.gradle/caches/modules-2/gc.properties
|
||||
rm -f /home/runner/.gradle/caches/modules-2/modules-2.lock
|
||||
rm -f /home/runner/.gradle/caches/modules-2/gc.properties
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew :spring-security-docs:antora --stacktrace
|
||||
- name: Deploy
|
||||
run: ${GITHUB_WORKSPACE}/.github/actions/algolia-deploy.sh "${{ secrets.DOCS_USERNAME }}@${{ secrets.DOCS_HOST }}" "/opt/www/domains/spring.io/docs/htdocs/spring-security/reference/" "${{ secrets.DOCS_SSH_KEY }}" "${{ secrets.DOCS_SSH_HOST_KEY }}"
|
||||
|
||||
@@ -5,10 +5,15 @@ on:
|
||||
env:
|
||||
DUE_ON: ${{ github.event.milestone.due_on }}
|
||||
TITLE: ${{ github.event.milestone.title }}
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
spring-releasetrain-checks:
|
||||
name: Check DueOn is on a Release Date
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
permissions:
|
||||
contents: none
|
||||
steps:
|
||||
- name: Print Milestone Being Checked
|
||||
run: echo "Validating DueOn '$DUE_ON' for milestone '$TITLE'"
|
||||
@@ -23,6 +28,8 @@ jobs:
|
||||
needs: [spring-releasetrain-checks]
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
steps:
|
||||
- name: Send Slack message
|
||||
uses: Gamesight/slack-workflow-status@v1.0.1
|
||||
|
||||
@@ -2,27 +2,20 @@ name: PR Build
|
||||
|
||||
on: pull_request
|
||||
|
||||
env:
|
||||
RUN_JOBS: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
steps:
|
||||
- if: env.RUN_JOBS == 'true'
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up JDK
|
||||
if: env.RUN_JOBS == 'true'
|
||||
uses: actions/setup-java@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
- name: Cache Gradle packages
|
||||
if: env.RUN_JOBS == 'true'
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.gradle/caches
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}
|
||||
distribution: 'adopt'
|
||||
- name: Build with Gradle
|
||||
if: env.RUN_JOBS == 'true'
|
||||
run: ./gradlew clean build --continue --scan
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
name: Update Scheduled Release Version
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Manual trigger only. Triggered by release-scheduler.yml on main.
|
||||
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
GRADLE_ENTERPRISE_CACHE_USER: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
|
||||
GRADLE_ENTERPRISE_SECRET_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update_scheduled_release_version:
|
||||
name: Initiate Release If Scheduled
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
steps:
|
||||
- id: checkout-source
|
||||
name: Checkout Source Code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- id: check-release-due
|
||||
name: Check Release Due
|
||||
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 gitHubCheckNextVersionDueToday
|
||||
echo "::set-output name=is_due_today::$(cat build/github/milestones/is-due-today)"
|
||||
- id: check-open-issues
|
||||
name: Check for open issues
|
||||
if: steps.check-release-due.outputs.is_due_today == 'true'
|
||||
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 gitHubCheckMilestoneHasNoOpenIssues
|
||||
echo "::set-output name=is_open_issues::$(cat build/github/milestones/is-open-issues)"
|
||||
- id: validate-release-state
|
||||
name: Validate State of Release
|
||||
if: steps.check-release-due.outputs.is_due_today == 'true' && steps.check-open-issues.outputs.is_open_issues == 'true'
|
||||
run: |
|
||||
echo "The release is due today but there are open issues"
|
||||
exit 1
|
||||
- id: update-version-and-push
|
||||
name: Update version and push
|
||||
if: steps.check-release-due.outputs.is_due_today == 'true' && steps.check-open-issues.outputs.is_open_issues == 'false'
|
||||
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"
|
||||
git config user.name 'github-actions[bot]'
|
||||
git config user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
./gradlew :updateProjectVersion
|
||||
./gradlew :spring-security-docs:antoraUpdateVersion
|
||||
updatedVersion=$(cat gradle.properties | grep "version=" | awk -F'=' '{print $2}')
|
||||
git commit -am "Release $updatedVersion"
|
||||
git tag $updatedVersion
|
||||
git push
|
||||
git push origin $updatedVersion
|
||||
- id: send-slack-notification
|
||||
name: Send Slack message
|
||||
if: failure()
|
||||
uses: Gamesight/slack-workflow-status@v1.0.1
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
channel: '#spring-security-ci'
|
||||
name: 'CI Notifier'
|
||||
+1
-1
@@ -135,7 +135,7 @@ git push origin 5.4.0-RC1
|
||||
|
||||
The following command will update https://spring.io/projects/spring-security#learn with the new release version using the following parameters
|
||||
|
||||
<github-personal-access-token> - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `public_repo`
|
||||
<github-personal-access-token> - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `read:org` as https://spring.io/restdocs/index.html#authentication[documented for spring.io api]
|
||||
<next-version> - Replace with the milestone you are releasing now (i.e. 5.5.0-RC1)
|
||||
<previous-version> - Replace with the previous release which will be removed from the listed versions (i.e. 5.5.0-M3)
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ import org.springframework.security.acls.domain.AuditLogger;
|
||||
import org.springframework.security.acls.domain.DefaultPermissionFactory;
|
||||
import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;
|
||||
import org.springframework.security.acls.domain.GrantedAuthoritySid;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityImpl;
|
||||
import org.springframework.security.acls.domain.PermissionFactory;
|
||||
import org.springframework.security.acls.domain.PrincipalSid;
|
||||
import org.springframework.security.acls.model.AccessControlEntry;
|
||||
@@ -51,7 +51,6 @@ import org.springframework.security.acls.model.AclCache;
|
||||
import org.springframework.security.acls.model.MutableAcl;
|
||||
import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.ObjectIdentityGenerator;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.PermissionGrantingStrategy;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
@@ -74,8 +73,8 @@ import org.springframework.util.Assert;
|
||||
* one in <tt>lookupObjectIdentities</tt>. These are built from the same select and "order
|
||||
* by" clause, using a different where clause in each case. In order to use custom schema
|
||||
* or column names, each of these SQL clauses can be customized, but they must be
|
||||
* consistent with each other and with the expected result set generated by the the
|
||||
* default values.
|
||||
* consistent with each other and with the expected result set generated by the default
|
||||
* values.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
@@ -110,8 +109,6 @@ public class BasicLookupStrategy implements LookupStrategy {
|
||||
|
||||
private final AclAuthorizationStrategy aclAuthorizationStrategy;
|
||||
|
||||
private ObjectIdentityGenerator objectIdentityGenerator;
|
||||
|
||||
private PermissionFactory permissionFactory = new DefaultPermissionFactory();
|
||||
|
||||
private final AclCache aclCache;
|
||||
@@ -165,7 +162,6 @@ public class BasicLookupStrategy implements LookupStrategy {
|
||||
this.aclCache = aclCache;
|
||||
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
|
||||
this.grantingStrategy = grantingStrategy;
|
||||
this.objectIdentityGenerator = new ObjectIdentityRetrievalStrategyImpl();
|
||||
this.aclClassIdUtils = new AclClassIdUtils();
|
||||
this.fieldAces.setAccessible(true);
|
||||
this.fieldAcl.setAccessible(true);
|
||||
@@ -492,11 +488,6 @@ public class BasicLookupStrategy implements LookupStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
public final void setObjectIdentityGenerator(ObjectIdentityGenerator objectIdentityGenerator) {
|
||||
Assert.notNull(objectIdentityGenerator, "objectIdentityGenerator cannot be null");
|
||||
this.objectIdentityGenerator = objectIdentityGenerator;
|
||||
}
|
||||
|
||||
public final void setConversionService(ConversionService conversionService) {
|
||||
this.aclClassIdUtils = new AclClassIdUtils(conversionService);
|
||||
}
|
||||
@@ -578,8 +569,7 @@ public class BasicLookupStrategy implements LookupStrategy {
|
||||
// target id type, e.g. UUID.
|
||||
Serializable identifier = (Serializable) rs.getObject("object_id_identity");
|
||||
identifier = BasicLookupStrategy.this.aclClassIdUtils.identifierFrom(identifier, rs);
|
||||
ObjectIdentity objectIdentity = BasicLookupStrategy.this.objectIdentityGenerator
|
||||
.createObjectIdentity(identifier, rs.getString("class"));
|
||||
ObjectIdentity objectIdentity = new ObjectIdentityImpl(rs.getString("class"), identifier);
|
||||
|
||||
Acl parentAcl = null;
|
||||
long parentAclId = rs.getLong("parent_object");
|
||||
|
||||
@@ -31,12 +31,11 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityImpl;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.AclService;
|
||||
import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.ObjectIdentityGenerator;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -82,8 +81,6 @@ public class JdbcAclService implements AclService {
|
||||
|
||||
private AclClassIdUtils aclClassIdUtils;
|
||||
|
||||
private ObjectIdentityGenerator objectIdentityGenerator;
|
||||
|
||||
public JdbcAclService(DataSource dataSource, LookupStrategy lookupStrategy) {
|
||||
this(new JdbcTemplate(dataSource), lookupStrategy);
|
||||
}
|
||||
@@ -94,7 +91,6 @@ public class JdbcAclService implements AclService {
|
||||
this.jdbcOperations = jdbcOperations;
|
||||
this.lookupStrategy = lookupStrategy;
|
||||
this.aclClassIdUtils = new AclClassIdUtils();
|
||||
this.objectIdentityGenerator = new ObjectIdentityRetrievalStrategyImpl();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,7 +105,7 @@ public class JdbcAclService implements AclService {
|
||||
String javaType = rs.getString("class");
|
||||
Serializable identifier = (Serializable) rs.getObject("obj_id");
|
||||
identifier = this.aclClassIdUtils.identifierFrom(identifier, rs);
|
||||
return this.objectIdentityGenerator.createObjectIdentity(identifier, javaType);
|
||||
return new ObjectIdentityImpl(javaType, identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -169,11 +165,6 @@ public class JdbcAclService implements AclService {
|
||||
this.aclClassIdUtils = new AclClassIdUtils(conversionService);
|
||||
}
|
||||
|
||||
public void setObjectIdentityGenerator(ObjectIdentityGenerator objectIdentityGenerator) {
|
||||
Assert.notNull(objectIdentityGenerator, "objectIdentityGenerator cannot be null");
|
||||
this.objectIdentityGenerator = objectIdentityGenerator;
|
||||
}
|
||||
|
||||
protected boolean isAclClassIdSupported() {
|
||||
return this.aclClassIdSupported;
|
||||
}
|
||||
|
||||
-9
@@ -318,13 +318,4 @@ public abstract class AbstractBasicLookupStrategyTests {
|
||||
assertThat(((GrantedAuthoritySid) result).getGrantedAuthority()).isEqualTo("sid");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setObjectIdentityGeneratorWhenNullThenThrowsIllegalArgumentException() {
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.strategy.setObjectIdentityGenerator(null))
|
||||
.withMessage("objectIdentityGenerator cannot be null");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -45,7 +45,6 @@ import org.springframework.security.acls.model.Sid;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyList;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
@@ -171,26 +170,6 @@ public class JdbcAclServiceTests {
|
||||
.isEqualTo(UUID.fromString("25d93b3f-c3aa-4814-9d5e-c7c96ced7762"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setObjectIdentityGeneratorWhenNullThenThrowsIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.aclServiceIntegration.setObjectIdentityGenerator(null))
|
||||
.withMessage("objectIdentityGenerator cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findChildrenWhenObjectIdentityGeneratorSetThenUsed() {
|
||||
this.aclServiceIntegration
|
||||
.setObjectIdentityGenerator((id, type) -> new ObjectIdentityImpl(type, "prefix:" + id));
|
||||
|
||||
ObjectIdentity objectIdentity = new ObjectIdentityImpl("location", "US");
|
||||
this.aclServiceIntegration.setAclClassIdSupported(true);
|
||||
List<ObjectIdentity> objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity);
|
||||
assertThat(objectIdentities.size()).isEqualTo(1);
|
||||
assertThat(objectIdentities.get(0).getType()).isEqualTo("location");
|
||||
assertThat(objectIdentities.get(0).getIdentifier()).isEqualTo("prefix:US-PAL");
|
||||
}
|
||||
|
||||
class MockLongIdDomainObject {
|
||||
|
||||
private Object id;
|
||||
|
||||
@@ -27,3 +27,7 @@ sourceSets.test.aspectj.srcDir "src/test/java"
|
||||
sourceSets.test.java.srcDirs = files()
|
||||
|
||||
compileAspectj.ajcOptions.outxmlfile = "META-INF/aop.xml"
|
||||
|
||||
aspectj {
|
||||
version = aspectjVersion
|
||||
}
|
||||
|
||||
+49
-2
@@ -2,12 +2,13 @@ buildscript {
|
||||
dependencies {
|
||||
classpath "io.spring.javaformat:spring-javaformat-gradle-plugin:$springJavaformatVersion"
|
||||
classpath 'io.spring.nohttp:nohttp-gradle:0.0.10'
|
||||
classpath "io.freefair.gradle:aspectj-plugin:6.4.3"
|
||||
classpath "io.freefair.gradle:aspectj-plugin:6.2.0"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
classpath "com.netflix.nebula:nebula-project-plugin:8.2.0"
|
||||
}
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
maven { url 'https://repo.spring.io/plugins-snapshot' }
|
||||
maven { url 'https://plugins.gradle.org/m2/' }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +19,7 @@ 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.update-version'
|
||||
apply plugin: 'org.springframework.security.sagan'
|
||||
apply plugin: 'org.springframework.github.milestone'
|
||||
apply plugin: 'org.springframework.github.changelog'
|
||||
@@ -46,6 +48,29 @@ tasks.named("gitHubCheckMilestoneHasNoOpenIssues") {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("gitHubNextReleaseMilestone") {
|
||||
repository {
|
||||
owner = "spring-projects"
|
||||
name = "spring-security"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("gitHubCheckNextVersionDueToday") {
|
||||
repository {
|
||||
owner = "spring-projects"
|
||||
name = "spring-security"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("scheduleNextRelease") {
|
||||
repository {
|
||||
owner = "spring-projects"
|
||||
name = "spring-security"
|
||||
}
|
||||
weekOfMonth = 3
|
||||
dayOfWeek = 1
|
||||
}
|
||||
|
||||
tasks.named("createGitHubRelease") {
|
||||
repository {
|
||||
owner = "spring-projects"
|
||||
@@ -53,6 +78,13 @@ tasks.named("createGitHubRelease") {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("dispatchGitHubWorkflow") {
|
||||
repository {
|
||||
owner = "spring-projects"
|
||||
name = "spring-security"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("updateDependencies") {
|
||||
// we aren't Gradle 7 compatible yet
|
||||
checkForGradleUpdate = false
|
||||
@@ -70,7 +102,10 @@ updateDependenciesSettings {
|
||||
})
|
||||
dependencyExcludes {
|
||||
majorVersionBump()
|
||||
minorVersionBump()
|
||||
alphaBetaVersions()
|
||||
releaseCandidatesVersions()
|
||||
milestoneVersions()
|
||||
snapshotVersions()
|
||||
addRule { components ->
|
||||
components.withModule("commons-codec:commons-codec") { selection ->
|
||||
@@ -99,6 +134,18 @@ updateDependenciesSettings {
|
||||
selection.reject("org.opensaml maintains two different versions, so it must be updated manually");
|
||||
}
|
||||
}
|
||||
components.withModule("io.spring.javaformat:spring-javaformat-gradle-plugin") { selection ->
|
||||
ModuleComponentIdentifier candidate = selection.getCandidate();
|
||||
if (!candidate.getVersion().equals(selection.getCurrentVersion())) {
|
||||
selection.reject("spring-javaformat-gradle-plugin updates break checkstyle");
|
||||
}
|
||||
}
|
||||
components.withModule("io.spring.javaformat:spring-javaformat-checkstyle") { selection ->
|
||||
ModuleComponentIdentifier candidate = selection.getCandidate();
|
||||
if (!candidate.getVersion().equals(selection.getCurrentVersion())) {
|
||||
selection.reject("spring-javaformat-checkstyle updates break checkstyle");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ plugins {
|
||||
id 'com.apollographql.apollo' version '2.4.5'
|
||||
}
|
||||
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
repositories {
|
||||
@@ -29,7 +28,7 @@ gradlePlugin {
|
||||
plugins {
|
||||
checkAntoraVersion {
|
||||
id = "org.springframework.antora.check-version"
|
||||
implementationClass = "org.springframework.gradle.antora.CheckAntoraVersionPlugin"
|
||||
implementationClass = "org.springframework.gradle.antora.AntoraVersionPlugin"
|
||||
}
|
||||
trang {
|
||||
id = "trang"
|
||||
@@ -47,6 +46,10 @@ gradlePlugin {
|
||||
id = "org.springframework.security.update-dependencies"
|
||||
implementationClass = "org.springframework.security.convention.versions.UpdateDependenciesPlugin"
|
||||
}
|
||||
updateProjectVersion {
|
||||
id = "org.springframework.security.update-version"
|
||||
implementationClass = "org.springframework.security.convention.versions.UpdateProjectVersionPlugin"
|
||||
}
|
||||
sagan {
|
||||
id = "org.springframework.security.sagan"
|
||||
implementationClass = "org.springframework.gradle.sagan.SaganPlugin"
|
||||
@@ -84,7 +87,7 @@ dependencies {
|
||||
implementation localGroovy()
|
||||
|
||||
implementation 'io.github.gradle-nexus:publish-plugin:1.1.0'
|
||||
implementation 'io.projectreactor:reactor-core:3.5.0-M1'
|
||||
implementation 'io.projectreactor:reactor-core:3.4.22'
|
||||
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'
|
||||
@@ -93,7 +96,7 @@ dependencies {
|
||||
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.24.20'
|
||||
implementation 'org.jfrog.buildinfo:build-info-extractor-gradle:4.29.0'
|
||||
implementation 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1'
|
||||
|
||||
testImplementation platform('org.junit:junit-bom:5.8.2')
|
||||
|
||||
@@ -17,6 +17,7 @@ package io.spring.gradle.convention
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
|
||||
|
||||
class ArtifactoryPlugin implements Plugin<Project> {
|
||||
|
||||
@@ -36,8 +37,14 @@ class ArtifactoryPlugin implements Plugin<Project> {
|
||||
password = artifactoryPassword
|
||||
}
|
||||
}
|
||||
defaults {
|
||||
publications('mavenJava')
|
||||
}
|
||||
}
|
||||
project.plugins.withType(MavenPublishPlugin) {
|
||||
project.artifactory {
|
||||
publish {
|
||||
defaults {
|
||||
publications('mavenJava')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,22 +59,14 @@ public class IntegrationTestPlugin implements Plugin<Project> {
|
||||
integrationTestRuntime {
|
||||
extendsFrom integrationTestCompile, testRuntime, testRuntimeOnly
|
||||
}
|
||||
integrationTestCompileClasspath {
|
||||
extendsFrom integrationTestCompile
|
||||
canBeResolved = true
|
||||
}
|
||||
integrationTestRuntimeClasspath {
|
||||
extendsFrom integrationTestRuntime
|
||||
canBeResolved = true
|
||||
}
|
||||
}
|
||||
|
||||
project.sourceSets {
|
||||
integrationTest {
|
||||
java.srcDir project.file('src/integration-test/java')
|
||||
resources.srcDir project.file('src/integration-test/resources')
|
||||
compileClasspath = project.sourceSets.main.output + project.sourceSets.test.output + project.configurations.integrationTestCompileClasspath
|
||||
runtimeClasspath = output + compileClasspath + project.configurations.integrationTestRuntimeClasspath
|
||||
compileClasspath = project.sourceSets.main.output + project.sourceSets.test.output + project.configurations.integrationTestCompile
|
||||
runtimeClasspath = output + compileClasspath + project.configurations.integrationTestRuntime
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +85,7 @@ public class IntegrationTestPlugin implements Plugin<Project> {
|
||||
project.idea {
|
||||
module {
|
||||
testSourceDirs += project.file('src/integration-test/java')
|
||||
scopes.TEST.plus += [ project.configurations.integrationTestCompileClasspath ]
|
||||
scopes.TEST.plus += [ project.configurations.integrationTestCompile ]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,7 +115,7 @@ public class IntegrationTestPlugin implements Plugin<Project> {
|
||||
|
||||
project.plugins.withType(EclipsePlugin) {
|
||||
project.eclipse.classpath {
|
||||
plusConfigurations += [ project.configurations.integrationTestCompileClasspath ]
|
||||
plusConfigurations += [ project.configurations.integrationTestCompile ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ class RootProjectPlugin implements Plugin<Project> {
|
||||
pluginManager.apply(NoHttpPlugin)
|
||||
pluginManager.apply(SpringNexusPublishPlugin)
|
||||
pluginManager.apply(CheckProhibitedDependenciesLifecyclePlugin)
|
||||
pluginManager.apply(ArtifactoryPlugin)
|
||||
pluginManager.apply("org.sonarqube")
|
||||
|
||||
project.repositories.mavenCentral()
|
||||
|
||||
+12
-26
@@ -8,7 +8,7 @@ import org.gradle.api.Task;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin;
|
||||
|
||||
public class CheckAntoraVersionPlugin implements Plugin<Project> {
|
||||
public class AntoraVersionPlugin implements Plugin<Project> {
|
||||
public static final String ANTORA_CHECK_VERSION_TASK_NAME = "antoraCheckVersion";
|
||||
|
||||
@Override
|
||||
@@ -35,32 +35,29 @@ public class CheckAntoraVersionPlugin implements Plugin<Project> {
|
||||
});
|
||||
}
|
||||
});
|
||||
project.getTasks().register("antoraUpdateVersion", UpdateAntoraVersionTask.class, new Action<UpdateAntoraVersionTask>() {
|
||||
@Override
|
||||
public void execute(UpdateAntoraVersionTask antoraUpdateVersion) {
|
||||
antoraUpdateVersion.setGroup("Release");
|
||||
antoraUpdateVersion.setDescription("Updates the antora.yml version properties to match the Gradle version");
|
||||
antoraUpdateVersion.getAntoraYmlFile().fileProvider(project.provider(() -> project.file("antora.yml")));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static String getDefaultAntoraVersion(Project project) {
|
||||
String projectVersion = getProjectVersion(project);
|
||||
int preReleaseIndex = getSnapshotIndex(projectVersion);
|
||||
return isSnapshot(projectVersion) ? projectVersion.substring(0, preReleaseIndex) : projectVersion;
|
||||
return AntoraVersionUtils.getDefaultAntoraVersion(projectVersion);
|
||||
}
|
||||
|
||||
private static String getDefaultAntoraPrerelease(Project project) {
|
||||
String projectVersion = getProjectVersion(project);
|
||||
if (isSnapshot(projectVersion)) {
|
||||
int preReleaseIndex = getSnapshotIndex(projectVersion);
|
||||
return projectVersion.substring(preReleaseIndex);
|
||||
}
|
||||
if (isPreRelease(projectVersion)) {
|
||||
return Boolean.TRUE.toString();
|
||||
}
|
||||
return null;
|
||||
return AntoraVersionUtils.getDefaultAntoraPrerelease(projectVersion);
|
||||
}
|
||||
|
||||
private static String getDefaultAntoraDisplayVersion(Project project) {
|
||||
String projectVersion = getProjectVersion(project);
|
||||
if (!isSnapshot(projectVersion) && isPreRelease(projectVersion)) {
|
||||
return getDefaultAntoraVersion(project);
|
||||
}
|
||||
return null;
|
||||
return AntoraVersionUtils.getDefaultAntoraDisplayVersion(projectVersion);
|
||||
}
|
||||
|
||||
private static String getProjectVersion(Project project) {
|
||||
@@ -71,15 +68,4 @@ public class CheckAntoraVersionPlugin implements Plugin<Project> {
|
||||
return String.valueOf(projectVersion);
|
||||
}
|
||||
|
||||
private static boolean isSnapshot(String projectVersion) {
|
||||
return getSnapshotIndex(projectVersion) >= 0;
|
||||
}
|
||||
|
||||
private static int getSnapshotIndex(String projectVersion) {
|
||||
return projectVersion.lastIndexOf("-SNAPSHOT");
|
||||
}
|
||||
|
||||
private static boolean isPreRelease(String projectVersion) {
|
||||
return projectVersion.lastIndexOf("-") >= 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.antora;
|
||||
|
||||
public class AntoraVersionUtils {
|
||||
|
||||
public static String getDefaultAntoraVersion(String projectVersion) {
|
||||
int preReleaseIndex = getSnapshotIndex(projectVersion);
|
||||
return isSnapshot(projectVersion) ? projectVersion.substring(0, preReleaseIndex) : projectVersion;
|
||||
}
|
||||
|
||||
public static String getDefaultAntoraPrerelease(String projectVersion) {
|
||||
if (isSnapshot(projectVersion)) {
|
||||
int preReleaseIndex = getSnapshotIndex(projectVersion);
|
||||
return projectVersion.substring(preReleaseIndex);
|
||||
}
|
||||
if (isPreRelease(projectVersion)) {
|
||||
return Boolean.TRUE.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getDefaultAntoraDisplayVersion(String projectVersion) {
|
||||
if (!isSnapshot(projectVersion) && isPreRelease(projectVersion)) {
|
||||
return getDefaultAntoraVersion(projectVersion);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isSnapshot(String projectVersion) {
|
||||
return getSnapshotIndex(projectVersion) >= 0;
|
||||
}
|
||||
|
||||
private static int getSnapshotIndex(String projectVersion) {
|
||||
return projectVersion.lastIndexOf("-SNAPSHOT");
|
||||
}
|
||||
|
||||
private static boolean isPreRelease(String projectVersion) {
|
||||
return projectVersion.lastIndexOf("-") >= 0;
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.antora;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.GradleException;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.Optional;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
import org.yaml.snakeyaml.nodes.NodeTuple;
|
||||
import org.yaml.snakeyaml.nodes.Tag;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.gradle.github.milestones.NextVersionYml;
|
||||
|
||||
public abstract class UpdateAntoraVersionTask extends DefaultTask {
|
||||
|
||||
@TaskAction
|
||||
public void update() throws IOException {
|
||||
String projectVersion = getProject().getVersion().toString();
|
||||
File antoraYmlFile = getAntoraYmlFile().getAsFile().get();
|
||||
String updatedAntoraVersion = AntoraVersionUtils.getDefaultAntoraVersion(projectVersion);
|
||||
String updatedAntoraPrerelease = AntoraVersionUtils.getDefaultAntoraPrerelease(projectVersion);
|
||||
String updatedAntoraDisplayVersion = AntoraVersionUtils.getDefaultAntoraDisplayVersion(projectVersion);
|
||||
|
||||
Representer representer = new Representer();
|
||||
representer.getPropertyUtils().setSkipMissingProperties(true);
|
||||
|
||||
Yaml yaml = new Yaml(new Constructor(AntoraYml.class), representer);
|
||||
AntoraYml antoraYml = yaml.load(new FileInputStream(antoraYmlFile));
|
||||
|
||||
System.out.println("Updating the version parameters in " + antoraYmlFile.getName() + " to version: "
|
||||
+ updatedAntoraVersion + ", prerelease: " + updatedAntoraPrerelease + ", display_version: "
|
||||
+ updatedAntoraDisplayVersion);
|
||||
antoraYml.setVersion(updatedAntoraVersion);
|
||||
antoraYml.setPrerelease(updatedAntoraPrerelease);
|
||||
antoraYml.setDisplay_version(updatedAntoraDisplayVersion);
|
||||
|
||||
FileWriter outputWriter = new FileWriter(antoraYmlFile);
|
||||
getYaml().dump(antoraYml, outputWriter);
|
||||
}
|
||||
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getAntoraYmlFile();
|
||||
|
||||
public static class AntoraYml {
|
||||
|
||||
private String name;
|
||||
|
||||
private String version;
|
||||
|
||||
private String prerelease;
|
||||
|
||||
private String display_version;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getPrerelease() {
|
||||
return prerelease;
|
||||
}
|
||||
|
||||
public void setPrerelease(String prerelease) {
|
||||
this.prerelease = prerelease;
|
||||
}
|
||||
|
||||
public String getDisplay_version() {
|
||||
return display_version;
|
||||
}
|
||||
|
||||
public void setDisplay_version(String display_version) {
|
||||
this.display_version = display_version;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Yaml getYaml() {
|
||||
Representer representer = new Representer() {
|
||||
@Override
|
||||
protected NodeTuple representJavaBeanProperty(Object javaBean,
|
||||
org.yaml.snakeyaml.introspector.Property property, Object propertyValue, Tag customTag) {
|
||||
// Don't write out null values
|
||||
if (propertyValue == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return super.representJavaBeanProperty(javaBean, property, propertyValue, customTag);
|
||||
}
|
||||
}
|
||||
};
|
||||
representer.addClassTag(AntoraYml.class, Tag.MAP);
|
||||
DumperOptions ymlOptions = new DumperOptions();
|
||||
ymlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
ymlOptions.setDefaultScalarStyle(DumperOptions.ScalarStyle.SINGLE_QUOTED);
|
||||
return new Yaml(representer, ymlOptions);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
package org.springframework.gradle.github;
|
||||
|
||||
public class RepositoryRef {
|
||||
import java.io.Serializable;
|
||||
|
||||
public class RepositoryRef implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 7151218536746822797L;
|
||||
|
||||
private String owner;
|
||||
|
||||
private String name;
|
||||
|
||||
+172
-17
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2020 the original author or authors.
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,13 +17,21 @@
|
||||
package org.springframework.gradle.github.milestones;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
import org.springframework.gradle.github.RepositoryRef;
|
||||
@@ -33,7 +41,10 @@ public class GitHubMilestoneApi {
|
||||
|
||||
private OkHttpClient client;
|
||||
|
||||
private Gson gson = new Gson();
|
||||
private final Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(LocalDate.class, new LocalDateAdapter().nullSafe())
|
||||
.registerTypeAdapter(LocalDateTime.class, new LocalDateTimeAdapter().nullSafe())
|
||||
.create();
|
||||
|
||||
public GitHubMilestoneApi() {
|
||||
this.client = new OkHttpClient.Builder().build();
|
||||
@@ -50,26 +61,30 @@ public class GitHubMilestoneApi {
|
||||
}
|
||||
|
||||
public long findMilestoneNumberByTitle(RepositoryRef repositoryRef, String milestoneTitle) {
|
||||
List<Milestone> milestones = this.getMilestones(repositoryRef);
|
||||
for (Milestone milestone : milestones) {
|
||||
if (milestoneTitle.equals(milestone.getTitle())) {
|
||||
return milestone.getNumber();
|
||||
}
|
||||
}
|
||||
if (milestones.size() <= 100) {
|
||||
throw new RuntimeException("Could not find open milestone with title " + milestoneTitle + " for repository " + repositoryRef + " Got " + milestones);
|
||||
}
|
||||
throw new RuntimeException("It is possible there are too many open milestones (only 100 are supported). Could not find open milestone with title " + milestoneTitle + " for repository " + repositoryRef + " Got " + milestones);
|
||||
}
|
||||
|
||||
public List<Milestone> getMilestones(RepositoryRef repositoryRef) {
|
||||
String url = this.baseUrl + "/repos/" + repositoryRef.getOwner() + "/" + repositoryRef.getName() + "/milestones?per_page=100";
|
||||
Request request = new Request.Builder().get().url(url)
|
||||
.build();
|
||||
try {
|
||||
Response response = this.client.newCall(request).execute();
|
||||
if (!response.isSuccessful()) {
|
||||
throw new RuntimeException("Could not find milestone with title " + milestoneTitle + " for repository " + repositoryRef + ". Response " + response);
|
||||
throw new RuntimeException("Could not retrieve milestones for repository " + repositoryRef + ". Response " + response);
|
||||
}
|
||||
List<Milestone> milestones = this.gson.fromJson(response.body().charStream(), new TypeToken<List<Milestone>>(){}.getType());
|
||||
for (Milestone milestone : milestones) {
|
||||
if (milestoneTitle.equals(milestone.getTitle())) {
|
||||
return milestone.getNumber();
|
||||
}
|
||||
}
|
||||
if (milestones.size() <= 100) {
|
||||
throw new RuntimeException("Could not find open milestone with title " + milestoneTitle + " for repository " + repositoryRef + " Got " + milestones);
|
||||
}
|
||||
throw new RuntimeException("It is possible there are too many open milestones open (only 100 are supported). Could not find open milestone with title " + milestoneTitle + " for repository " + repositoryRef + " Got " + milestones);
|
||||
return this.gson.fromJson(response.body().charStream(), new TypeToken<List<Milestone>>(){}.getType());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Could not find open milestone with title " + milestoneTitle + " for repository " + repositoryRef, e);
|
||||
throw new RuntimeException("Could not retrieve milestones for repository " + repositoryRef, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,10 +104,150 @@ public class GitHubMilestoneApi {
|
||||
}
|
||||
}
|
||||
|
||||
// public boolean isOpenIssuesForMilestoneName(String owner, String repository, String milestoneName) {
|
||||
//
|
||||
// }
|
||||
/**
|
||||
* Check if the given milestone is due today or past due.
|
||||
*
|
||||
* @param repositoryRef The repository owner/name
|
||||
* @param milestoneTitle The title of the milestone whose due date should be checked
|
||||
* @return true if the given milestone is due today or past due, false otherwise
|
||||
*/
|
||||
public boolean isMilestoneDueToday(RepositoryRef repositoryRef, String milestoneTitle) {
|
||||
String url = this.baseUrl + "/repos/" + repositoryRef.getOwner() + "/" + repositoryRef.getName()
|
||||
+ "/milestones?per_page=100";
|
||||
Request request = new Request.Builder().get().url(url).build();
|
||||
try {
|
||||
Response response = this.client.newCall(request).execute();
|
||||
if (!response.isSuccessful()) {
|
||||
throw new RuntimeException("Could not find milestone with title " + milestoneTitle + " for repository "
|
||||
+ repositoryRef + ". Response " + response);
|
||||
}
|
||||
List<Milestone> milestones = this.gson.fromJson(response.body().charStream(),
|
||||
new TypeToken<List<Milestone>>() {
|
||||
}.getType());
|
||||
for (Milestone milestone : milestones) {
|
||||
if (milestoneTitle.equals(milestone.getTitle())) {
|
||||
LocalDate today = LocalDate.now();
|
||||
return milestone.getDueOn() != null && today.compareTo(milestone.getDueOn().toLocalDate()) >= 0;
|
||||
}
|
||||
}
|
||||
if (milestones.size() <= 100) {
|
||||
throw new RuntimeException("Could not find open milestone with title " + milestoneTitle
|
||||
+ " for repository " + repositoryRef + " Got " + milestones);
|
||||
}
|
||||
throw new RuntimeException(
|
||||
"It is possible there are too many open milestones open (only 100 are supported). Could not find open milestone with title "
|
||||
+ milestoneTitle + " for repository " + repositoryRef + " Got " + milestones);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(
|
||||
"Could not find open milestone with title " + milestoneTitle + " for repository " + repositoryRef,
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the next release version based on the current version.
|
||||
*
|
||||
* The current version must conform to the pattern MAJOR.MINOR.PATCH-SNAPSHOT. If the
|
||||
* current version is a snapshot of a patch release, then the patch release will be
|
||||
* returned. For example, if the current version is 5.6.1-SNAPSHOT, then 5.6.1 will be
|
||||
* returned. If the current version is a snapshot of a version that is not GA (i.e the
|
||||
* PATCH segment is 0), then GitHub will be queried to find the next milestone or
|
||||
* release candidate. If no pre-release versions are found, then the next version will
|
||||
* be assumed to be the GA.
|
||||
* @param repositoryRef The repository owner/name
|
||||
* @param currentVersion The current project version
|
||||
* @return the next matching milestone/release candidate or null if none exist
|
||||
*/
|
||||
public String getNextReleaseMilestone(RepositoryRef repositoryRef, String currentVersion) {
|
||||
Pattern snapshotPattern = Pattern.compile("^([0-9]+)\\.([0-9]+)\\.([0-9]+)-SNAPSHOT$");
|
||||
Matcher snapshotVersion = snapshotPattern.matcher(currentVersion);
|
||||
|
||||
if (snapshotVersion.find()) {
|
||||
String patchSegment = snapshotVersion.group(3);
|
||||
String currentVersionNoIdentifier = currentVersion.replace("-SNAPSHOT", "");
|
||||
if (patchSegment.equals("0")) {
|
||||
String nextPreRelease = getNextPreRelease(repositoryRef, currentVersionNoIdentifier);
|
||||
return nextPreRelease != null ? nextPreRelease : currentVersionNoIdentifier;
|
||||
}
|
||||
else {
|
||||
return currentVersionNoIdentifier;
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"Cannot calculate next release version because the current project version does not conform to the expected format");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the next pre-release version (milestone or release candidate) based on
|
||||
* the current version.
|
||||
*
|
||||
* The current version must conform to the pattern MAJOR.MINOR.PATCH. If no matching
|
||||
* milestone or release candidate is found in GitHub then it will return null.
|
||||
* @param repositoryRef The repository owner/name
|
||||
* @param currentVersionNoIdentifier The current project version without any
|
||||
* identifier
|
||||
* @return the next matching milestone/release candidate or null if none exist
|
||||
*/
|
||||
private String getNextPreRelease(RepositoryRef repositoryRef, String currentVersionNoIdentifier) {
|
||||
String url = this.baseUrl + "/repos/" + repositoryRef.getOwner() + "/" + repositoryRef.getName()
|
||||
+ "/milestones?per_page=100";
|
||||
Request request = new Request.Builder().get().url(url).build();
|
||||
try {
|
||||
Response response = this.client.newCall(request).execute();
|
||||
if (!response.isSuccessful()) {
|
||||
throw new RuntimeException(
|
||||
"Could not get milestones for repository " + repositoryRef + ". Response " + response);
|
||||
}
|
||||
List<Milestone> milestones = this.gson.fromJson(response.body().charStream(),
|
||||
new TypeToken<List<Milestone>>() {
|
||||
}.getType());
|
||||
Optional<String> nextPreRelease = milestones.stream().map(Milestone::getTitle)
|
||||
.filter(m -> m.startsWith(currentVersionNoIdentifier + "-"))
|
||||
.min((m1, m2) -> {
|
||||
Pattern preReleasePattern = Pattern.compile("^.*-([A-Z]+)([0-9]+)$");
|
||||
Matcher matcher1 = preReleasePattern.matcher(m1);
|
||||
Matcher matcher2 = preReleasePattern.matcher(m2);
|
||||
matcher1.find();
|
||||
matcher2.find();
|
||||
if (!matcher1.group(1).equals(matcher2.group(1))) {
|
||||
return m1.compareTo(m2);
|
||||
}
|
||||
else {
|
||||
return Integer.valueOf(matcher1.group(2)).compareTo(Integer.valueOf(matcher2.group(2)));
|
||||
}
|
||||
});
|
||||
return nextPreRelease.orElse(null);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException("Could not find open milestones with for repository " + repositoryRef, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a milestone.
|
||||
*
|
||||
* @param repository The repository owner/name
|
||||
* @param milestone The milestone containing a title and due date
|
||||
*/
|
||||
public void createMilestone(RepositoryRef repository, Milestone milestone) {
|
||||
String url = this.baseUrl + "/repos/" + repository.getOwner() + "/" + repository.getName() + "/milestones";
|
||||
String json = this.gson.toJson(milestone);
|
||||
RequestBody body = RequestBody.create(MediaType.parse("application/json"), json);
|
||||
Request request = new Request.Builder().url(url).post(body).build();
|
||||
try {
|
||||
Response response = this.client.newCall(request).execute();
|
||||
if (!response.isSuccessful()) {
|
||||
throw new RuntimeException(String.format("Could not create milestone %s for repository %s/%s. Got response %s",
|
||||
milestone.getTitle(), repository.getOwner(), repository.getName(), response));
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(String.format("Could not create release %s for repository %s/%s",
|
||||
milestone.getTitle(), repository.getOwner(), repository.getName()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static class AuthorizationInterceptor implements Interceptor {
|
||||
|
||||
|
||||
+40
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2020 the original author or authors.
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,32 +17,66 @@ package org.springframework.gradle.github.milestones;
|
||||
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.Optional;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.work.DisableCachingByDefault;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.springframework.gradle.github.RepositoryRef;
|
||||
|
||||
public class GitHubMilestoneHasNoOpenIssuesTask extends DefaultTask {
|
||||
@DisableCachingByDefault(because = "the due date needs to be checked every time in case it changes")
|
||||
public abstract class GitHubMilestoneHasNoOpenIssuesTask extends DefaultTask {
|
||||
@Input
|
||||
private RepositoryRef repository = new RepositoryRef();
|
||||
|
||||
@Input
|
||||
@Input @Optional
|
||||
private String milestoneTitle;
|
||||
|
||||
@InputFile @Optional
|
||||
public abstract RegularFileProperty getNextVersionFile();
|
||||
|
||||
@Input @Optional
|
||||
private String gitHubAccessToken;
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getIsOpenIssuesFile();
|
||||
|
||||
private GitHubMilestoneApi milestones = new GitHubMilestoneApi();
|
||||
|
||||
@TaskAction
|
||||
public void checkHasNoOpenIssues() {
|
||||
public void checkHasNoOpenIssues() throws IOException {
|
||||
if (this.milestoneTitle == null) {
|
||||
File nextVersionFile = getNextVersionFile().getAsFile().get();
|
||||
Yaml yaml = new Yaml(new Constructor(NextVersionYml.class));
|
||||
NextVersionYml nextVersionYml = yaml.load(new FileInputStream(nextVersionFile));
|
||||
String nextVersion = nextVersionYml.getVersion();
|
||||
if (nextVersion == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Could not find version property in provided file " + nextVersionFile.getName());
|
||||
}
|
||||
this.milestoneTitle = nextVersion;
|
||||
}
|
||||
long milestoneNumber = this.milestones.findMilestoneNumberByTitle(this.repository, this.milestoneTitle);
|
||||
boolean isOpenIssues = this.milestones.isOpenIssuesForMilestoneNumber(this.repository, milestoneNumber);
|
||||
Path isOpenIssuesPath = getIsOpenIssuesFile().getAsFile().get().toPath();
|
||||
Files.write(isOpenIssuesPath, String.valueOf(isOpenIssues).getBytes());
|
||||
if (isOpenIssues) {
|
||||
throw new IllegalStateException("The repository " + this.repository + " has open issues for milestone with the title " + this.milestoneTitle + " and number " + milestoneNumber);
|
||||
System.out.println("The repository " + this.repository + " has open issues for milestone with the title " + this.milestoneTitle + " and number " + milestoneNumber);
|
||||
}
|
||||
else {
|
||||
System.out.println("The repository " + this.repository + " has no open issues for milestone with the title " + this.milestoneTitle + " and number " + milestoneNumber);
|
||||
}
|
||||
System.out.println("The repository " + this.repository + " has no open issues for milestone with the title " + this.milestoneTitle + " and number " + milestoneNumber);
|
||||
}
|
||||
|
||||
public RepositoryRef getRepository() {
|
||||
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.github.milestones;
|
||||
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.Optional;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.nodes.Tag;
|
||||
import org.yaml.snakeyaml.representer.Representer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.gradle.github.RepositoryRef;
|
||||
|
||||
public abstract class GitHubMilestoneNextReleaseTask extends DefaultTask {
|
||||
|
||||
@Input
|
||||
private RepositoryRef repository = new RepositoryRef();
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
private String gitHubAccessToken;
|
||||
|
||||
private GitHubMilestoneApi milestones = new GitHubMilestoneApi();
|
||||
|
||||
@TaskAction
|
||||
public void calculateNextReleaseMilestone() throws IOException {
|
||||
String currentVersion = getProject().getVersion().toString();
|
||||
String nextPreRelease = this.milestones.getNextReleaseMilestone(this.repository, currentVersion);
|
||||
System.out.println("The next release milestone is: " + nextPreRelease);
|
||||
NextVersionYml nextVersionYml = new NextVersionYml();
|
||||
nextVersionYml.setVersion(nextPreRelease);
|
||||
File outputFile = getNextReleaseFile().get().getAsFile();
|
||||
FileWriter outputWriter = new FileWriter(outputFile);
|
||||
Yaml yaml = getYaml();
|
||||
yaml.dump(nextVersionYml, outputWriter);
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getNextReleaseFile();
|
||||
|
||||
public RepositoryRef getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public void repository(Action<RepositoryRef> repository) {
|
||||
repository.execute(this.repository);
|
||||
}
|
||||
|
||||
public void setRepository(RepositoryRef repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public String getGitHubAccessToken() {
|
||||
return gitHubAccessToken;
|
||||
}
|
||||
|
||||
public void setGitHubAccessToken(String gitHubAccessToken) {
|
||||
this.gitHubAccessToken = gitHubAccessToken;
|
||||
this.milestones = new GitHubMilestoneApi(gitHubAccessToken);
|
||||
}
|
||||
|
||||
private Yaml getYaml() {
|
||||
Representer representer = new Representer();
|
||||
representer.addClassTag(NextVersionYml.class, Tag.MAP);
|
||||
DumperOptions ymlOptions = new DumperOptions();
|
||||
ymlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
|
||||
return new Yaml(representer, ymlOptions);
|
||||
}
|
||||
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.github.milestones;
|
||||
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.Optional;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.work.DisableCachingByDefault;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.springframework.gradle.github.RepositoryRef;
|
||||
|
||||
@DisableCachingByDefault(because = "the due date needs to be checked every time in case it changes")
|
||||
public abstract class GitHubMilestoneNextVersionDueTodayTask extends DefaultTask {
|
||||
|
||||
@Input
|
||||
private RepositoryRef repository = new RepositoryRef();
|
||||
|
||||
@Input
|
||||
@Optional
|
||||
private String gitHubAccessToken;
|
||||
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getNextVersionFile();
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getIsDueTodayFile();
|
||||
|
||||
private GitHubMilestoneApi milestones = new GitHubMilestoneApi();
|
||||
|
||||
@TaskAction
|
||||
public void checkReleaseDueToday() throws IOException {
|
||||
File nextVersionFile = getNextVersionFile().getAsFile().get();
|
||||
Yaml yaml = new Yaml(new Constructor(NextVersionYml.class));
|
||||
NextVersionYml nextVersionYml = yaml.load(new FileInputStream(nextVersionFile));
|
||||
String nextVersion = nextVersionYml.getVersion();
|
||||
if (nextVersion == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Could not find version property in provided file " + nextVersionFile.getName());
|
||||
}
|
||||
boolean milestoneDueToday = this.milestones.isMilestoneDueToday(this.repository, nextVersion);
|
||||
Path isDueTodayPath = getIsDueTodayFile().getAsFile().get().toPath();
|
||||
Files.writeString(isDueTodayPath, String.valueOf(milestoneDueToday));
|
||||
if (milestoneDueToday) {
|
||||
System.out.println("The milestone with the title " + nextVersion + " in the repository " + this.repository
|
||||
+ " is due today");
|
||||
}
|
||||
else {
|
||||
System.out.println("The milestone with the title " + nextVersion + " in the repository "
|
||||
+ this.repository + " is not due yet");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public RepositoryRef getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public void repository(Action<RepositoryRef> repository) {
|
||||
repository.execute(this.repository);
|
||||
}
|
||||
|
||||
public void setRepository(RepositoryRef repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public String getGitHubAccessToken() {
|
||||
return gitHubAccessToken;
|
||||
}
|
||||
|
||||
public void setGitHubAccessToken(String gitHubAccessToken) {
|
||||
this.gitHubAccessToken = gitHubAccessToken;
|
||||
this.milestones = new GitHubMilestoneApi(gitHubAccessToken);
|
||||
}
|
||||
|
||||
}
|
||||
+43
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2020 the original author or authors.
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,23 +16,55 @@
|
||||
|
||||
package org.springframework.gradle.github.milestones;
|
||||
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
|
||||
public class GitHubMilestonePlugin implements Plugin<Project> {
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getTasks().register("gitHubCheckMilestoneHasNoOpenIssues", GitHubMilestoneHasNoOpenIssuesTask.class, new Action<GitHubMilestoneHasNoOpenIssuesTask>() {
|
||||
@Override
|
||||
public void execute(GitHubMilestoneHasNoOpenIssuesTask githubCheckMilestoneHasNoOpenIssues) {
|
||||
githubCheckMilestoneHasNoOpenIssues.setGroup("Release");
|
||||
githubCheckMilestoneHasNoOpenIssues.setDescription("Checks if there are any open issues for the specified repository and milestone");
|
||||
githubCheckMilestoneHasNoOpenIssues.setMilestoneTitle((String) project.findProperty("nextVersion"));
|
||||
if (project.hasProperty("gitHubAccessToken")) {
|
||||
githubCheckMilestoneHasNoOpenIssues.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken"));
|
||||
}
|
||||
TaskProvider<GitHubMilestoneNextReleaseTask> nextReleaseMilestoneTask = project.getTasks().register("gitHubNextReleaseMilestone", GitHubMilestoneNextReleaseTask.class, (gitHubMilestoneNextReleaseTask) -> {
|
||||
gitHubMilestoneNextReleaseTask.doNotTrackState("API call to GitHub needs to check for new milestones every time");
|
||||
gitHubMilestoneNextReleaseTask.setGroup("Release");
|
||||
gitHubMilestoneNextReleaseTask.setDescription("Calculates the next release version based on the current version and outputs it to a yaml file");
|
||||
gitHubMilestoneNextReleaseTask.getNextReleaseFile()
|
||||
.fileProvider(project.provider(() -> project.file("next-release.yml")));
|
||||
if (project.hasProperty("gitHubAccessToken")) {
|
||||
gitHubMilestoneNextReleaseTask
|
||||
.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken"));
|
||||
}
|
||||
});
|
||||
project.getTasks().register("gitHubCheckMilestoneHasNoOpenIssues", GitHubMilestoneHasNoOpenIssuesTask.class, (githubCheckMilestoneHasNoOpenIssues) -> {
|
||||
githubCheckMilestoneHasNoOpenIssues.setGroup("Release");
|
||||
githubCheckMilestoneHasNoOpenIssues.setDescription("Checks if there are any open issues for the specified repository and milestone");
|
||||
githubCheckMilestoneHasNoOpenIssues.getIsOpenIssuesFile().value(project.getLayout().getBuildDirectory().file("github/milestones/is-open-issues"));
|
||||
githubCheckMilestoneHasNoOpenIssues.setMilestoneTitle((String) project.findProperty("nextVersion"));
|
||||
if (!project.hasProperty("nextVersion")) {
|
||||
githubCheckMilestoneHasNoOpenIssues.getNextVersionFile().convention(
|
||||
nextReleaseMilestoneTask.flatMap(GitHubMilestoneNextReleaseTask::getNextReleaseFile));
|
||||
}
|
||||
if (project.hasProperty("gitHubAccessToken")) {
|
||||
githubCheckMilestoneHasNoOpenIssues.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken"));
|
||||
}
|
||||
});
|
||||
project.getTasks().register("gitHubCheckNextVersionDueToday", GitHubMilestoneNextVersionDueTodayTask.class, (gitHubMilestoneNextVersionDueTodayTask) -> {
|
||||
gitHubMilestoneNextVersionDueTodayTask.setGroup("Release");
|
||||
gitHubMilestoneNextVersionDueTodayTask.setDescription("Checks if the next release version is due today or past due, will fail if the next version is not due yet");
|
||||
gitHubMilestoneNextVersionDueTodayTask.getIsDueTodayFile().value(project.getLayout().getBuildDirectory().file("github/milestones/is-due-today"));
|
||||
gitHubMilestoneNextVersionDueTodayTask.getNextVersionFile().convention(
|
||||
nextReleaseMilestoneTask.flatMap(GitHubMilestoneNextReleaseTask::getNextReleaseFile));
|
||||
if (project.hasProperty("gitHubAccessToken")) {
|
||||
gitHubMilestoneNextVersionDueTodayTask
|
||||
.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken"));
|
||||
}
|
||||
});
|
||||
project.getTasks().register("scheduleNextRelease", ScheduleNextReleaseTask.class, (scheduleNextRelease) -> {
|
||||
scheduleNextRelease.doNotTrackState("API call to GitHub needs to check for new milestones every time");
|
||||
scheduleNextRelease.setGroup("Release");
|
||||
scheduleNextRelease.setDescription("Schedule the next release (even months only) or release train (series of milestones starting in January or July) based on the current version");
|
||||
|
||||
scheduleNextRelease.setVersion((String) project.findProperty("nextVersion"));
|
||||
scheduleNextRelease.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package org.springframework.gradle.github.milestones;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
/**
|
||||
* @author Steve Riesenberg
|
||||
*/
|
||||
class LocalDateAdapter extends TypeAdapter<LocalDate> {
|
||||
@Override
|
||||
public void write(JsonWriter jsonWriter, LocalDate localDate) throws IOException {
|
||||
jsonWriter.value(localDate.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDate read(JsonReader jsonReader) throws IOException {
|
||||
return LocalDate.parse(jsonReader.nextString());
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package org.springframework.gradle.github.milestones;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
/**
|
||||
* @author Steve Riesenberg
|
||||
*/
|
||||
class LocalDateTimeAdapter extends TypeAdapter<LocalDateTime> {
|
||||
@Override
|
||||
public void write(JsonWriter jsonWriter, LocalDateTime localDateTime) throws IOException {
|
||||
jsonWriter.value(localDateTime.atOffset(ZoneOffset.UTC).format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalDateTime read(JsonReader jsonReader) throws IOException {
|
||||
return LocalDateTime.parse(jsonReader.nextString(), DateTimeFormatter.ISO_ZONED_DATE_TIME);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,35 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.github.milestones;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author Steve Riesenberg
|
||||
*/
|
||||
public class Milestone {
|
||||
private String title;
|
||||
|
||||
private long number;
|
||||
private Long number;
|
||||
|
||||
@SerializedName("due_on")
|
||||
private LocalDateTime dueOn;
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
@@ -13,19 +39,28 @@ public class Milestone {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public long getNumber() {
|
||||
public Long getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(long number) {
|
||||
public void setNumber(Long number) {
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
public LocalDateTime getDueOn() {
|
||||
return dueOn;
|
||||
}
|
||||
|
||||
public void setDueOn(LocalDateTime dueOn) {
|
||||
this.dueOn = dueOn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Milestone{" +
|
||||
"title='" + title + '\'' +
|
||||
", number=" + number +
|
||||
", number='" + number + '\'' +
|
||||
", dueOn='" + dueOn + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.github.milestones;
|
||||
|
||||
public class NextVersionYml {
|
||||
private String version;
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.github.milestones;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import org.springframework.gradle.github.RepositoryRef;
|
||||
|
||||
/**
|
||||
* @author Steve Riesenberg
|
||||
*/
|
||||
public class ScheduleNextReleaseTask extends DefaultTask {
|
||||
@Input
|
||||
private RepositoryRef repository = new RepositoryRef();
|
||||
|
||||
@Input
|
||||
private String gitHubAccessToken;
|
||||
|
||||
@Input
|
||||
private String version;
|
||||
|
||||
@Input
|
||||
private Integer weekOfMonth;
|
||||
|
||||
@Input
|
||||
private Integer dayOfWeek;
|
||||
|
||||
@TaskAction
|
||||
public void scheduleNextRelease() {
|
||||
GitHubMilestoneApi gitHubMilestoneApi = new GitHubMilestoneApi(this.gitHubAccessToken);
|
||||
String nextReleaseMilestone = gitHubMilestoneApi.getNextReleaseMilestone(this.repository, this.version);
|
||||
|
||||
// If the next release contains a dash (e.g. 5.6.0-RC1), it is already scheduled
|
||||
if (nextReleaseMilestone.contains("-")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check to see if a scheduled GA version already exists
|
||||
boolean hasExistingMilestone = gitHubMilestoneApi.getMilestones(this.repository).stream()
|
||||
.anyMatch(milestone -> nextReleaseMilestone.equals(milestone.getTitle()));
|
||||
if (hasExistingMilestone) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Next milestone is either a patch version or minor version
|
||||
// Note: Major versions will be handled like minor and get a release
|
||||
// train which can be manually updated to match the desired schedule.
|
||||
if (nextReleaseMilestone.endsWith(".0")) {
|
||||
// Create M1, M2, M3, RC1 and GA milestones for release train
|
||||
getReleaseTrain(nextReleaseMilestone).getTrainDates().forEach((milestoneTitle, dueOn) -> {
|
||||
Milestone milestone = new Milestone();
|
||||
milestone.setTitle(milestoneTitle);
|
||||
// Note: GitHub seems to store full date/time as UTC then displays
|
||||
// as a date (no time) in your timezone, which means the date will
|
||||
// not always be the same date as we intend.
|
||||
// Using 12pm/noon UTC allows GitHub to schedule and display the
|
||||
// correct date.
|
||||
milestone.setDueOn(dueOn.atTime(LocalTime.NOON));
|
||||
gitHubMilestoneApi.createMilestone(this.repository, milestone);
|
||||
});
|
||||
} else {
|
||||
// Create GA milestone for patch release on the next even month
|
||||
LocalDate startDate = LocalDate.now();
|
||||
LocalDate dueOn = getReleaseTrain(nextReleaseMilestone).getNextReleaseDate(startDate);
|
||||
Milestone milestone = new Milestone();
|
||||
milestone.setTitle(nextReleaseMilestone);
|
||||
milestone.setDueOn(dueOn.atTime(LocalTime.NOON));
|
||||
gitHubMilestoneApi.createMilestone(this.repository, milestone);
|
||||
}
|
||||
}
|
||||
|
||||
private SpringReleaseTrain getReleaseTrain(String nextReleaseMilestone) {
|
||||
SpringReleaseTrainSpec releaseTrainSpec =
|
||||
SpringReleaseTrainSpec.builder()
|
||||
.nextTrain()
|
||||
.version(nextReleaseMilestone)
|
||||
.weekOfMonth(this.weekOfMonth)
|
||||
.dayOfWeek(this.dayOfWeek)
|
||||
.build();
|
||||
|
||||
return new SpringReleaseTrain(releaseTrainSpec);
|
||||
}
|
||||
|
||||
public RepositoryRef getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
public void repository(Action<RepositoryRef> repository) {
|
||||
repository.execute(this.repository);
|
||||
}
|
||||
|
||||
public void setRepository(RepositoryRef repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public String getGitHubAccessToken() {
|
||||
return this.gitHubAccessToken;
|
||||
}
|
||||
|
||||
public void setGitHubAccessToken(String gitHubAccessToken) {
|
||||
this.gitHubAccessToken = gitHubAccessToken;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return this.version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Integer getWeekOfMonth() {
|
||||
return weekOfMonth;
|
||||
}
|
||||
|
||||
public void setWeekOfMonth(Integer weekOfMonth) {
|
||||
this.weekOfMonth = weekOfMonth;
|
||||
}
|
||||
|
||||
public Integer getDayOfWeek() {
|
||||
return dayOfWeek;
|
||||
}
|
||||
|
||||
public void setDayOfWeek(Integer dayOfWeek) {
|
||||
this.dayOfWeek = dayOfWeek;
|
||||
}
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.github.milestones;
|
||||
|
||||
import java.time.DayOfWeek;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.time.Year;
|
||||
import java.time.temporal.TemporalAdjuster;
|
||||
import java.time.temporal.TemporalAdjusters;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Spring release train generator based on rules contained in a specification.
|
||||
* <p>
|
||||
* The rules are:
|
||||
* <ol>
|
||||
* <li>Train 1 (January-May) or 2 (July-November)</li>
|
||||
* <li>Version number (e.g. 0.1.2, 1.0.0, etc.)</li>
|
||||
* <li>Week of month (1st, 2nd, 3rd, 4th)</li>
|
||||
* <li>Day of week (Monday-Friday)</li>
|
||||
* <li>Year (e.g. 2020, 2021, etc.)</li>
|
||||
* </ol>
|
||||
*
|
||||
* The release train generated will contain M1, M2, M3, RC1 and GA versions
|
||||
* mapped to their respective dates in the train.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
*/
|
||||
public final class SpringReleaseTrain {
|
||||
private final SpringReleaseTrainSpec releaseTrainSpec;
|
||||
|
||||
public SpringReleaseTrain(SpringReleaseTrainSpec releaseTrainSpec) {
|
||||
this.releaseTrainSpec = releaseTrainSpec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate release train dates based on the release train specification.
|
||||
*
|
||||
* @return A mapping of release milestones to scheduled release dates
|
||||
*/
|
||||
public Map<String, LocalDate> getTrainDates() {
|
||||
Map<String, LocalDate> releaseDates = new LinkedHashMap<>();
|
||||
switch (this.releaseTrainSpec.getTrain()) {
|
||||
case ONE:
|
||||
addTrainDate(releaseDates, "M1", Month.JANUARY);
|
||||
addTrainDate(releaseDates, "M2", Month.FEBRUARY);
|
||||
addTrainDate(releaseDates, "M3", Month.MARCH);
|
||||
addTrainDate(releaseDates, "RC1", Month.APRIL);
|
||||
addTrainDate(releaseDates, null, Month.MAY);
|
||||
break;
|
||||
case TWO:
|
||||
addTrainDate(releaseDates, "M1", Month.JULY);
|
||||
addTrainDate(releaseDates, "M2", Month.AUGUST);
|
||||
addTrainDate(releaseDates, "M3", Month.SEPTEMBER);
|
||||
addTrainDate(releaseDates, "RC1", Month.OCTOBER);
|
||||
addTrainDate(releaseDates, null, Month.NOVEMBER);
|
||||
break;
|
||||
}
|
||||
|
||||
return releaseDates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given date matches the due date of given version.
|
||||
*
|
||||
* @param version The version number (e.g. 5.6.0-M1, 5.6.0, etc.)
|
||||
* @param expectedDate The expected date
|
||||
* @return true if the given date matches the due date of the given version, false otherwise
|
||||
*/
|
||||
public boolean isTrainDate(String version, LocalDate expectedDate) {
|
||||
return expectedDate.isEqual(getTrainDates().get(version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the next release date following the given date.
|
||||
* <p>
|
||||
* The next release date is always on an even month so that a patch release
|
||||
* is the month after the GA version of a release train. This method does
|
||||
* not consider the year of the release train, only the given start date.
|
||||
*
|
||||
* @param startDate The start date
|
||||
* @return The next release date following the given date
|
||||
*/
|
||||
public LocalDate getNextReleaseDate(LocalDate startDate) {
|
||||
LocalDate trainDate;
|
||||
LocalDate currentDate = startDate;
|
||||
do {
|
||||
trainDate = calculateReleaseDate(
|
||||
Year.of(currentDate.getYear()),
|
||||
currentDate.getMonth(),
|
||||
this.releaseTrainSpec.getDayOfWeek().getDayOfWeek(),
|
||||
this.releaseTrainSpec.getWeekOfMonth().getDayOffset()
|
||||
);
|
||||
currentDate = currentDate.plusMonths(1);
|
||||
} while (!trainDate.isAfter(startDate) || trainDate.getMonthValue() % 2 != 0);
|
||||
|
||||
return trainDate;
|
||||
}
|
||||
|
||||
private void addTrainDate(Map<String, LocalDate> releaseDates, String milestone, Month month) {
|
||||
LocalDate releaseDate = calculateReleaseDate(
|
||||
this.releaseTrainSpec.getYear(),
|
||||
month,
|
||||
this.releaseTrainSpec.getDayOfWeek().getDayOfWeek(),
|
||||
this.releaseTrainSpec.getWeekOfMonth().getDayOffset()
|
||||
);
|
||||
String suffix = (milestone == null) ? "" : "-" + milestone;
|
||||
releaseDates.put(this.releaseTrainSpec.getVersion() + suffix, releaseDate);
|
||||
}
|
||||
|
||||
private static LocalDate calculateReleaseDate(Year year, Month month, DayOfWeek dayOfWeek, int dayOffset) {
|
||||
TemporalAdjuster nextMonday = TemporalAdjusters.nextOrSame(DayOfWeek.MONDAY);
|
||||
TemporalAdjuster nextDayOfWeek = TemporalAdjusters.nextOrSame(dayOfWeek);
|
||||
|
||||
LocalDate firstDayOfMonth = year.atMonth(month).atDay(1);
|
||||
LocalDate firstMondayOfMonth = firstDayOfMonth.with(nextMonday);
|
||||
|
||||
return firstMondayOfMonth.with(nextDayOfWeek).plusDays(dayOffset);
|
||||
}
|
||||
}
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.github.milestones;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.time.Year;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A specification for a release train.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @see SpringReleaseTrain
|
||||
*/
|
||||
public final class SpringReleaseTrainSpec {
|
||||
private final Train train;
|
||||
private final String version;
|
||||
private final WeekOfMonth weekOfMonth;
|
||||
private final DayOfWeek dayOfWeek;
|
||||
private final Year year;
|
||||
|
||||
public SpringReleaseTrainSpec(Train train, String version, WeekOfMonth weekOfMonth, DayOfWeek dayOfWeek, Year year) {
|
||||
this.train = train;
|
||||
this.version = version;
|
||||
this.weekOfMonth = weekOfMonth;
|
||||
this.dayOfWeek = dayOfWeek;
|
||||
this.year = year;
|
||||
}
|
||||
|
||||
public Train getTrain() {
|
||||
return train;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public WeekOfMonth getWeekOfMonth() {
|
||||
return weekOfMonth;
|
||||
}
|
||||
|
||||
public DayOfWeek getDayOfWeek() {
|
||||
return dayOfWeek;
|
||||
}
|
||||
|
||||
public Year getYear() {
|
||||
return year;
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public enum WeekOfMonth {
|
||||
FIRST(0), SECOND(7), THIRD(14), FOURTH(21);
|
||||
|
||||
private final int dayOffset;
|
||||
|
||||
WeekOfMonth(int dayOffset) {
|
||||
this.dayOffset = dayOffset;
|
||||
}
|
||||
|
||||
public int getDayOffset() {
|
||||
return dayOffset;
|
||||
}
|
||||
}
|
||||
|
||||
public enum DayOfWeek {
|
||||
MONDAY(java.time.DayOfWeek.MONDAY),
|
||||
TUESDAY(java.time.DayOfWeek.TUESDAY),
|
||||
WEDNESDAY(java.time.DayOfWeek.WEDNESDAY),
|
||||
THURSDAY(java.time.DayOfWeek.THURSDAY),
|
||||
FRIDAY(java.time.DayOfWeek.FRIDAY);
|
||||
|
||||
private final java.time.DayOfWeek dayOfWeek;
|
||||
|
||||
DayOfWeek(java.time.DayOfWeek dayOfWeek) {
|
||||
this.dayOfWeek = dayOfWeek;
|
||||
}
|
||||
|
||||
public java.time.DayOfWeek getDayOfWeek() {
|
||||
return dayOfWeek;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Train {
|
||||
ONE, TWO
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private Train train;
|
||||
private String version;
|
||||
private WeekOfMonth weekOfMonth;
|
||||
private DayOfWeek dayOfWeek;
|
||||
private Year year;
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public Builder train(int train) {
|
||||
switch (train) {
|
||||
case 1: this.train = Train.ONE; break;
|
||||
case 2: this.train = Train.TWO; break;
|
||||
default: throw new IllegalArgumentException("Invalid train: " + train);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder train(Train train) {
|
||||
this.train = train;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder nextTrain() {
|
||||
// Search for next train starting with this month
|
||||
return nextTrain(LocalDate.now().withDayOfMonth(1));
|
||||
}
|
||||
|
||||
public Builder nextTrain(LocalDate startDate) {
|
||||
Train nextTrain = null;
|
||||
|
||||
// Search for next train from a given start date
|
||||
LocalDate currentDate = startDate;
|
||||
while (nextTrain == null) {
|
||||
if (currentDate.getMonth() == Month.JANUARY) {
|
||||
nextTrain = Train.ONE;
|
||||
} else if (currentDate.getMonth() == Month.JULY) {
|
||||
nextTrain = Train.TWO;
|
||||
}
|
||||
|
||||
currentDate = currentDate.plusMonths(1);
|
||||
}
|
||||
|
||||
return train(nextTrain).year(currentDate.getYear());
|
||||
}
|
||||
|
||||
public Builder version(String version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder weekOfMonth(int weekOfMonth) {
|
||||
switch (weekOfMonth) {
|
||||
case 1: this.weekOfMonth = WeekOfMonth.FIRST; break;
|
||||
case 2: this.weekOfMonth = WeekOfMonth.SECOND; break;
|
||||
case 3: this.weekOfMonth = WeekOfMonth.THIRD; break;
|
||||
case 4: this.weekOfMonth = WeekOfMonth.FOURTH; break;
|
||||
default: throw new IllegalArgumentException("Invalid weekOfMonth: " + weekOfMonth);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder weekOfMonth(WeekOfMonth weekOfMonth) {
|
||||
this.weekOfMonth = weekOfMonth;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder dayOfWeek(int dayOfWeek) {
|
||||
switch (dayOfWeek) {
|
||||
case 1: this.dayOfWeek = DayOfWeek.MONDAY; break;
|
||||
case 2: this.dayOfWeek = DayOfWeek.TUESDAY; break;
|
||||
case 3: this.dayOfWeek = DayOfWeek.WEDNESDAY; break;
|
||||
case 4: this.dayOfWeek = DayOfWeek.THURSDAY; break;
|
||||
case 5: this.dayOfWeek = DayOfWeek.FRIDAY; break;
|
||||
default: throw new IllegalArgumentException("Invalid dayOfWeek: " + dayOfWeek);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder dayOfWeek(DayOfWeek dayOfWeek) {
|
||||
this.dayOfWeek = dayOfWeek;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder year(int year) {
|
||||
this.year = Year.of(year);
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpringReleaseTrainSpec build() {
|
||||
Assert.notNull(train, "train cannot be null");
|
||||
Assert.notNull(version, "version cannot be null");
|
||||
Assert.notNull(weekOfMonth, "weekOfMonth cannot be null");
|
||||
Assert.notNull(dayOfWeek, "dayOfWeek cannot be null");
|
||||
Assert.notNull(year, "year cannot be null");
|
||||
return new SpringReleaseTrainSpec(train, version, weekOfMonth, dayOfWeek, year);
|
||||
}
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.github.release;
|
||||
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import org.springframework.gradle.github.RepositoryRef;
|
||||
|
||||
/**
|
||||
* @author Steve Riesenberg
|
||||
*/
|
||||
public class DispatchGitHubWorkflowTask extends DefaultTask {
|
||||
@Input
|
||||
private RepositoryRef repository = new RepositoryRef();
|
||||
|
||||
@Input
|
||||
private String gitHubAccessToken;
|
||||
|
||||
@Input
|
||||
private String branch;
|
||||
|
||||
@Input
|
||||
private String workflowId;
|
||||
|
||||
@TaskAction
|
||||
public void dispatchGitHubWorkflow() {
|
||||
GitHubActionsApi gitHubActionsApi = new GitHubActionsApi(this.gitHubAccessToken);
|
||||
WorkflowDispatch workflowDispatch = new WorkflowDispatch(this.branch, null);
|
||||
gitHubActionsApi.dispatchWorkflow(this.repository, this.workflowId, workflowDispatch);
|
||||
}
|
||||
|
||||
public RepositoryRef getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public void repository(Action<RepositoryRef> repository) {
|
||||
repository.execute(this.repository);
|
||||
}
|
||||
|
||||
public void setRepository(RepositoryRef repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public String getGitHubAccessToken() {
|
||||
return gitHubAccessToken;
|
||||
}
|
||||
|
||||
public void setGitHubAccessToken(String gitHubAccessToken) {
|
||||
this.gitHubAccessToken = gitHubAccessToken;
|
||||
}
|
||||
|
||||
public String getBranch() {
|
||||
return branch;
|
||||
}
|
||||
|
||||
public void setBranch(String branch) {
|
||||
this.branch = branch;
|
||||
}
|
||||
|
||||
public String getWorkflowId() {
|
||||
return workflowId;
|
||||
}
|
||||
|
||||
public void setWorkflowId(String workflowId) {
|
||||
this.workflowId = workflowId;
|
||||
}
|
||||
}
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.github.release;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
import org.springframework.gradle.github.RepositoryRef;
|
||||
|
||||
/**
|
||||
* Manage GitHub Actions.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
*/
|
||||
public class GitHubActionsApi {
|
||||
private String baseUrl = "https://api.github.com";
|
||||
|
||||
private final OkHttpClient client;
|
||||
|
||||
private final Gson gson = new GsonBuilder().create();
|
||||
|
||||
public GitHubActionsApi() {
|
||||
this.client = new OkHttpClient.Builder().build();
|
||||
}
|
||||
|
||||
public GitHubActionsApi(String gitHubToken) {
|
||||
this.client = new OkHttpClient.Builder()
|
||||
.addInterceptor(new AuthorizationInterceptor(gitHubToken))
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a workflow dispatch event.
|
||||
*
|
||||
* @param repository The repository owner/name
|
||||
* @param workflowId The ID of the workflow or the name of the workflow file name
|
||||
* @param workflowDispatch The workflow dispatch containing a ref (branch) and optional inputs
|
||||
*/
|
||||
public void dispatchWorkflow(RepositoryRef repository, String workflowId, WorkflowDispatch workflowDispatch) {
|
||||
String url = this.baseUrl + "/repos/" + repository.getOwner() + "/" + repository.getName() + "/actions/workflows/" + workflowId + "/dispatches";
|
||||
String json = this.gson.toJson(workflowDispatch);
|
||||
RequestBody body = RequestBody.create(MediaType.parse("application/json"), json);
|
||||
Request request = new Request.Builder().url(url).post(body).build();
|
||||
try {
|
||||
Response response = this.client.newCall(request).execute();
|
||||
if (!response.isSuccessful()) {
|
||||
throw new RuntimeException(String.format("Could not create workflow dispatch %s for repository %s/%s. Got response %s",
|
||||
workflowId, repository.getOwner(), repository.getName(), response));
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(String.format("Could not create workflow dispatch %s for repository %s/%s",
|
||||
workflowId, repository.getOwner(), repository.getName()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static class AuthorizationInterceptor implements Interceptor {
|
||||
private final String token;
|
||||
|
||||
public AuthorizationInterceptor(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request request = chain.request().newBuilder()
|
||||
.addHeader("Authorization", "Bearer " + this.token)
|
||||
.build();
|
||||
|
||||
return chain.proceed(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
+21
-16
@@ -17,7 +17,6 @@
|
||||
package org.springframework.gradle.github.release;
|
||||
|
||||
import groovy.lang.MissingPropertyException;
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
|
||||
@@ -27,23 +26,29 @@ import org.gradle.api.Project;
|
||||
public class GitHubReleasePlugin implements Plugin<Project> {
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getTasks().register("createGitHubRelease", CreateGitHubReleaseTask.class, new Action<CreateGitHubReleaseTask>() {
|
||||
@Override
|
||||
public void execute(CreateGitHubReleaseTask createGitHubRelease) {
|
||||
createGitHubRelease.setGroup("Release");
|
||||
createGitHubRelease.setDescription("Create a github release");
|
||||
createGitHubRelease.dependsOn("generateChangelog");
|
||||
project.getTasks().register("createGitHubRelease", CreateGitHubReleaseTask.class, (createGitHubRelease) -> {
|
||||
createGitHubRelease.setGroup("Release");
|
||||
createGitHubRelease.setDescription("Create a github release");
|
||||
createGitHubRelease.dependsOn("generateChangelog");
|
||||
|
||||
createGitHubRelease.setCreateRelease("true".equals(project.findProperty("createRelease")));
|
||||
createGitHubRelease.setVersion((String) project.findProperty("nextVersion"));
|
||||
if (project.hasProperty("branch")) {
|
||||
createGitHubRelease.setBranch((String) project.findProperty("branch"));
|
||||
}
|
||||
createGitHubRelease.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken"));
|
||||
if (createGitHubRelease.isCreateRelease() && createGitHubRelease.getGitHubAccessToken() == null) {
|
||||
throw new MissingPropertyException("Please provide an access token with -PgitHubAccessToken=...");
|
||||
}
|
||||
createGitHubRelease.setCreateRelease("true".equals(project.findProperty("createRelease")));
|
||||
createGitHubRelease.setVersion((String) project.findProperty("nextVersion"));
|
||||
if (project.hasProperty("branch")) {
|
||||
createGitHubRelease.setBranch((String) project.findProperty("branch"));
|
||||
}
|
||||
createGitHubRelease.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken"));
|
||||
if (createGitHubRelease.isCreateRelease() && createGitHubRelease.getGitHubAccessToken() == null) {
|
||||
throw new MissingPropertyException("Please provide an access token with -PgitHubAccessToken=...");
|
||||
}
|
||||
});
|
||||
|
||||
project.getTasks().register("dispatchGitHubWorkflow", DispatchGitHubWorkflowTask.class, (dispatchGitHubWorkflow) -> {
|
||||
dispatchGitHubWorkflow.setGroup("Release");
|
||||
dispatchGitHubWorkflow.setDescription("Create a workflow_dispatch event on a given branch");
|
||||
|
||||
dispatchGitHubWorkflow.setBranch((String) project.findProperty("branch"));
|
||||
dispatchGitHubWorkflow.setWorkflowId((String) project.findProperty("workflowId"));
|
||||
dispatchGitHubWorkflow.setGitHubAccessToken((String) project.findProperty("gitHubAccessToken"));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+28
-11
@@ -14,21 +14,38 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.saml2.provider.service.web.authentication;
|
||||
package org.springframework.gradle.github.release;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A strategy for resolving a SAML 2.0 Authentication Request from the
|
||||
* {@link HttpServletRequest}.
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 5.7
|
||||
* @author Steve Riesenberg
|
||||
*/
|
||||
public interface Saml2AuthenticationRequestResolver {
|
||||
public class WorkflowDispatch {
|
||||
private String ref;
|
||||
private Map<String, Object> inputs;
|
||||
|
||||
<T extends AbstractSaml2AuthenticationRequest> T resolve(HttpServletRequest request);
|
||||
public WorkflowDispatch() {
|
||||
}
|
||||
|
||||
public WorkflowDispatch(String ref, Map<String, Object> inputs) {
|
||||
this.ref = ref;
|
||||
this.inputs = inputs;
|
||||
}
|
||||
|
||||
public String getRef() {
|
||||
return ref;
|
||||
}
|
||||
|
||||
public void setRef(String ref) {
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
public Map<String, Object> getInputs() {
|
||||
return inputs;
|
||||
}
|
||||
|
||||
public void setInputs(Map<String, Object> inputs) {
|
||||
this.inputs = inputs;
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.convention.versions;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Scanner;
|
||||
|
||||
class CommandLineUtils {
|
||||
static void runCommand(File dir, String... args) {
|
||||
try {
|
||||
Process process = new ProcessBuilder()
|
||||
.directory(dir)
|
||||
.command(args)
|
||||
.start();
|
||||
writeLinesTo(process.getInputStream(), System.out);
|
||||
writeLinesTo(process.getErrorStream(), System.out);
|
||||
if (process.waitFor() != 0) {
|
||||
new RuntimeException("Failed to run " + Arrays.toString(args));
|
||||
}
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new RuntimeException("Failed to run " + Arrays.toString(args), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeLinesTo(InputStream input, PrintStream out) {
|
||||
Scanner scanner = new Scanner(input);
|
||||
while(scanner.hasNextLine()) {
|
||||
out.println(scanner.nextLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.convention.versions;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.function.Function;
|
||||
|
||||
class FileUtils {
|
||||
static void replaceFileText(File file, Function<String, String> replaceText) {
|
||||
String buildFileText = readString(file);
|
||||
String updatedBuildFileText = replaceText.apply(buildFileText);
|
||||
writeString(file, updatedBuildFileText);
|
||||
}
|
||||
|
||||
static String readString(File file) {
|
||||
try {
|
||||
byte[] bytes = Files.readAllBytes(file.toPath());
|
||||
return new String(bytes);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeString(File file, String text) {
|
||||
try {
|
||||
Files.write(file.toPath(), text.getBytes());
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-57
@@ -33,13 +33,8 @@ import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.file.Files;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -168,7 +163,7 @@ public class UpdateDependenciesPlugin implements Plugin<Project> {
|
||||
Integer issueNumber = gitHubApi.createIssue(createIssueResult.getRepositoryId(), title, createIssueResult.getLabelIds(), createIssueResult.getMilestoneId(), createIssueResult.getAssigneeId()).delayElement(Duration.ofSeconds(1)).block();
|
||||
commitMessage += "\n\nCloses gh-" + issueNumber;
|
||||
}
|
||||
runCommand(rootDir, "git", "commit", "-am", commitMessage);
|
||||
CommandLineUtils.runCommand(rootDir, "git", "commit", "-am", commitMessage);
|
||||
}
|
||||
|
||||
private Mono<GitHubApi.FindCreateIssueResult> createIssueResultMono(UpdateDependenciesExtension updateDependenciesExtension) {
|
||||
@@ -187,7 +182,7 @@ public class UpdateDependenciesPlugin implements Plugin<Project> {
|
||||
if (current.compareTo(running) > 0) {
|
||||
String title = "Update Gradle to " + current.getVersion();
|
||||
System.out.println(title);
|
||||
runCommand(project.getRootDir(), "./gradlew", "wrapper", "--gradle-version", current.getVersion(), "--no-daemon");
|
||||
CommandLineUtils.runCommand(project.getRootDir(), "./gradlew", "wrapper", "--gradle-version", current.getVersion(), "--no-daemon");
|
||||
afterGroup(updateDependenciesSettings, project.getRootDir(), title, createIssueResultMono(updateDependenciesSettings));
|
||||
}
|
||||
}
|
||||
@@ -204,30 +199,6 @@ public class UpdateDependenciesPlugin implements Plugin<Project> {
|
||||
};
|
||||
}
|
||||
|
||||
static void runCommand(File dir, String... args) {
|
||||
try {
|
||||
Process process = new ProcessBuilder()
|
||||
.directory(dir)
|
||||
.command(args)
|
||||
.start();
|
||||
writeLinesTo(process.getInputStream(), System.out);
|
||||
writeLinesTo(process.getErrorStream(), System.out);
|
||||
if (process.waitFor() != 0) {
|
||||
new RuntimeException("Failed to run " + Arrays.toString(args));
|
||||
}
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new RuntimeException("Failed to run " + Arrays.toString(args), e);
|
||||
}
|
||||
}
|
||||
|
||||
static void writeLinesTo(InputStream input, PrintStream out) {
|
||||
Scanner scanner = new Scanner(input);
|
||||
while(scanner.hasNextLine()) {
|
||||
out.println(scanner.nextLine());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Action<ComponentSelectionWithCurrent> excludeWithRegex(String regex, String reason) {
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
return (selection) -> {
|
||||
@@ -242,40 +213,17 @@ public class UpdateDependenciesPlugin implements Plugin<Project> {
|
||||
String ga = dependency.getGroup() + ":" + dependency.getName() + ":";
|
||||
String originalDependency = ga + dependency.getVersion();
|
||||
String replacementDependency = ga + updatedVersion(dependency);
|
||||
replaceFileText(buildFile, buildFileText -> buildFileText.replace(originalDependency, replacementDependency));
|
||||
}
|
||||
|
||||
static void replaceFileText(File file, Function<String, String> replaceText) {
|
||||
String buildFileText = readString(file);
|
||||
String updatedBuildFileText = replaceText.apply(buildFileText);
|
||||
writeString(file, updatedBuildFileText);
|
||||
}
|
||||
|
||||
private static String readString(File file) {
|
||||
try {
|
||||
byte[] bytes = Files.readAllBytes(file.toPath());
|
||||
return new String(bytes);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeString(File file, String text) {
|
||||
try {
|
||||
Files.write(file.toPath(), text.getBytes());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
FileUtils.replaceFileText(buildFile, buildFileText -> buildFileText.replace(originalDependency, replacementDependency));
|
||||
}
|
||||
|
||||
static void updateDependencyWithVersionVariable(File scanFile, File gradlePropertiesFile, DependencyOutdated dependency) {
|
||||
if (!gradlePropertiesFile.exists()) {
|
||||
return;
|
||||
}
|
||||
replaceFileText(gradlePropertiesFile, (gradlePropertiesText) -> {
|
||||
FileUtils.replaceFileText(gradlePropertiesFile, (gradlePropertiesText) -> {
|
||||
String ga = dependency.getGroup() + ":" + dependency.getName() + ":";
|
||||
Pattern pattern = Pattern.compile("\"" + ga + "\\$\\{?([^'\"]+?)\\}?\"");
|
||||
String buildFileText = readString(scanFile);
|
||||
String buildFileText = FileUtils.readString(scanFile);
|
||||
Matcher matcher = pattern.matcher(buildFileText);
|
||||
while (matcher.find()) {
|
||||
String versionVariable = matcher.group(1);
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.convention.versions;
|
||||
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
|
||||
public class UpdateProjectVersionPlugin implements Plugin<Project> {
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
project.getTasks().register("updateProjectVersion", UpdateProjectVersionTask.class, new Action<UpdateProjectVersionTask>() {
|
||||
@Override
|
||||
public void execute(UpdateProjectVersionTask updateProjectVersionTask) {
|
||||
updateProjectVersionTask.setGroup("Release");
|
||||
updateProjectVersionTask.setDescription("Updates the project version to the next release in gradle.properties");
|
||||
updateProjectVersionTask.dependsOn("gitHubNextReleaseMilestone");
|
||||
updateProjectVersionTask.getNextVersionFile().fileProvider(project.provider(() -> project.file("next-release.yml")));
|
||||
}
|
||||
});
|
||||
project.getTasks().register("updateToSnapshotVersion", UpdateToSnapshotVersionTask.class, new Action<UpdateToSnapshotVersionTask>() {
|
||||
@Override
|
||||
public void execute(UpdateToSnapshotVersionTask updateToSnapshotVersionTask) {
|
||||
updateToSnapshotVersionTask.setGroup("Release");
|
||||
updateToSnapshotVersionTask.setDescription(
|
||||
"Updates the project version to the next snapshot in gradle.properties");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.convention.versions;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.Optional;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.constructor.Constructor;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import org.springframework.gradle.github.milestones.NextVersionYml;
|
||||
|
||||
public abstract class UpdateProjectVersionTask extends DefaultTask {
|
||||
|
||||
@InputFile
|
||||
public abstract RegularFileProperty getNextVersionFile();
|
||||
|
||||
@TaskAction
|
||||
public void checkReleaseDueToday() throws FileNotFoundException {
|
||||
File nextVersionFile = getNextVersionFile().getAsFile().get();
|
||||
Yaml yaml = new Yaml(new Constructor(NextVersionYml.class));
|
||||
NextVersionYml nextVersionYml = yaml.load(new FileInputStream(nextVersionFile));
|
||||
String nextVersion = nextVersionYml.getVersion();
|
||||
if (nextVersion == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Could not find version property in provided file " + nextVersionFile.getName());
|
||||
}
|
||||
String currentVersion = getProject().getVersion().toString();
|
||||
File gradlePropertiesFile = getProject().getRootProject().file(Project.GRADLE_PROPERTIES);
|
||||
if (!gradlePropertiesFile.exists()) {
|
||||
return;
|
||||
}
|
||||
System.out.println("Updating the project version in " + Project.GRADLE_PROPERTIES + " from " + currentVersion
|
||||
+ " to " + nextVersion);
|
||||
FileUtils.replaceFileText(gradlePropertiesFile, (gradlePropertiesText) -> {
|
||||
gradlePropertiesText = gradlePropertiesText.replace("version=" + currentVersion, "version=" + nextVersion);
|
||||
return gradlePropertiesText;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.convention.versions;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public abstract class UpdateToSnapshotVersionTask extends DefaultTask {
|
||||
|
||||
private static final String RELEASE_VERSION_PATTERN = "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-M\\d+|-RC\\d+)?$";
|
||||
|
||||
@TaskAction
|
||||
public void updateToSnapshotVersion() {
|
||||
String currentVersion = getProject().getVersion().toString();
|
||||
File gradlePropertiesFile = getProject().getRootProject().file(Project.GRADLE_PROPERTIES);
|
||||
if (!gradlePropertiesFile.exists()) {
|
||||
return;
|
||||
}
|
||||
String nextVersion = calculateNextSnapshotVersion(currentVersion);
|
||||
System.out.println("Updating the project version in " + Project.GRADLE_PROPERTIES + " from " + currentVersion
|
||||
+ " to " + nextVersion);
|
||||
FileUtils.replaceFileText(gradlePropertiesFile, (gradlePropertiesText) -> {
|
||||
gradlePropertiesText = gradlePropertiesText.replace("version=" + currentVersion, "version=" + nextVersion);
|
||||
return gradlePropertiesText;
|
||||
});
|
||||
}
|
||||
|
||||
private String calculateNextSnapshotVersion(String currentVersion) {
|
||||
Pattern releaseVersionPattern = Pattern.compile(RELEASE_VERSION_PATTERN);
|
||||
Matcher releaseVersion = releaseVersionPattern.matcher(currentVersion);
|
||||
|
||||
if (releaseVersion.find()) {
|
||||
String majorSegment = releaseVersion.group(1);
|
||||
String minorSegment = releaseVersion.group(2);
|
||||
String patchSegment = releaseVersion.group(3);
|
||||
String modifier = releaseVersion.group(4);
|
||||
if (modifier == null) {
|
||||
patchSegment = String.valueOf(Integer.parseInt(patchSegment) + 1);
|
||||
}
|
||||
System.out.println("modifier = " + modifier);
|
||||
return String.format("%s.%s.%s-SNAPSHOT", majorSegment, minorSegment, patchSegment);
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"Cannot calculate next snapshot version because the current project version does not conform to the expected format");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -164,18 +164,14 @@ public class S101Configurer {
|
||||
String source = "https://structure101.com/binaries/v6";
|
||||
try (final WebClient webClient = new WebClient()) {
|
||||
HtmlPage page = webClient.getPage(source);
|
||||
Matcher matcher = null;
|
||||
for (HtmlAnchor anchor : page.getAnchors()) {
|
||||
Matcher candidate = Pattern.compile("(structure101-build-java-all-)(.*).zip").matcher(anchor.getHrefAttribute());
|
||||
if (candidate.find()) {
|
||||
matcher = candidate;
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (matcher == null) {
|
||||
return null;
|
||||
}
|
||||
copyZipToFilesystem(source, installationDirectory, matcher.group(1) + matcher.group(2));
|
||||
return matcher.group(2);
|
||||
return null;
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
-389
@@ -1,389 +0,0 @@
|
||||
package io.spring.gradle.github.milestones;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import okhttp3.mockwebserver.RecordedRequest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.gradle.github.RepositoryRef;
|
||||
import org.springframework.gradle.github.milestones.GitHubMilestoneApi;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
|
||||
public class GitHubMilestoneApiTests {
|
||||
private GitHubMilestoneApi github;
|
||||
|
||||
private RepositoryRef repositoryRef = RepositoryRef.owner("spring-projects").repository("spring-security").build();
|
||||
|
||||
private MockWebServer server;
|
||||
|
||||
private String baseUrl;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
this.server = new MockWebServer();
|
||||
this.server.start();
|
||||
this.github = new GitHubMilestoneApi("mock-oauth-token");
|
||||
this.baseUrl = this.server.url("/api").toString();
|
||||
this.github.setBaseUrl(this.baseUrl);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanup() throws Exception {
|
||||
this.server.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMilestoneNumberByTitleWhenFoundThenSuccess() throws Exception {
|
||||
String responseJson = "[\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/207\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207/labels\",\n" +
|
||||
" \"id\":6611880,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNjYxMTg4MA==\",\n" +
|
||||
" \"number\":207,\n" +
|
||||
" \"title\":\"5.6.x\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jgrandja\",\n" +
|
||||
" \"id\":10884212,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjEwODg0MjEy\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/10884212?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jgrandja\",\n" +
|
||||
" \"html_url\":\"https://github.com/jgrandja\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jgrandja/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jgrandja/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jgrandja/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jgrandja/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jgrandja/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jgrandja/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jgrandja/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jgrandja/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jgrandja/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":1,\n" +
|
||||
" \"closed_issues\":0,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2021-03-31T11:29:17Z\",\n" +
|
||||
" \"updated_at\":\"2021-03-31T11:30:47Z\",\n" +
|
||||
" \"due_on\":null,\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/191\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191/labels\",\n" +
|
||||
" \"id\":5884208,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNTg4NDIwOA==\",\n" +
|
||||
" \"number\":191,\n" +
|
||||
" \"title\":\"5.5.0-RC1\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jzheaux\",\n" +
|
||||
" \"id\":3627351,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" +
|
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":21,\n" +
|
||||
" \"closed_issues\":23,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2020-09-16T13:28:03Z\",\n" +
|
||||
" \"updated_at\":\"2021-04-06T23:47:10Z\",\n" +
|
||||
" \"due_on\":\"2021-04-12T07:00:00Z\",\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" }\n" +
|
||||
"]";
|
||||
this.server.enqueue(new MockResponse().setBody(responseJson));
|
||||
|
||||
long milestoneNumberByTitle = this.github.findMilestoneNumberByTitle(this.repositoryRef, "5.5.0-RC1");
|
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
|
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("get");
|
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/milestones?per_page=100");
|
||||
|
||||
assertThat(milestoneNumberByTitle).isEqualTo(191);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findMilestoneNumberByTitleWhenNotFoundThenException() throws Exception {
|
||||
String responseJson = "[\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/207\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207/labels\",\n" +
|
||||
" \"id\":6611880,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNjYxMTg4MA==\",\n" +
|
||||
" \"number\":207,\n" +
|
||||
" \"title\":\"5.6.x\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jgrandja\",\n" +
|
||||
" \"id\":10884212,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjEwODg0MjEy\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/10884212?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jgrandja\",\n" +
|
||||
" \"html_url\":\"https://github.com/jgrandja\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jgrandja/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jgrandja/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jgrandja/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jgrandja/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jgrandja/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jgrandja/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jgrandja/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jgrandja/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jgrandja/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":1,\n" +
|
||||
" \"closed_issues\":0,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2021-03-31T11:29:17Z\",\n" +
|
||||
" \"updated_at\":\"2021-03-31T11:30:47Z\",\n" +
|
||||
" \"due_on\":null,\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/191\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191/labels\",\n" +
|
||||
" \"id\":5884208,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNTg4NDIwOA==\",\n" +
|
||||
" \"number\":191,\n" +
|
||||
" \"title\":\"5.5.0-RC1\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jzheaux\",\n" +
|
||||
" \"id\":3627351,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" +
|
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":21,\n" +
|
||||
" \"closed_issues\":23,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2020-09-16T13:28:03Z\",\n" +
|
||||
" \"updated_at\":\"2021-04-06T23:47:10Z\",\n" +
|
||||
" \"due_on\":\"2021-04-12T07:00:00Z\",\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" }\n" +
|
||||
"]";
|
||||
this.server.enqueue(new MockResponse().setBody(responseJson));
|
||||
|
||||
assertThatExceptionOfType(RuntimeException.class)
|
||||
.isThrownBy(() -> this.github.findMilestoneNumberByTitle(this.repositoryRef, "missing"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isOpenIssuesForMilestoneNumberWhenAllClosedThenFalse() throws Exception {
|
||||
String responseJson = "[]";
|
||||
long milestoneNumber = 202;
|
||||
this.server.enqueue(new MockResponse().setBody(responseJson));
|
||||
|
||||
assertThat(this.github.isOpenIssuesForMilestoneNumber(this.repositoryRef, milestoneNumber)).isFalse();
|
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
|
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("get");
|
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/issues?per_page=1&milestone=" + milestoneNumber);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isOpenIssuesForMilestoneNumberWhenOpenIssuesThenTrue() throws Exception {
|
||||
String responseJson = "[\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/issues/9562\",\n" +
|
||||
" \"repository_url\":\"https://api.github.com/repos/spring-projects/spring-security\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/issues/9562/labels{/name}\",\n" +
|
||||
" \"comments_url\":\"https://api.github.com/repos/spring-projects/spring-security/issues/9562/comments\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/repos/spring-projects/spring-security/issues/9562/events\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/pull/9562\",\n" +
|
||||
" \"id\":851886504,\n" +
|
||||
" \"node_id\":\"MDExOlB1bGxSZXF1ZXN0NjEwMjMzMDcw\",\n" +
|
||||
" \"number\":9562,\n" +
|
||||
" \"title\":\"Add package-list\",\n" +
|
||||
" \"user\":{\n" +
|
||||
" \"login\":\"jzheaux\",\n" +
|
||||
" \"id\":3627351,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" +
|
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"labels\":[\n" +
|
||||
" {\n" +
|
||||
" \"id\":322225043,\n" +
|
||||
" \"node_id\":\"MDU6TGFiZWwzMjIyMjUwNDM=\",\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/labels/in:%20build\",\n" +
|
||||
" \"name\":\"in: build\",\n" +
|
||||
" \"color\":\"e8f9de\",\n" +
|
||||
" \"default\":false,\n" +
|
||||
" \"description\":\"An issue in the build\"\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"id\":322225079,\n" +
|
||||
" \"node_id\":\"MDU6TGFiZWwzMjIyMjUwNzk=\",\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/labels/type:%20bug\",\n" +
|
||||
" \"name\":\"type: bug\",\n" +
|
||||
" \"color\":\"e3d9fc\",\n" +
|
||||
" \"default\":false,\n" +
|
||||
" \"description\":\"A general bug\"\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"locked\":false,\n" +
|
||||
" \"assignee\":{\n" +
|
||||
" \"login\":\"rwinch\",\n" +
|
||||
" \"id\":362503,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjM2MjUwMw==\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/362503?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/rwinch\",\n" +
|
||||
" \"html_url\":\"https://github.com/rwinch\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/rwinch/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/rwinch/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/rwinch/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/rwinch/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/rwinch/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/rwinch/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/rwinch/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/rwinch/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/rwinch/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"assignees\":[\n" +
|
||||
" {\n" +
|
||||
" \"login\":\"rwinch\",\n" +
|
||||
" \"id\":362503,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjM2MjUwMw==\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/362503?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/rwinch\",\n" +
|
||||
" \"html_url\":\"https://github.com/rwinch\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/rwinch/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/rwinch/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/rwinch/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/rwinch/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/rwinch/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/rwinch/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/rwinch/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/rwinch/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/rwinch/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"milestone\":{\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/191\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191/labels\",\n" +
|
||||
" \"id\":5884208,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNTg4NDIwOA==\",\n" +
|
||||
" \"number\":191,\n" +
|
||||
" \"title\":\"5.5.0-RC1\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jzheaux\",\n" +
|
||||
" \"id\":3627351,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" +
|
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":21,\n" +
|
||||
" \"closed_issues\":23,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2020-09-16T13:28:03Z\",\n" +
|
||||
" \"updated_at\":\"2021-04-06T23:47:10Z\",\n" +
|
||||
" \"due_on\":\"2021-04-12T07:00:00Z\",\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" },\n" +
|
||||
" \"comments\":0,\n" +
|
||||
" \"created_at\":\"2021-04-06T23:47:10Z\",\n" +
|
||||
" \"updated_at\":\"2021-04-07T17:00:00Z\",\n" +
|
||||
" \"closed_at\":null,\n" +
|
||||
" \"author_association\":\"MEMBER\",\n" +
|
||||
" \"active_lock_reason\":null,\n" +
|
||||
" \"pull_request\":{\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/pulls/9562\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/pull/9562\",\n" +
|
||||
" \"diff_url\":\"https://github.com/spring-projects/spring-security/pull/9562.diff\",\n" +
|
||||
" \"patch_url\":\"https://github.com/spring-projects/spring-security/pull/9562.patch\"\n" +
|
||||
" },\n" +
|
||||
" \"body\":\"Closes gh-9528\\r\\n\\r\\n<!--\\r\\nFor Security Vulnerabilities, please use https://pivotal.io/security#reporting\\r\\n-->\\r\\n\\r\\n<!--\\r\\nBefore creating new features, we recommend creating an issue to discuss the feature. This ensures that everyone is on the same page before extensive work is done.\\r\\n\\r\\nThanks for contributing to Spring Security. Please provide a brief description of your pull-request and reference any related issue numbers (prefix references with gh-).\\r\\n-->\\r\\n\",\n" +
|
||||
" \"performed_via_github_app\":null\n" +
|
||||
" }\n" +
|
||||
"]";
|
||||
long milestoneNumber = 191;
|
||||
this.server.enqueue(new MockResponse().setBody(responseJson));
|
||||
|
||||
assertThat(this.github.isOpenIssuesForMilestoneNumber(this.repositoryRef, milestoneNumber)).isTrue();
|
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
|
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("get");
|
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/issues?per_page=1&milestone=" + milestoneNumber);
|
||||
}
|
||||
|
||||
}
|
||||
+31
-31
@@ -15,16 +15,16 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIOException;
|
||||
|
||||
class CheckAntoraVersionPluginTests {
|
||||
class AntoraVersionPluginTests {
|
||||
|
||||
@Test
|
||||
void defaultsPropertiesWhenSnapshot() {
|
||||
String expectedVersion = "1.0.0-SNAPSHOT";
|
||||
Project project = ProjectBuilder.builder().build();
|
||||
project.setVersion(expectedVersion);
|
||||
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
|
||||
project.getPluginManager().apply(AntoraVersionPlugin.class);
|
||||
|
||||
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
Task task = project.getTasks().findByName(AntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
|
||||
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
|
||||
|
||||
@@ -40,9 +40,9 @@ class CheckAntoraVersionPluginTests {
|
||||
String expectedVersion = "1.0.0-M1";
|
||||
Project project = ProjectBuilder.builder().build();
|
||||
project.setVersion(expectedVersion);
|
||||
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
|
||||
project.getPluginManager().apply(AntoraVersionPlugin.class);
|
||||
|
||||
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
Task task = project.getTasks().findByName(AntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
|
||||
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
|
||||
|
||||
@@ -58,9 +58,9 @@ class CheckAntoraVersionPluginTests {
|
||||
String expectedVersion = "1.0.0-RC1";
|
||||
Project project = ProjectBuilder.builder().build();
|
||||
project.setVersion(expectedVersion);
|
||||
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
|
||||
project.getPluginManager().apply(AntoraVersionPlugin.class);
|
||||
|
||||
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
Task task = project.getTasks().findByName(AntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
|
||||
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
|
||||
|
||||
@@ -76,9 +76,9 @@ class CheckAntoraVersionPluginTests {
|
||||
String expectedVersion = "1.0.0";
|
||||
Project project = ProjectBuilder.builder().build();
|
||||
project.setVersion(expectedVersion);
|
||||
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
|
||||
project.getPluginManager().apply(AntoraVersionPlugin.class);
|
||||
|
||||
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
Task task = project.getTasks().findByName(AntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
|
||||
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
|
||||
|
||||
@@ -92,9 +92,9 @@ class CheckAntoraVersionPluginTests {
|
||||
@Test
|
||||
void explicitProperties() {
|
||||
Project project = ProjectBuilder.builder().build();
|
||||
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
|
||||
project.getPluginManager().apply(AntoraVersionPlugin.class);
|
||||
|
||||
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
Task task = project.getTasks().findByName(AntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
|
||||
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
|
||||
checkAntoraVersionTask.getAntoraVersion().set("1.0.0");
|
||||
@@ -110,9 +110,9 @@ class CheckAntoraVersionPluginTests {
|
||||
Project project = ProjectBuilder.builder().build();
|
||||
File rootDir = project.getRootDir();
|
||||
IOUtils.write("version: '1.0.0'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
|
||||
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
|
||||
project.getPluginManager().apply(AntoraVersionPlugin.class);
|
||||
|
||||
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
Task task = project.getTasks().findByName(AntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
|
||||
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
|
||||
|
||||
@@ -125,9 +125,9 @@ class CheckAntoraVersionPluginTests {
|
||||
String expectedVersion = "1.0.0-SNAPSHOT";
|
||||
Project project = ProjectBuilder.builder().build();
|
||||
project.setVersion(expectedVersion);
|
||||
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
|
||||
project.getPluginManager().apply(AntoraVersionPlugin.class);
|
||||
|
||||
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
Task task = project.getTasks().findByName(AntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
|
||||
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
|
||||
|
||||
@@ -142,9 +142,9 @@ class CheckAntoraVersionPluginTests {
|
||||
File rootDir = project.getRootDir();
|
||||
IOUtils.write("version: '1.0.0'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
|
||||
project.setVersion(expectedVersion);
|
||||
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
|
||||
project.getPluginManager().apply(AntoraVersionPlugin.class);
|
||||
|
||||
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
Task task = project.getTasks().findByName(AntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
|
||||
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
|
||||
|
||||
@@ -160,9 +160,9 @@ class CheckAntoraVersionPluginTests {
|
||||
File rootDir = project.getRootDir();
|
||||
IOUtils.write("version: '1.0.0'\nprerelease: '-SNAPSHOT'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
|
||||
project.setVersion(expectedVersion);
|
||||
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
|
||||
project.getPluginManager().apply(AntoraVersionPlugin.class);
|
||||
|
||||
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
Task task = project.getTasks().findByName(AntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
|
||||
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
|
||||
|
||||
@@ -177,9 +177,9 @@ class CheckAntoraVersionPluginTests {
|
||||
File rootDir = project.getRootDir();
|
||||
IOUtils.write("version: '1.0.0-M1'\nprerelease: 'true'\ndisplay_version: '1.0.0-M1'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
|
||||
project.setVersion(expectedVersion);
|
||||
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
|
||||
project.getPluginManager().apply(AntoraVersionPlugin.class);
|
||||
|
||||
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
Task task = project.getTasks().findByName(AntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
|
||||
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
|
||||
|
||||
@@ -194,9 +194,9 @@ class CheckAntoraVersionPluginTests {
|
||||
File rootDir = project.getRootDir();
|
||||
IOUtils.write("version: '1.0.0-RC1'\nprerelease: 'true'\ndisplay_version: '1.0.0-RC1'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
|
||||
project.setVersion(expectedVersion);
|
||||
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
|
||||
project.getPluginManager().apply(AntoraVersionPlugin.class);
|
||||
|
||||
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
Task task = project.getTasks().findByName(AntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
|
||||
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
|
||||
|
||||
@@ -211,9 +211,9 @@ class CheckAntoraVersionPluginTests {
|
||||
File rootDir = project.getRootDir();
|
||||
IOUtils.write("version: '1.0.0'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
|
||||
project.setVersion(expectedVersion);
|
||||
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
|
||||
project.getPluginManager().apply(AntoraVersionPlugin.class);
|
||||
|
||||
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
Task task = project.getTasks().findByName(AntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
|
||||
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
|
||||
|
||||
@@ -226,9 +226,9 @@ class CheckAntoraVersionPluginTests {
|
||||
Project project = ProjectBuilder.builder().build();
|
||||
File rootDir = project.getRootDir();
|
||||
IOUtils.write("version: '1.0.0'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
|
||||
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
|
||||
project.getPluginManager().apply(AntoraVersionPlugin.class);
|
||||
|
||||
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
Task task = project.getTasks().findByName(AntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
|
||||
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
|
||||
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
|
||||
@@ -241,9 +241,9 @@ class CheckAntoraVersionPluginTests {
|
||||
Project project = ProjectBuilder.builder().build();
|
||||
File rootDir = project.getRootDir();
|
||||
IOUtils.write("version: '1.0.0'\nprerelease: '-SNAPSHOT'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
|
||||
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
|
||||
project.getPluginManager().apply(AntoraVersionPlugin.class);
|
||||
|
||||
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
Task task = project.getTasks().findByName(AntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
|
||||
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
|
||||
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
|
||||
@@ -257,9 +257,9 @@ class CheckAntoraVersionPluginTests {
|
||||
Project project = ProjectBuilder.builder().build();
|
||||
File rootDir = project.getRootDir();
|
||||
IOUtils.write("name: 'ROOT'\nversion: '1.0.0'", new FileOutputStream(new File(rootDir, "antora.yml")), StandardCharsets.UTF_8);
|
||||
project.getPluginManager().apply(CheckAntoraVersionPlugin.class);
|
||||
project.getPluginManager().apply(AntoraVersionPlugin.class);
|
||||
|
||||
Task task = project.getTasks().findByName(CheckAntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
Task task = project.getTasks().findByName(AntoraVersionPlugin.ANTORA_CHECK_VERSION_TASK_NAME);
|
||||
|
||||
assertThat(task).isInstanceOf(CheckAntoraVersionTask.class);
|
||||
CheckAntoraVersionTask checkAntoraVersionTask = (CheckAntoraVersionTask) task;
|
||||
+836
@@ -1,5 +1,9 @@
|
||||
package org.springframework.gradle.github.milestones;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalTime;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
@@ -385,4 +389,836 @@ public class GitHubMilestoneApiTests {
|
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/issues?per_page=1&milestone=" + milestoneNumber);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isMilestoneDueTodayWhenNotFoundThenException() throws Exception {
|
||||
String responseJson = "[\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/207\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207/labels\",\n" +
|
||||
" \"id\":6611880,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNjYxMTg4MA==\",\n" +
|
||||
" \"number\":207,\n" +
|
||||
" \"title\":\"5.6.x\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jgrandja\",\n" +
|
||||
" \"id\":10884212,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjEwODg0MjEy\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/10884212?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jgrandja\",\n" +
|
||||
" \"html_url\":\"https://github.com/jgrandja\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jgrandja/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jgrandja/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jgrandja/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jgrandja/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jgrandja/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jgrandja/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jgrandja/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jgrandja/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jgrandja/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":1,\n" +
|
||||
" \"closed_issues\":0,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2021-03-31T11:29:17Z\",\n" +
|
||||
" \"updated_at\":\"2021-03-31T11:30:47Z\",\n" +
|
||||
" \"due_on\":null,\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/191\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191/labels\",\n" +
|
||||
" \"id\":5884208,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNTg4NDIwOA==\",\n" +
|
||||
" \"number\":191,\n" +
|
||||
" \"title\":\"5.5.0-RC1\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jzheaux\",\n" +
|
||||
" \"id\":3627351,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" +
|
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":21,\n" +
|
||||
" \"closed_issues\":23,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2020-09-16T13:28:03Z\",\n" +
|
||||
" \"updated_at\":\"2021-04-06T23:47:10Z\",\n" +
|
||||
" \"due_on\":\"2021-04-12T07:00:00Z\",\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" }\n" +
|
||||
"]";
|
||||
this.server.enqueue(new MockResponse().setBody(responseJson));
|
||||
|
||||
assertThatExceptionOfType(RuntimeException.class)
|
||||
.isThrownBy(() -> this.github.isMilestoneDueToday(this.repositoryRef, "missing"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isMilestoneDueTodayWhenPastDueThenTrue() throws Exception {
|
||||
String responseJson = "[\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/207\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207/labels\",\n" +
|
||||
" \"id\":6611880,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNjYxMTg4MA==\",\n" +
|
||||
" \"number\":207,\n" +
|
||||
" \"title\":\"5.6.x\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jgrandja\",\n" +
|
||||
" \"id\":10884212,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjEwODg0MjEy\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/10884212?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jgrandja\",\n" +
|
||||
" \"html_url\":\"https://github.com/jgrandja\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jgrandja/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jgrandja/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jgrandja/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jgrandja/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jgrandja/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jgrandja/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jgrandja/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jgrandja/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jgrandja/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":1,\n" +
|
||||
" \"closed_issues\":0,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2021-03-31T11:29:17Z\",\n" +
|
||||
" \"updated_at\":\"2021-03-31T11:30:47Z\",\n" +
|
||||
" \"due_on\":null,\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/191\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191/labels\",\n" +
|
||||
" \"id\":5884208,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNTg4NDIwOA==\",\n" +
|
||||
" \"number\":191,\n" +
|
||||
" \"title\":\"5.5.0-RC1\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jzheaux\",\n" +
|
||||
" \"id\":3627351,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" +
|
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":21,\n" +
|
||||
" \"closed_issues\":23,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2020-09-16T13:28:03Z\",\n" +
|
||||
" \"updated_at\":\"2021-04-06T23:47:10Z\",\n" +
|
||||
" \"due_on\":\"2021-04-12T07:00:00Z\",\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" }\n" +
|
||||
"]";
|
||||
this.server.enqueue(new MockResponse().setBody(responseJson));
|
||||
|
||||
boolean dueToday = this.github.isMilestoneDueToday(this.repositoryRef, "5.5.0-RC1");
|
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
|
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("get");
|
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/milestones?per_page=100");
|
||||
|
||||
assertThat(dueToday).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isMilestoneDueTodayWhenDueTodayThenTrue() throws Exception {
|
||||
String responseJson = "[\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/207\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207/labels\",\n" +
|
||||
" \"id\":6611880,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNjYxMTg4MA==\",\n" +
|
||||
" \"number\":207,\n" +
|
||||
" \"title\":\"5.6.x\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jgrandja\",\n" +
|
||||
" \"id\":10884212,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjEwODg0MjEy\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/10884212?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jgrandja\",\n" +
|
||||
" \"html_url\":\"https://github.com/jgrandja\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jgrandja/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jgrandja/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jgrandja/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jgrandja/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jgrandja/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jgrandja/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jgrandja/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jgrandja/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jgrandja/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":1,\n" +
|
||||
" \"closed_issues\":0,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2021-03-31T11:29:17Z\",\n" +
|
||||
" \"updated_at\":\"2021-03-31T11:30:47Z\",\n" +
|
||||
" \"due_on\":null,\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/191\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191/labels\",\n" +
|
||||
" \"id\":5884208,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNTg4NDIwOA==\",\n" +
|
||||
" \"number\":191,\n" +
|
||||
" \"title\":\"5.5.0-RC1\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jzheaux\",\n" +
|
||||
" \"id\":3627351,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" +
|
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":21,\n" +
|
||||
" \"closed_issues\":23,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2020-09-16T13:28:03Z\",\n" +
|
||||
" \"updated_at\":\"2021-04-06T23:47:10Z\",\n" +
|
||||
" \"due_on\":\"" + Instant.now().toString() + "\",\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" }\n" +
|
||||
"]";
|
||||
this.server.enqueue(new MockResponse().setBody(responseJson));
|
||||
|
||||
boolean dueToday = this.github.isMilestoneDueToday(this.repositoryRef, "5.5.0-RC1");
|
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
|
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("get");
|
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/milestones?per_page=100");
|
||||
|
||||
assertThat(dueToday).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isMilestoneDueTodayWhenNoDueDateThenFalse() throws Exception {
|
||||
String responseJson = "[\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/207\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207/labels\",\n" +
|
||||
" \"id\":6611880,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNjYxMTg4MA==\",\n" +
|
||||
" \"number\":207,\n" +
|
||||
" \"title\":\"5.6.x\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jgrandja\",\n" +
|
||||
" \"id\":10884212,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjEwODg0MjEy\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/10884212?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jgrandja\",\n" +
|
||||
" \"html_url\":\"https://github.com/jgrandja\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jgrandja/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jgrandja/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jgrandja/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jgrandja/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jgrandja/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jgrandja/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jgrandja/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jgrandja/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jgrandja/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":1,\n" +
|
||||
" \"closed_issues\":0,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2021-03-31T11:29:17Z\",\n" +
|
||||
" \"updated_at\":\"2021-03-31T11:30:47Z\",\n" +
|
||||
" \"due_on\":null,\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/191\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191/labels\",\n" +
|
||||
" \"id\":5884208,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNTg4NDIwOA==\",\n" +
|
||||
" \"number\":191,\n" +
|
||||
" \"title\":\"5.5.0-RC1\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jzheaux\",\n" +
|
||||
" \"id\":3627351,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" +
|
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":21,\n" +
|
||||
" \"closed_issues\":23,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2020-09-16T13:28:03Z\",\n" +
|
||||
" \"updated_at\":\"2021-04-06T23:47:10Z\",\n" +
|
||||
" \"due_on\":null,\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" }\n" +
|
||||
"]";
|
||||
this.server.enqueue(new MockResponse().setBody(responseJson));
|
||||
|
||||
boolean dueToday = this.github.isMilestoneDueToday(this.repositoryRef, "5.5.0-RC1");
|
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
|
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("get");
|
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/milestones?per_page=100");
|
||||
|
||||
assertThat(dueToday).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isMilestoneDueTodayWhenDueDateInFutureThenFalse() throws Exception {
|
||||
String responseJson = "[\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/207\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207/labels\",\n" +
|
||||
" \"id\":6611880,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNjYxMTg4MA==\",\n" +
|
||||
" \"number\":207,\n" +
|
||||
" \"title\":\"5.6.x\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jgrandja\",\n" +
|
||||
" \"id\":10884212,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjEwODg0MjEy\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/10884212?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jgrandja\",\n" +
|
||||
" \"html_url\":\"https://github.com/jgrandja\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jgrandja/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jgrandja/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jgrandja/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jgrandja/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jgrandja/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jgrandja/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jgrandja/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jgrandja/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jgrandja/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":1,\n" +
|
||||
" \"closed_issues\":0,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2021-03-31T11:29:17Z\",\n" +
|
||||
" \"updated_at\":\"2021-03-31T11:30:47Z\",\n" +
|
||||
" \"due_on\":null,\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/191\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191/labels\",\n" +
|
||||
" \"id\":5884208,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNTg4NDIwOA==\",\n" +
|
||||
" \"number\":191,\n" +
|
||||
" \"title\":\"5.5.0-RC1\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jzheaux\",\n" +
|
||||
" \"id\":3627351,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" +
|
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":21,\n" +
|
||||
" \"closed_issues\":23,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2020-09-16T13:28:03Z\",\n" +
|
||||
" \"updated_at\":\"2021-04-06T23:47:10Z\",\n" +
|
||||
" \"due_on\":\"3000-04-12T07:00:00Z\",\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" }\n" +
|
||||
"]";
|
||||
this.server.enqueue(new MockResponse().setBody(responseJson));
|
||||
|
||||
boolean dueToday = this.github.isMilestoneDueToday(this.repositoryRef, "5.5.0-RC1");
|
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
|
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("get");
|
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/milestones?per_page=100");
|
||||
|
||||
assertThat(dueToday).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void calculateNextReleaseMilestoneWhenCurrentVersionIsNotSnapshotThenException() {
|
||||
assertThatExceptionOfType(RuntimeException.class)
|
||||
.isThrownBy(() -> this.github.getNextReleaseMilestone(this.repositoryRef, "5.5.0-RC1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void calculateNextReleaseMilestoneWhenPatchSegmentGreaterThan0ThenReturnsVersionWithoutSnapshot() {
|
||||
String nextVersion = this.github.getNextReleaseMilestone(this.repositoryRef, "5.5.1-SNAPSHOT");
|
||||
|
||||
assertThat(nextVersion).isEqualTo("5.5.1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void calculateNextReleaseMilestoneWhenMilestoneAndRcExistThenReturnsMilestone() throws Exception {
|
||||
String responseJson = "[\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/207\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207/labels\",\n" +
|
||||
" \"id\":6611880,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNjYxMTg4MA==\",\n" +
|
||||
" \"number\":207,\n" +
|
||||
" \"title\":\"5.5.0-M1\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jgrandja\",\n" +
|
||||
" \"id\":10884212,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjEwODg0MjEy\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/10884212?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jgrandja\",\n" +
|
||||
" \"html_url\":\"https://github.com/jgrandja\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jgrandja/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jgrandja/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jgrandja/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jgrandja/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jgrandja/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jgrandja/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jgrandja/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jgrandja/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jgrandja/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":1,\n" +
|
||||
" \"closed_issues\":0,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2021-03-31T11:29:17Z\",\n" +
|
||||
" \"updated_at\":\"2021-03-31T11:30:47Z\",\n" +
|
||||
" \"due_on\":null,\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/191\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191/labels\",\n" +
|
||||
" \"id\":5884208,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNTg4NDIwOA==\",\n" +
|
||||
" \"number\":191,\n" +
|
||||
" \"title\":\"5.5.0-RC1\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jzheaux\",\n" +
|
||||
" \"id\":3627351,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" +
|
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":21,\n" +
|
||||
" \"closed_issues\":23,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2020-09-16T13:28:03Z\",\n" +
|
||||
" \"updated_at\":\"2021-04-06T23:47:10Z\",\n" +
|
||||
" \"due_on\":\"3000-04-12T07:00:00Z\",\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" }\n" +
|
||||
"]";
|
||||
this.server.enqueue(new MockResponse().setBody(responseJson));
|
||||
|
||||
String nextVersion = this.github.getNextReleaseMilestone(this.repositoryRef, "5.5.0-SNAPSHOT");
|
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
|
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("get");
|
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/milestones?per_page=100");
|
||||
|
||||
assertThat(nextVersion).isEqualTo("5.5.0-M1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void calculateNextReleaseMilestoneWhenTwoMilestonesExistThenReturnsSmallerMilestone() throws Exception {
|
||||
String responseJson = "[\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/207\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207/labels\",\n" +
|
||||
" \"id\":6611880,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNjYxMTg4MA==\",\n" +
|
||||
" \"number\":207,\n" +
|
||||
" \"title\":\"5.5.0-M9\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jgrandja\",\n" +
|
||||
" \"id\":10884212,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjEwODg0MjEy\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/10884212?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jgrandja\",\n" +
|
||||
" \"html_url\":\"https://github.com/jgrandja\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jgrandja/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jgrandja/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jgrandja/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jgrandja/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jgrandja/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jgrandja/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jgrandja/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jgrandja/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jgrandja/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":1,\n" +
|
||||
" \"closed_issues\":0,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2021-03-31T11:29:17Z\",\n" +
|
||||
" \"updated_at\":\"2021-03-31T11:30:47Z\",\n" +
|
||||
" \"due_on\":null,\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/191\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191/labels\",\n" +
|
||||
" \"id\":5884208,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNTg4NDIwOA==\",\n" +
|
||||
" \"number\":191,\n" +
|
||||
" \"title\":\"5.5.0-M10\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jzheaux\",\n" +
|
||||
" \"id\":3627351,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" +
|
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":21,\n" +
|
||||
" \"closed_issues\":23,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2020-09-16T13:28:03Z\",\n" +
|
||||
" \"updated_at\":\"2021-04-06T23:47:10Z\",\n" +
|
||||
" \"due_on\":\"3000-04-12T07:00:00Z\",\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" }\n" +
|
||||
"]";
|
||||
this.server.enqueue(new MockResponse().setBody(responseJson));
|
||||
|
||||
String nextVersion = this.github.getNextReleaseMilestone(this.repositoryRef, "5.5.0-SNAPSHOT");
|
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
|
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("get");
|
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/milestones?per_page=100");
|
||||
|
||||
assertThat(nextVersion).isEqualTo("5.5.0-M9");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void calculateNextReleaseMilestoneWhenTwoRcsExistThenReturnsSmallerRc() throws Exception {
|
||||
String responseJson = "[\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/207\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207/labels\",\n" +
|
||||
" \"id\":6611880,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNjYxMTg4MA==\",\n" +
|
||||
" \"number\":207,\n" +
|
||||
" \"title\":\"5.5.0-RC9\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jgrandja\",\n" +
|
||||
" \"id\":10884212,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjEwODg0MjEy\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/10884212?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jgrandja\",\n" +
|
||||
" \"html_url\":\"https://github.com/jgrandja\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jgrandja/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jgrandja/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jgrandja/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jgrandja/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jgrandja/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jgrandja/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jgrandja/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jgrandja/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jgrandja/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":1,\n" +
|
||||
" \"closed_issues\":0,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2021-03-31T11:29:17Z\",\n" +
|
||||
" \"updated_at\":\"2021-03-31T11:30:47Z\",\n" +
|
||||
" \"due_on\":null,\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/191\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191/labels\",\n" +
|
||||
" \"id\":5884208,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNTg4NDIwOA==\",\n" +
|
||||
" \"number\":191,\n" +
|
||||
" \"title\":\"5.5.0-RC10\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jzheaux\",\n" +
|
||||
" \"id\":3627351,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" +
|
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":21,\n" +
|
||||
" \"closed_issues\":23,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2020-09-16T13:28:03Z\",\n" +
|
||||
" \"updated_at\":\"2021-04-06T23:47:10Z\",\n" +
|
||||
" \"due_on\":\"3000-04-12T07:00:00Z\",\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" }\n" +
|
||||
"]";
|
||||
this.server.enqueue(new MockResponse().setBody(responseJson));
|
||||
|
||||
String nextVersion = this.github.getNextReleaseMilestone(this.repositoryRef, "5.5.0-SNAPSHOT");
|
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
|
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("get");
|
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/milestones?per_page=100");
|
||||
|
||||
assertThat(nextVersion).isEqualTo("5.5.0-RC9");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void calculateNextReleaseMilestoneWhenNoPreReleaseThenReturnsVersionWithoutSnapshot() throws Exception {
|
||||
String responseJson = "[\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/207\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/207/labels\",\n" +
|
||||
" \"id\":6611880,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNjYxMTg4MA==\",\n" +
|
||||
" \"number\":207,\n" +
|
||||
" \"title\":\"5.6.x\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jgrandja\",\n" +
|
||||
" \"id\":10884212,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjEwODg0MjEy\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/10884212?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jgrandja\",\n" +
|
||||
" \"html_url\":\"https://github.com/jgrandja\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jgrandja/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jgrandja/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jgrandja/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jgrandja/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jgrandja/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jgrandja/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jgrandja/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jgrandja/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jgrandja/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":1,\n" +
|
||||
" \"closed_issues\":0,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2021-03-31T11:29:17Z\",\n" +
|
||||
" \"updated_at\":\"2021-03-31T11:30:47Z\",\n" +
|
||||
" \"due_on\":null,\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" },\n" +
|
||||
" {\n" +
|
||||
" \"url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191\",\n" +
|
||||
" \"html_url\":\"https://github.com/spring-projects/spring-security/milestone/191\",\n" +
|
||||
" \"labels_url\":\"https://api.github.com/repos/spring-projects/spring-security/milestones/191/labels\",\n" +
|
||||
" \"id\":5884208,\n" +
|
||||
" \"node_id\":\"MDk6TWlsZXN0b25lNTg4NDIwOA==\",\n" +
|
||||
" \"number\":191,\n" +
|
||||
" \"title\":\"5.4.3\",\n" +
|
||||
" \"description\":\"\",\n" +
|
||||
" \"creator\":{\n" +
|
||||
" \"login\":\"jzheaux\",\n" +
|
||||
" \"id\":3627351,\n" +
|
||||
" \"node_id\":\"MDQ6VXNlcjM2MjczNTE=\",\n" +
|
||||
" \"avatar_url\":\"https://avatars.githubusercontent.com/u/3627351?v=4\",\n" +
|
||||
" \"gravatar_id\":\"\",\n" +
|
||||
" \"url\":\"https://api.github.com/users/jzheaux\",\n" +
|
||||
" \"html_url\":\"https://github.com/jzheaux\",\n" +
|
||||
" \"followers_url\":\"https://api.github.com/users/jzheaux/followers\",\n" +
|
||||
" \"following_url\":\"https://api.github.com/users/jzheaux/following{/other_user}\",\n" +
|
||||
" \"gists_url\":\"https://api.github.com/users/jzheaux/gists{/gist_id}\",\n" +
|
||||
" \"starred_url\":\"https://api.github.com/users/jzheaux/starred{/owner}{/repo}\",\n" +
|
||||
" \"subscriptions_url\":\"https://api.github.com/users/jzheaux/subscriptions\",\n" +
|
||||
" \"organizations_url\":\"https://api.github.com/users/jzheaux/orgs\",\n" +
|
||||
" \"repos_url\":\"https://api.github.com/users/jzheaux/repos\",\n" +
|
||||
" \"events_url\":\"https://api.github.com/users/jzheaux/events{/privacy}\",\n" +
|
||||
" \"received_events_url\":\"https://api.github.com/users/jzheaux/received_events\",\n" +
|
||||
" \"type\":\"User\",\n" +
|
||||
" \"site_admin\":false\n" +
|
||||
" },\n" +
|
||||
" \"open_issues\":21,\n" +
|
||||
" \"closed_issues\":23,\n" +
|
||||
" \"state\":\"open\",\n" +
|
||||
" \"created_at\":\"2020-09-16T13:28:03Z\",\n" +
|
||||
" \"updated_at\":\"2021-04-06T23:47:10Z\",\n" +
|
||||
" \"due_on\":\"2021-04-12T07:00:00Z\",\n" +
|
||||
" \"closed_at\":null\n" +
|
||||
" }\n" +
|
||||
"]";
|
||||
this.server.enqueue(new MockResponse().setBody(responseJson));
|
||||
|
||||
String nextVersion = this.github.getNextReleaseMilestone(this.repositoryRef, "5.5.0-SNAPSHOT");
|
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
|
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("get");
|
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/milestones?per_page=100");
|
||||
|
||||
assertThat(nextVersion).isEqualTo("5.5.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMilestoneWhenValidParametersThenSuccess() throws Exception {
|
||||
this.server.enqueue(new MockResponse().setResponseCode(204));
|
||||
Milestone milestone = new Milestone();
|
||||
milestone.setTitle("1.0.0");
|
||||
milestone.setDueOn(LocalDate.of(2022, 5, 4).atTime(LocalTime.NOON));
|
||||
this.github.createMilestone(this.repositoryRef, milestone);
|
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
|
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("post");
|
||||
assertThat(recordedRequest.getRequestUrl().toString())
|
||||
.isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/milestones");
|
||||
assertThat(recordedRequest.getBody().readString(Charset.defaultCharset()))
|
||||
.isEqualTo("{\"title\":\"1.0.0\",\"due_on\":\"2022-05-04T12:00:00Z\"}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createMilestoneWhenErrorResponseThenException() throws Exception {
|
||||
this.server.enqueue(new MockResponse().setResponseCode(400));
|
||||
assertThatExceptionOfType(RuntimeException.class)
|
||||
.isThrownBy(() -> this.github.createMilestone(this.repositoryRef, new Milestone()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+245
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.github.milestones;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.Year;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import org.springframework.gradle.github.milestones.SpringReleaseTrainSpec.Train;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Steve Riesenberg
|
||||
*/
|
||||
public class SpringReleaseTrainTests {
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"2019-12-31, ONE, 2020",
|
||||
"2020-01-01, ONE, 2020",
|
||||
"2020-01-31, ONE, 2020",
|
||||
"2020-02-01, TWO, 2020",
|
||||
"2020-07-31, TWO, 2020",
|
||||
"2020-08-01, ONE, 2021"
|
||||
})
|
||||
public void nextTrainWhenBoundaryConditionsThenSuccess(LocalDate startDate, Train expectedTrain, Year expectedYear) {
|
||||
SpringReleaseTrainSpec releaseTrainSpec =
|
||||
SpringReleaseTrainSpec.builder()
|
||||
.nextTrain(startDate)
|
||||
.version("1.0.0")
|
||||
.weekOfMonth(2)
|
||||
.dayOfWeek(2)
|
||||
.build();
|
||||
assertThat(releaseTrainSpec.getTrain()).isEqualTo(expectedTrain);
|
||||
assertThat(releaseTrainSpec.getYear()).isEqualTo(expectedYear);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTrainDatesWhenTrainOneIsSecondTuesdayOf2020ThenSuccess() {
|
||||
SpringReleaseTrainSpec releaseTrainSpec =
|
||||
SpringReleaseTrainSpec.builder()
|
||||
.train(1)
|
||||
.version("1.0.0")
|
||||
.weekOfMonth(2)
|
||||
.dayOfWeek(2)
|
||||
.year(2020)
|
||||
.build();
|
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec);
|
||||
Map<String, LocalDate> trainDates = releaseTrain.getTrainDates();
|
||||
assertThat(trainDates).hasSize(5);
|
||||
assertThat(trainDates.get("1.0.0-M1")).isEqualTo(LocalDate.of(2020, 1, 14));
|
||||
assertThat(trainDates.get("1.0.0-M2")).isEqualTo(LocalDate.of(2020, 2, 11));
|
||||
assertThat(trainDates.get("1.0.0-M3")).isEqualTo(LocalDate.of(2020, 3, 10));
|
||||
assertThat(trainDates.get("1.0.0-RC1")).isEqualTo(LocalDate.of(2020, 4, 14));
|
||||
assertThat(trainDates.get("1.0.0")).isEqualTo(LocalDate.of(2020, 5, 12));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTrainDatesWhenTrainTwoIsSecondTuesdayOf2020ThenSuccess() {
|
||||
SpringReleaseTrainSpec releaseTrainSpec =
|
||||
SpringReleaseTrainSpec.builder()
|
||||
.train(2)
|
||||
.version("1.0.0")
|
||||
.weekOfMonth(2)
|
||||
.dayOfWeek(2)
|
||||
.year(2020)
|
||||
.build();
|
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec);
|
||||
Map<String, LocalDate> trainDates = releaseTrain.getTrainDates();
|
||||
assertThat(trainDates).hasSize(5);
|
||||
assertThat(trainDates.get("1.0.0-M1")).isEqualTo(LocalDate.of(2020, 7, 14));
|
||||
assertThat(trainDates.get("1.0.0-M2")).isEqualTo(LocalDate.of(2020, 8, 11));
|
||||
assertThat(trainDates.get("1.0.0-M3")).isEqualTo(LocalDate.of(2020, 9, 15));
|
||||
assertThat(trainDates.get("1.0.0-RC1")).isEqualTo(LocalDate.of(2020, 10, 13));
|
||||
assertThat(trainDates.get("1.0.0")).isEqualTo(LocalDate.of(2020, 11, 10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTrainDatesWhenTrainOneIsSecondTuesdayOf2022ThenSuccess() {
|
||||
SpringReleaseTrainSpec releaseTrainSpec =
|
||||
SpringReleaseTrainSpec.builder()
|
||||
.train(1)
|
||||
.version("1.0.0")
|
||||
.weekOfMonth(2)
|
||||
.dayOfWeek(2)
|
||||
.year(2022)
|
||||
.build();
|
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec);
|
||||
Map<String, LocalDate> trainDates = releaseTrain.getTrainDates();
|
||||
assertThat(trainDates).hasSize(5);
|
||||
assertThat(trainDates.get("1.0.0-M1")).isEqualTo(LocalDate.of(2022, 1, 11));
|
||||
assertThat(trainDates.get("1.0.0-M2")).isEqualTo(LocalDate.of(2022, 2, 15));
|
||||
assertThat(trainDates.get("1.0.0-M3")).isEqualTo(LocalDate.of(2022, 3, 15));
|
||||
assertThat(trainDates.get("1.0.0-RC1")).isEqualTo(LocalDate.of(2022, 4, 12));
|
||||
assertThat(trainDates.get("1.0.0")).isEqualTo(LocalDate.of(2022, 5, 10));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTrainDatesWhenTrainTwoIsSecondTuesdayOf2022ThenSuccess() {
|
||||
SpringReleaseTrainSpec releaseTrainSpec =
|
||||
SpringReleaseTrainSpec.builder()
|
||||
.train(2)
|
||||
.version("1.0.0")
|
||||
.weekOfMonth(2)
|
||||
.dayOfWeek(2)
|
||||
.year(2022)
|
||||
.build();
|
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec);
|
||||
Map<String, LocalDate> trainDates = releaseTrain.getTrainDates();
|
||||
assertThat(trainDates).hasSize(5);
|
||||
assertThat(trainDates.get("1.0.0-M1")).isEqualTo(LocalDate.of(2022, 7, 12));
|
||||
assertThat(trainDates.get("1.0.0-M2")).isEqualTo(LocalDate.of(2022, 8, 9));
|
||||
assertThat(trainDates.get("1.0.0-M3")).isEqualTo(LocalDate.of(2022, 9, 13));
|
||||
assertThat(trainDates.get("1.0.0-RC1")).isEqualTo(LocalDate.of(2022, 10, 11));
|
||||
assertThat(trainDates.get("1.0.0")).isEqualTo(LocalDate.of(2022, 11, 15));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTrainDatesWhenTrainOneIsThirdMondayOf2022ThenSuccess() {
|
||||
SpringReleaseTrainSpec releaseTrainSpec =
|
||||
SpringReleaseTrainSpec.builder()
|
||||
.train(1)
|
||||
.version("1.0.0")
|
||||
.weekOfMonth(3)
|
||||
.dayOfWeek(1)
|
||||
.year(2022)
|
||||
.build();
|
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec);
|
||||
Map<String, LocalDate> trainDates = releaseTrain.getTrainDates();
|
||||
assertThat(trainDates).hasSize(5);
|
||||
assertThat(trainDates.get("1.0.0-M1")).isEqualTo(LocalDate.of(2022, 1, 17));
|
||||
assertThat(trainDates.get("1.0.0-M2")).isEqualTo(LocalDate.of(2022, 2, 21));
|
||||
assertThat(trainDates.get("1.0.0-M3")).isEqualTo(LocalDate.of(2022, 3, 21));
|
||||
assertThat(trainDates.get("1.0.0-RC1")).isEqualTo(LocalDate.of(2022, 4, 18));
|
||||
assertThat(trainDates.get("1.0.0")).isEqualTo(LocalDate.of(2022, 5, 16));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTrainDatesWhenTrainTwoIsThirdMondayOf2022ThenSuccess() {
|
||||
SpringReleaseTrainSpec releaseTrainSpec =
|
||||
SpringReleaseTrainSpec.builder()
|
||||
.train(2)
|
||||
.version("1.0.0")
|
||||
.weekOfMonth(3)
|
||||
.dayOfWeek(1)
|
||||
.year(2022)
|
||||
.build();
|
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec);
|
||||
Map<String, LocalDate> trainDates = releaseTrain.getTrainDates();
|
||||
assertThat(trainDates).hasSize(5);
|
||||
assertThat(trainDates.get("1.0.0-M1")).isEqualTo(LocalDate.of(2022, 7, 18));
|
||||
assertThat(trainDates.get("1.0.0-M2")).isEqualTo(LocalDate.of(2022, 8, 15));
|
||||
assertThat(trainDates.get("1.0.0-M3")).isEqualTo(LocalDate.of(2022, 9, 19));
|
||||
assertThat(trainDates.get("1.0.0-RC1")).isEqualTo(LocalDate.of(2022, 10, 17));
|
||||
assertThat(trainDates.get("1.0.0")).isEqualTo(LocalDate.of(2022, 11, 21));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isTrainDateWhenTrainOneIsThirdMondayOf2022ThenSuccess() {
|
||||
SpringReleaseTrainSpec releaseTrainSpec =
|
||||
SpringReleaseTrainSpec.builder()
|
||||
.train(1)
|
||||
.version("1.0.0")
|
||||
.weekOfMonth(3)
|
||||
.dayOfWeek(1)
|
||||
.year(2022)
|
||||
.build();
|
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec);
|
||||
for (int dayOfMonth = 1; dayOfMonth <= 31; dayOfMonth++) {
|
||||
assertThat(releaseTrain.isTrainDate("1.0.0-M1", LocalDate.of(2022, 1, dayOfMonth))).isEqualTo(dayOfMonth == 17);
|
||||
}
|
||||
for (int dayOfMonth = 1; dayOfMonth <= 28; dayOfMonth++) {
|
||||
assertThat(releaseTrain.isTrainDate("1.0.0-M2", LocalDate.of(2022, 2, dayOfMonth))).isEqualTo(dayOfMonth == 21);
|
||||
}
|
||||
for (int dayOfMonth = 1; dayOfMonth <= 31; dayOfMonth++) {
|
||||
assertThat(releaseTrain.isTrainDate("1.0.0-M3", LocalDate.of(2022, 3, dayOfMonth))).isEqualTo(dayOfMonth == 21);
|
||||
}
|
||||
for (int dayOfMonth = 1; dayOfMonth <= 30; dayOfMonth++) {
|
||||
assertThat(releaseTrain.isTrainDate("1.0.0-RC1", LocalDate.of(2022, 4, dayOfMonth))).isEqualTo(dayOfMonth == 18);
|
||||
}
|
||||
for (int dayOfMonth = 1; dayOfMonth <= 31; dayOfMonth++) {
|
||||
assertThat(releaseTrain.isTrainDate("1.0.0", LocalDate.of(2022, 5, dayOfMonth))).isEqualTo(dayOfMonth == 16);
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource({
|
||||
"2022-01-01, 2022-02-21",
|
||||
"2022-02-01, 2022-02-21",
|
||||
"2022-02-21, 2022-04-18",
|
||||
"2022-03-01, 2022-04-18",
|
||||
"2022-04-01, 2022-04-18",
|
||||
"2022-04-18, 2022-06-20",
|
||||
"2022-05-01, 2022-06-20",
|
||||
"2022-06-01, 2022-06-20",
|
||||
"2022-06-20, 2022-08-15",
|
||||
"2022-07-01, 2022-08-15",
|
||||
"2022-08-01, 2022-08-15",
|
||||
"2022-08-15, 2022-10-17",
|
||||
"2022-09-01, 2022-10-17",
|
||||
"2022-10-01, 2022-10-17",
|
||||
"2022-10-17, 2022-12-19",
|
||||
"2022-11-01, 2022-12-19",
|
||||
"2022-12-01, 2022-12-19",
|
||||
"2022-12-19, 2023-02-20"
|
||||
})
|
||||
public void getNextReleaseDateWhenBoundaryConditionsThenSuccess(LocalDate startDate, LocalDate expectedDate) {
|
||||
SpringReleaseTrainSpec releaseTrainSpec =
|
||||
SpringReleaseTrainSpec.builder()
|
||||
.train(1)
|
||||
.version("1.0.0")
|
||||
.weekOfMonth(3)
|
||||
.dayOfWeek(1)
|
||||
.year(2022)
|
||||
.build();
|
||||
|
||||
SpringReleaseTrain releaseTrain = new SpringReleaseTrain(releaseTrainSpec);
|
||||
assertThat(releaseTrain.getNextReleaseDate(startDate)).isEqualTo(expectedDate);
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.gradle.github.release;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import okhttp3.mockwebserver.RecordedRequest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.gradle.github.RepositoryRef;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Steve Riesenberg
|
||||
*/
|
||||
public class GitHubActionsApiTests {
|
||||
private GitHubActionsApi gitHubActionsApi;
|
||||
|
||||
private MockWebServer server;
|
||||
|
||||
private String baseUrl;
|
||||
|
||||
private RepositoryRef repository;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
this.server = new MockWebServer();
|
||||
this.server.start();
|
||||
this.baseUrl = this.server.url("/api").toString();
|
||||
this.gitHubActionsApi = new GitHubActionsApi("mock-oauth-token");
|
||||
this.gitHubActionsApi.setBaseUrl(this.baseUrl);
|
||||
this.repository = new RepositoryRef("spring-projects", "spring-security");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanup() throws Exception {
|
||||
this.server.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dispatchWorkflowWhenValidParametersThenSuccess() throws Exception {
|
||||
this.server.enqueue(new MockResponse().setResponseCode(204));
|
||||
|
||||
Map<String, Object> inputs = new LinkedHashMap<>();
|
||||
inputs.put("input-1", "value");
|
||||
inputs.put("input-2", false);
|
||||
WorkflowDispatch workflowDispatch = new WorkflowDispatch("main", inputs);
|
||||
this.gitHubActionsApi.dispatchWorkflow(this.repository, "test-workflow.yml", workflowDispatch);
|
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
|
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("post");
|
||||
assertThat(recordedRequest.getRequestUrl().toString())
|
||||
.isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/actions/workflows/test-workflow.yml/dispatches");
|
||||
assertThat(recordedRequest.getBody().readString(Charset.defaultCharset()))
|
||||
.isEqualTo("{\"ref\":\"main\",\"inputs\":{\"input-1\":\"value\",\"input-2\":false}}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dispatchWorkflowWhenErrorResponseThenException() throws Exception {
|
||||
this.server.enqueue(new MockResponse().setResponseCode(400));
|
||||
|
||||
WorkflowDispatch workflowDispatch = new WorkflowDispatch("main", null);
|
||||
assertThatExceptionOfType(RuntimeException.class)
|
||||
.isThrownBy(() -> this.gitHubActionsApi.dispatchWorkflow(this.repository, "test-workflow.yml", workflowDispatch));
|
||||
}
|
||||
}
|
||||
+15
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,14 +16,15 @@
|
||||
|
||||
package org.springframework.gradle.github.release;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import okhttp3.mockwebserver.RecordedRequest;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.gradle.github.RepositoryRef;
|
||||
|
||||
@@ -34,21 +35,22 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
* @author Steve Riesenberg
|
||||
*/
|
||||
public class GitHubReleaseApiTests {
|
||||
private GitHubReleaseApi github;
|
||||
|
||||
private RepositoryRef repository = new RepositoryRef("spring-projects", "spring-security");
|
||||
private GitHubReleaseApi gitHubReleaseApi;
|
||||
|
||||
private MockWebServer server;
|
||||
|
||||
private String baseUrl;
|
||||
|
||||
private RepositoryRef repository;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws Exception {
|
||||
this.server = new MockWebServer();
|
||||
this.server.start();
|
||||
this.github = new GitHubReleaseApi("mock-oauth-token");
|
||||
this.baseUrl = this.server.url("/api").toString();
|
||||
this.github.setBaseUrl(this.baseUrl);
|
||||
this.gitHubReleaseApi = new GitHubReleaseApi("mock-oauth-token");
|
||||
this.gitHubReleaseApi.setBaseUrl(this.baseUrl);
|
||||
this.repository = new RepositoryRef("spring-projects", "spring-security");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@@ -134,18 +136,20 @@ public class GitHubReleaseApiTests {
|
||||
" ]\n" +
|
||||
"}";
|
||||
this.server.enqueue(new MockResponse().setBody(responseJson));
|
||||
this.github.publishRelease(this.repository, Release.tag("1.0.0").build());
|
||||
this.gitHubReleaseApi.publishRelease(this.repository, Release.tag("1.0.0").build());
|
||||
|
||||
RecordedRequest recordedRequest = this.server.takeRequest(1, TimeUnit.SECONDS);
|
||||
assertThat(recordedRequest.getMethod()).isEqualToIgnoringCase("post");
|
||||
assertThat(recordedRequest.getRequestUrl().toString()).isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/releases");
|
||||
assertThat(recordedRequest.getBody().toString()).isEqualTo("{\"tag_name\":\"1.0.0\"}");
|
||||
assertThat(recordedRequest.getRequestUrl().toString())
|
||||
.isEqualTo(this.baseUrl + "/repos/spring-projects/spring-security/releases");
|
||||
assertThat(recordedRequest.getBody().readString(Charset.defaultCharset()))
|
||||
.isEqualTo("{\"tag_name\":\"1.0.0\",\"draft\":false,\"prerelease\":false,\"generate_release_notes\":false}");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void publishReleaseWhenErrorResponseThenException() throws Exception {
|
||||
this.server.enqueue(new MockResponse().setResponseCode(400));
|
||||
assertThatExceptionOfType(RuntimeException.class)
|
||||
.isThrownBy(() -> this.github.publishRelease(this.repository, Release.tag("1.0.0").build()));
|
||||
.isThrownBy(() -> this.gitHubReleaseApi.publishRelease(this.repository, Release.tag("1.0.0").build()));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-19
@@ -42,8 +42,6 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.context.NullSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -207,8 +205,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
||||
|
||||
private AuthenticationFailureHandler proxyFailureHandler = new SimpleUrlAuthenticationFailureHandler();
|
||||
|
||||
private SecurityContextRepository securityContextRepository = new NullSecurityContextRepository();
|
||||
|
||||
public CasAuthenticationFilter() {
|
||||
super("/login/cas");
|
||||
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
|
||||
@@ -227,7 +223,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
||||
SecurityContext context = SecurityContextHolder.createEmptyContext();
|
||||
context.setAuthentication(authResult);
|
||||
SecurityContextHolder.setContext(context);
|
||||
this.securityContextRepository.saveContext(context, request, response);
|
||||
if (this.eventPublisher != null) {
|
||||
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
|
||||
}
|
||||
@@ -251,8 +246,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
||||
this.logger.debug("Failed to obtain an artifact (cas ticket)");
|
||||
password = "";
|
||||
}
|
||||
UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
|
||||
password);
|
||||
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
|
||||
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
|
||||
return this.getAuthenticationManager().authenticate(authRequest);
|
||||
}
|
||||
@@ -280,18 +274,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link SecurityContextRepository} to save the {@link SecurityContext} on
|
||||
* authentication success. The default action is not to save the
|
||||
* {@link SecurityContext}.
|
||||
* @param securityContextRepository the {@link SecurityContextRepository} to use.
|
||||
* Cannot be null.
|
||||
*/
|
||||
public void setSecurityContextRepository(SecurityContextRepository securityContextRepository) {
|
||||
Assert.notNull(securityContextRepository, "securityContextRepository cannot be null");
|
||||
this.securityContextRepository = securityContextRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link AuthenticationFailureHandler} for proxy requests.
|
||||
* @param proxyFailureHandler
|
||||
|
||||
+12
-12
@@ -87,8 +87,8 @@ public class CasAuthenticationProviderTests {
|
||||
cap.setServiceProperties(makeServiceProperties());
|
||||
cap.setTicketValidator(new MockTicketValidator(true));
|
||||
cap.afterPropertiesSet();
|
||||
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken
|
||||
.unauthenticated(CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, "ST-123");
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
|
||||
CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, "ST-123");
|
||||
token.setDetails("details");
|
||||
Authentication result = cap.authenticate(token);
|
||||
// Confirm ST-123 was NOT added to the cache
|
||||
@@ -120,8 +120,8 @@ public class CasAuthenticationProviderTests {
|
||||
cap.setTicketValidator(new MockTicketValidator(true));
|
||||
cap.setServiceProperties(makeServiceProperties());
|
||||
cap.afterPropertiesSet();
|
||||
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken
|
||||
.unauthenticated(CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, "ST-456");
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
|
||||
CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, "ST-456");
|
||||
token.setDetails("details");
|
||||
Authentication result = cap.authenticate(token);
|
||||
// Confirm ST-456 was added to the cache
|
||||
@@ -157,8 +157,8 @@ public class CasAuthenticationProviderTests {
|
||||
cap.setServiceProperties(serviceProperties);
|
||||
cap.afterPropertiesSet();
|
||||
String ticket = "ST-456";
|
||||
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken
|
||||
.unauthenticated(CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
|
||||
CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
|
||||
Authentication result = cap.authenticate(token);
|
||||
}
|
||||
|
||||
@@ -178,8 +178,8 @@ public class CasAuthenticationProviderTests {
|
||||
cap.setServiceProperties(serviceProperties);
|
||||
cap.afterPropertiesSet();
|
||||
String ticket = "ST-456";
|
||||
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken
|
||||
.unauthenticated(CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
|
||||
CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER, ticket);
|
||||
Authentication result = cap.authenticate(token);
|
||||
verify(validator).validate(ticket, serviceProperties.getService());
|
||||
serviceProperties.setAuthenticateAllArtifacts(true);
|
||||
@@ -211,8 +211,8 @@ public class CasAuthenticationProviderTests {
|
||||
cap.setTicketValidator(new MockTicketValidator(true));
|
||||
cap.setServiceProperties(makeServiceProperties());
|
||||
cap.afterPropertiesSet();
|
||||
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken
|
||||
.unauthenticated(CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, "");
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
|
||||
CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER, "");
|
||||
assertThatExceptionOfType(BadCredentialsException.class).isThrownBy(() -> cap.authenticate(token));
|
||||
}
|
||||
|
||||
@@ -314,8 +314,8 @@ public class CasAuthenticationProviderTests {
|
||||
cap.setTicketValidator(new MockTicketValidator(true));
|
||||
cap.setServiceProperties(makeServiceProperties());
|
||||
cap.afterPropertiesSet();
|
||||
UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken
|
||||
.authenticated("some_normal_user", "password", AuthorityUtils.createAuthorityList("ROLE_A"));
|
||||
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("some_normal_user",
|
||||
"password", AuthorityUtils.createAuthorityList("ROLE_A"));
|
||||
assertThat(cap.authenticate(token)).isNull();
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -121,8 +121,8 @@ public class CasAuthenticationTokenTests {
|
||||
final Assertion assertion = new AssertionImpl("test");
|
||||
CasAuthenticationToken token1 = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES,
|
||||
makeUserDetails(), assertion);
|
||||
UsernamePasswordAuthenticationToken token2 = UsernamePasswordAuthenticationToken.authenticated("Test",
|
||||
"Password", this.ROLES);
|
||||
UsernamePasswordAuthenticationToken token2 = new UsernamePasswordAuthenticationToken("Test", "Password",
|
||||
this.ROLES);
|
||||
assertThat(!token1.equals(token2)).isTrue();
|
||||
}
|
||||
|
||||
|
||||
-36
@@ -21,7 +21,6 @@ import javax.servlet.FilterChain;
|
||||
import org.jasig.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
@@ -33,15 +32,12 @@ import org.springframework.security.cas.ServiceProperties;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -186,38 +182,6 @@ public class CasAuthenticationFilterTests {
|
||||
verify(successHandler).onAuthenticationSuccess(request, response, authentication);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecurityContextHolder() throws Exception {
|
||||
SecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);
|
||||
AuthenticationManager manager = mock(AuthenticationManager.class);
|
||||
Authentication authentication = new TestingAuthenticationToken("un", "pwd", "ROLE_USER");
|
||||
given(manager.authenticate(any(Authentication.class))).willReturn(authentication);
|
||||
ServiceProperties serviceProperties = new ServiceProperties();
|
||||
serviceProperties.setAuthenticateAllArtifacts(true);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setParameter("ticket", "ST-1-123");
|
||||
request.setServletPath("/authenticate");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
FilterChain chain = mock(FilterChain.class);
|
||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||
filter.setServiceProperties(serviceProperties);
|
||||
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
|
||||
filter.setAuthenticationManager(manager);
|
||||
filter.setSecurityContextRepository(securityContextRepository);
|
||||
filter.afterPropertiesSet();
|
||||
filter.doFilter(request, response, chain);
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNotNull()
|
||||
.withFailMessage("Authentication should not be null");
|
||||
verify(chain).doFilter(request, response);
|
||||
// validate for when the filterProcessUrl matches
|
||||
filter.setFilterProcessesUrl(request.getServletPath());
|
||||
SecurityContextHolder.clearContext();
|
||||
filter.doFilter(request, response, chain);
|
||||
ArgumentCaptor<SecurityContext> contextArg = ArgumentCaptor.forClass(SecurityContext.class);
|
||||
verify(securityContextRepository).saveContext(contextArg.capture(), eq(request), eq(response));
|
||||
assertThat(contextArg.getValue().getAuthentication().getPrincipal()).isEqualTo(authentication.getName());
|
||||
}
|
||||
|
||||
// SEC-1592
|
||||
@Test
|
||||
public void testChainNotInvokedForProxyReceptor() throws Exception {
|
||||
|
||||
@@ -76,7 +76,6 @@ dependencies {
|
||||
testImplementation "org.apache.directory.server:apacheds-protocol-ldap"
|
||||
testImplementation "org.apache.directory.server:apacheds-server-jndi"
|
||||
testImplementation 'org.apache.directory.shared:shared-ldap'
|
||||
testImplementation "com.unboundid:unboundid-ldapsdk"
|
||||
testImplementation 'org.eclipse.persistence:javax.persistence'
|
||||
testImplementation('org.hibernate:hibernate-entitymanager') {
|
||||
exclude group: 'javax.activation', module: 'javax.activation-api'
|
||||
|
||||
-185
@@ -1,185 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.ldap;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.UnsatisfiedDependencyException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.ldap.core.support.LdapContextSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.test.SpringTestContext;
|
||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
|
||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
||||
|
||||
@ExtendWith(SpringTestContextExtension.class)
|
||||
public class EmbeddedLdapServerContextSourceFactoryBeanITests {
|
||||
|
||||
public final SpringTestContext spring = new SpringTestContext(this);
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void contextSourceFactoryBeanWhenEmbeddedServerThenAuthenticates() throws Exception {
|
||||
this.spring.register(FromEmbeddedLdapServerConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
|
||||
.andExpect(authenticated().withUsername("bob"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextSourceFactoryBeanWhenPortZeroThenAuthenticates() throws Exception {
|
||||
this.spring.register(PortZeroConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
|
||||
.andExpect(authenticated().withUsername("bob"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextSourceFactoryBeanWhenCustomLdifAndRootThenAuthenticates() throws Exception {
|
||||
this.spring.register(CustomLdifAndRootConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("pg").password("password")).andExpect(authenticated().withUsername("pg"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextSourceFactoryBeanWhenCustomManagerDnThenAuthenticates() throws Exception {
|
||||
this.spring.register(CustomManagerDnConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
|
||||
.andExpect(authenticated().withUsername("bob"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contextSourceFactoryBeanWhenManagerDnAndNoPasswordThenException() {
|
||||
assertThatExceptionOfType(UnsatisfiedDependencyException.class)
|
||||
.isThrownBy(() -> this.spring.register(CustomManagerDnNoPasswordConfig.class).autowire())
|
||||
.withRootCauseInstanceOf(IllegalStateException.class)
|
||||
.withMessageContaining("managerPassword is required if managerDn is supplied");
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class FromEmbeddedLdapServerConfig {
|
||||
|
||||
@Bean
|
||||
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
|
||||
return EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
AuthenticationManager authenticationManager(LdapContextSource contextSource) {
|
||||
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
|
||||
factory.setUserDnPatterns("uid={0},ou=people");
|
||||
return factory.createAuthenticationManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class PortZeroConfig {
|
||||
|
||||
@Bean
|
||||
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
|
||||
EmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean
|
||||
.fromEmbeddedLdapServer();
|
||||
factoryBean.setPort(0);
|
||||
return factoryBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
AuthenticationManager authenticationManager(LdapContextSource contextSource) {
|
||||
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
|
||||
factory.setUserDnPatterns("uid={0},ou=people");
|
||||
return factory.createAuthenticationManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomLdifAndRootConfig {
|
||||
|
||||
@Bean
|
||||
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
|
||||
EmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean
|
||||
.fromEmbeddedLdapServer();
|
||||
factoryBean.setLdif("classpath*:test-server2.xldif");
|
||||
factoryBean.setRoot("dc=monkeymachine,dc=co,dc=uk");
|
||||
return factoryBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
AuthenticationManager authenticationManager(LdapContextSource contextSource) {
|
||||
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
|
||||
factory.setUserDnPatterns("uid={0},ou=gorillas");
|
||||
return factory.createAuthenticationManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomManagerDnConfig {
|
||||
|
||||
@Bean
|
||||
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
|
||||
EmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean
|
||||
.fromEmbeddedLdapServer();
|
||||
factoryBean.setManagerDn("uid=admin,ou=system");
|
||||
factoryBean.setManagerPassword("secret");
|
||||
return factoryBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
AuthenticationManager authenticationManager(LdapContextSource contextSource) {
|
||||
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(
|
||||
contextSource, NoOpPasswordEncoder.getInstance());
|
||||
factory.setUserDnPatterns("uid={0},ou=people");
|
||||
return factory.createAuthenticationManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomManagerDnNoPasswordConfig {
|
||||
|
||||
@Bean
|
||||
EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
|
||||
EmbeddedLdapServerContextSourceFactoryBean factoryBean = EmbeddedLdapServerContextSourceFactoryBean
|
||||
.fromEmbeddedLdapServer();
|
||||
factoryBean.setManagerDn("uid=admin,ou=system");
|
||||
return factoryBean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
AuthenticationManager authenticationManager(LdapContextSource contextSource) {
|
||||
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(
|
||||
contextSource, NoOpPasswordEncoder.getInstance());
|
||||
factory.setUserDnPatterns("uid={0},ou=people");
|
||||
return factory.createAuthenticationManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-241
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.ldap;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.ldap.core.DirContextAdapter;
|
||||
import org.springframework.ldap.core.DirContextOperations;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.ldap.core.support.LdapContextSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.test.SpringTestContext;
|
||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||
import org.springframework.security.ldap.server.ApacheDSContainer;
|
||||
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
|
||||
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
|
||||
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
|
||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
||||
|
||||
@ExtendWith(SpringTestContextExtension.class)
|
||||
public class LdapBindAuthenticationManagerFactoryITests {
|
||||
|
||||
public final SpringTestContext spring = new SpringTestContext(this);
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void authenticationManagerFactoryWhenFromContextSourceThenAuthenticates() throws Exception {
|
||||
this.spring.register(FromContextSourceConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
|
||||
.andExpect(authenticated().withUsername("bob"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ldapAuthenticationProviderCustomLdapAuthoritiesPopulator() throws Exception {
|
||||
CustomAuthoritiesPopulatorConfig.LAP = new DefaultLdapAuthoritiesPopulator(mock(LdapContextSource.class),
|
||||
null) {
|
||||
@Override
|
||||
protected Set<GrantedAuthority> getAdditionalRoles(DirContextOperations user, String username) {
|
||||
return new HashSet<>(AuthorityUtils.createAuthorityList("ROLE_EXTRA"));
|
||||
}
|
||||
};
|
||||
|
||||
this.spring.register(CustomAuthoritiesPopulatorConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword")).andExpect(
|
||||
authenticated().withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_EXTRA"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticationManagerFactoryWhenCustomAuthoritiesMapperThenUsed() throws Exception {
|
||||
CustomAuthoritiesMapperConfig.AUTHORITIES_MAPPER = ((authorities) -> AuthorityUtils
|
||||
.createAuthorityList("ROLE_CUSTOM"));
|
||||
|
||||
this.spring.register(CustomAuthoritiesMapperConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword")).andExpect(
|
||||
authenticated().withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_CUSTOM"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticationManagerFactoryWhenCustomUserDetailsContextMapperThenUsed() throws Exception {
|
||||
CustomUserDetailsContextMapperConfig.CONTEXT_MAPPER = new UserDetailsContextMapper() {
|
||||
@Override
|
||||
public UserDetails mapUserFromContext(DirContextOperations ctx, String username,
|
||||
Collection<? extends GrantedAuthority> authorities) {
|
||||
return User.withUsername("other").password("password").roles("USER").build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mapUserToContext(UserDetails user, DirContextAdapter ctx) {
|
||||
}
|
||||
};
|
||||
|
||||
this.spring.register(CustomUserDetailsContextMapperConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
|
||||
.andExpect(authenticated().withUsername("other"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticationManagerFactoryWhenCustomUserDnPatternsThenUsed() throws Exception {
|
||||
this.spring.register(CustomUserDnPatternsConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
|
||||
.andExpect(authenticated().withUsername("bob"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticationManagerFactoryWhenCustomUserSearchThenUsed() throws Exception {
|
||||
this.spring.register(CustomUserSearchConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
|
||||
.andExpect(authenticated().withUsername("bob"));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class FromContextSourceConfig extends BaseLdapServerConfig {
|
||||
|
||||
@Bean
|
||||
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
|
||||
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
|
||||
factory.setUserDnPatterns("uid={0},ou=people");
|
||||
return factory.createAuthenticationManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomAuthoritiesMapperConfig extends BaseLdapServerConfig {
|
||||
|
||||
static GrantedAuthoritiesMapper AUTHORITIES_MAPPER;
|
||||
|
||||
@Bean
|
||||
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
|
||||
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
|
||||
factory.setUserDnPatterns("uid={0},ou=people");
|
||||
factory.setAuthoritiesMapper(AUTHORITIES_MAPPER);
|
||||
return factory.createAuthenticationManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomAuthoritiesPopulatorConfig extends BaseLdapServerConfig {
|
||||
|
||||
static LdapAuthoritiesPopulator LAP;
|
||||
|
||||
@Bean
|
||||
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
|
||||
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
|
||||
factory.setUserDnPatterns("uid={0},ou=people");
|
||||
factory.setLdapAuthoritiesPopulator(LAP);
|
||||
return factory.createAuthenticationManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomUserDetailsContextMapperConfig extends BaseLdapServerConfig {
|
||||
|
||||
static UserDetailsContextMapper CONTEXT_MAPPER;
|
||||
|
||||
@Bean
|
||||
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
|
||||
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
|
||||
factory.setUserDnPatterns("uid={0},ou=people");
|
||||
factory.setUserDetailsContextMapper(CONTEXT_MAPPER);
|
||||
return factory.createAuthenticationManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomUserDnPatternsConfig extends BaseLdapServerConfig {
|
||||
|
||||
@Bean
|
||||
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
|
||||
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
|
||||
factory.setUserDnPatterns("uid={0},ou=people");
|
||||
return factory.createAuthenticationManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomUserSearchConfig extends BaseLdapServerConfig {
|
||||
|
||||
@Bean
|
||||
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
|
||||
LdapBindAuthenticationManagerFactory factory = new LdapBindAuthenticationManagerFactory(contextSource);
|
||||
factory.setUserSearchFilter("uid={0}");
|
||||
factory.setUserSearchBase("ou=people");
|
||||
return factory.createAuthenticationManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
abstract static class BaseLdapServerConfig implements DisposableBean {
|
||||
|
||||
private ApacheDSContainer container;
|
||||
|
||||
@Bean
|
||||
ApacheDSContainer ldapServer() throws Exception {
|
||||
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
|
||||
this.container.setPort(0);
|
||||
return this.container;
|
||||
}
|
||||
|
||||
@Bean
|
||||
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
|
||||
int port = container.getLocalPort();
|
||||
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
this.container.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-114
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.ldap;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.test.SpringTestContext;
|
||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
|
||||
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
|
||||
import org.springframework.security.ldap.server.ApacheDSContainer;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
|
||||
import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated;
|
||||
|
||||
@ExtendWith(SpringTestContextExtension.class)
|
||||
public class LdapPasswordComparisonAuthenticationManagerFactoryITests {
|
||||
|
||||
public final SpringTestContext spring = new SpringTestContext(this);
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
public void authenticationManagerFactoryWhenCustomPasswordEncoderThenUsed() throws Exception {
|
||||
this.spring.register(CustomPasswordEncoderConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bcrypt").password("password"))
|
||||
.andExpect(authenticated().withUsername("bcrypt"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authenticationManagerFactoryWhenCustomPasswordAttributeThenUsed() throws Exception {
|
||||
this.spring.register(CustomPasswordAttributeConfig.class).autowire();
|
||||
|
||||
this.mockMvc.perform(formLogin().user("bob").password("bob")).andExpect(authenticated().withUsername("bob"));
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomPasswordEncoderConfig extends BaseLdapServerConfig {
|
||||
|
||||
@Bean
|
||||
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
|
||||
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(
|
||||
contextSource, new BCryptPasswordEncoder());
|
||||
factory.setUserDnPatterns("uid={0},ou=people");
|
||||
return factory.createAuthenticationManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
static class CustomPasswordAttributeConfig extends BaseLdapServerConfig {
|
||||
|
||||
@Bean
|
||||
AuthenticationManager authenticationManager(BaseLdapPathContextSource contextSource) {
|
||||
LdapPasswordComparisonAuthenticationManagerFactory factory = new LdapPasswordComparisonAuthenticationManagerFactory(
|
||||
contextSource, NoOpPasswordEncoder.getInstance());
|
||||
factory.setPasswordAttribute("uid");
|
||||
factory.setUserDnPatterns("uid={0},ou=people");
|
||||
return factory.createAuthenticationManager();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebSecurity
|
||||
abstract static class BaseLdapServerConfig implements DisposableBean {
|
||||
|
||||
private ApacheDSContainer container;
|
||||
|
||||
@Bean
|
||||
ApacheDSContainer ldapServer() throws Exception {
|
||||
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
|
||||
this.container.setPort(0);
|
||||
return this.container;
|
||||
}
|
||||
|
||||
@Bean
|
||||
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
|
||||
int port = container.getLocalPort();
|
||||
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
this.container.stop();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+5
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -56,7 +56,7 @@ public class LdapProviderBeanDefinitionParserTests {
|
||||
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
|
||||
AuthenticationManager.class);
|
||||
Authentication auth = authenticationManager
|
||||
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword"));
|
||||
.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
|
||||
UserDetails ben = (UserDetails) auth.getPrincipal();
|
||||
assertThat(ben.getAuthorities()).hasSize(3);
|
||||
}
|
||||
@@ -89,7 +89,7 @@ public class LdapProviderBeanDefinitionParserTests {
|
||||
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
|
||||
AuthenticationManager.class);
|
||||
Authentication auth = authenticationManager
|
||||
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword"));
|
||||
.authenticate(new UsernamePasswordAuthenticationToken("ben", "benspassword"));
|
||||
|
||||
assertThat(auth).isNotNull();
|
||||
}
|
||||
@@ -104,8 +104,7 @@ public class LdapProviderBeanDefinitionParserTests {
|
||||
|
||||
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
|
||||
AuthenticationManager.class);
|
||||
Authentication auth = authenticationManager
|
||||
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "ben"));
|
||||
Authentication auth = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken("ben", "ben"));
|
||||
|
||||
assertThat(auth).isNotNull();
|
||||
}
|
||||
@@ -122,7 +121,7 @@ public class LdapProviderBeanDefinitionParserTests {
|
||||
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
|
||||
AuthenticationManager.class);
|
||||
Authentication auth = authenticationManager
|
||||
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("bcrypt", "password"));
|
||||
.authenticate(new UsernamePasswordAuthenticationToken("bcrypt", "password"));
|
||||
|
||||
assertThat(auth).isNotNull();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -138,10 +138,4 @@ public abstract class Elements {
|
||||
|
||||
public static final String PASSWORD_MANAGEMENT = "password-management";
|
||||
|
||||
public static final String RELYING_PARTY_REGISTRATIONS = "relying-party-registrations";
|
||||
|
||||
public static final String SAML2_LOGIN = "saml2-login";
|
||||
|
||||
public static final String SAML2_LOGOUT = "saml2-logout";
|
||||
|
||||
}
|
||||
|
||||
+3
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2009-2022 the original author or authors.
|
||||
* Copyright 2009-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.
|
||||
@@ -47,7 +47,6 @@ import org.springframework.security.config.method.InterceptMethodsBeanDefinition
|
||||
import org.springframework.security.config.method.MethodSecurityBeanDefinitionParser;
|
||||
import org.springframework.security.config.method.MethodSecurityMetadataSourceBeanDefinitionParser;
|
||||
import org.springframework.security.config.oauth2.client.ClientRegistrationsBeanDefinitionParser;
|
||||
import org.springframework.security.config.saml2.RelyingPartyRegistrationsBeanDefinitionParser;
|
||||
import org.springframework.security.config.websocket.WebSocketMessageBrokerSecurityBeanDefinitionParser;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -95,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.7. Please update your schema declarations to the 5.7 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);
|
||||
@@ -191,7 +190,6 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
this.parsers.put(Elements.FILTER_CHAIN, new FilterChainBeanDefinitionParser());
|
||||
this.filterChainMapBDD = new FilterChainMapBeanDefinitionDecorator();
|
||||
this.parsers.put(Elements.CLIENT_REGISTRATIONS, new ClientRegistrationsBeanDefinitionParser());
|
||||
this.parsers.put(Elements.RELYING_PARTY_REGISTRATIONS, new RelyingPartyRegistrationsBeanDefinitionParser());
|
||||
}
|
||||
|
||||
private void loadWebSocketParsers() {
|
||||
@@ -217,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\\.7.*.xsd.*")
|
||||
return schemaLocation.matches("(?m).*spring-security-5\\.6.*.xsd.*")
|
||||
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|
||||
|| !schemaLocation.matches("(?m).*spring-security.*");
|
||||
}
|
||||
|
||||
+23
-29
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -17,16 +17,16 @@
|
||||
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;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
|
||||
@@ -45,64 +45,59 @@ import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
final class PrePostMethodSecurityConfiguration {
|
||||
final class PrePostMethodSecurityConfiguration implements ApplicationContextAware {
|
||||
|
||||
private final PreFilterAuthorizationMethodInterceptor preFilterAuthorizationMethodInterceptor = new PreFilterAuthorizationMethodInterceptor();
|
||||
|
||||
private final AuthorizationManagerBeforeMethodInterceptor preAuthorizeAuthorizationMethodInterceptor;
|
||||
|
||||
private final PreAuthorizeAuthorizationManager preAuthorizeAuthorizationManager = new PreAuthorizeAuthorizationManager();
|
||||
|
||||
private final AuthorizationManagerAfterMethodInterceptor postAuthorizeAuthorizaitonMethodInterceptor;
|
||||
|
||||
private final PostAuthorizeAuthorizationManager postAuthorizeAuthorizationManager = new PostAuthorizeAuthorizationManager();
|
||||
|
||||
private final PostFilterAuthorizationMethodInterceptor postFilterAuthorizationMethodInterceptor = new PostFilterAuthorizationMethodInterceptor();
|
||||
|
||||
private final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||
|
||||
@Autowired
|
||||
PrePostMethodSecurityConfiguration(ApplicationContext context) {
|
||||
this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
|
||||
this.preAuthorizeAuthorizationMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor
|
||||
.preAuthorize(this.preAuthorizeAuthorizationManager);
|
||||
this.postAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
|
||||
this.postAuthorizeAuthorizaitonMethodInterceptor = AuthorizationManagerAfterMethodInterceptor
|
||||
.postAuthorize(this.postAuthorizeAuthorizationManager);
|
||||
this.preFilterAuthorizationMethodInterceptor.setExpressionHandler(this.expressionHandler);
|
||||
this.postFilterAuthorizationMethodInterceptor.setExpressionHandler(this.expressionHandler);
|
||||
this.expressionHandler.setApplicationContext(context);
|
||||
AuthorizationEventPublisher publisher = new SpringAuthorizationEventPublisher(context);
|
||||
this.preAuthorizeAuthorizationMethodInterceptor.setAuthorizationEventPublisher(publisher);
|
||||
this.postAuthorizeAuthorizaitonMethodInterceptor.setAuthorizationEventPublisher(publisher);
|
||||
}
|
||||
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() {
|
||||
return this.preAuthorizeAuthorizationMethodInterceptor;
|
||||
if (!this.customMethodSecurityExpressionHandler) {
|
||||
this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
|
||||
}
|
||||
return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(this.preAuthorizeAuthorizationManager);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
Advisor postAuthorizeAuthorizationMethodInterceptor() {
|
||||
return this.postAuthorizeAuthorizaitonMethodInterceptor;
|
||||
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;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setMethodSecurityExpressionHandler(MethodSecurityExpressionHandler methodSecurityExpressionHandler) {
|
||||
this.customMethodSecurityExpressionHandler = true;
|
||||
this.preFilterAuthorizationMethodInterceptor.setExpressionHandler(methodSecurityExpressionHandler);
|
||||
this.preAuthorizeAuthorizationManager.setExpressionHandler(methodSecurityExpressionHandler);
|
||||
this.postAuthorizeAuthorizationManager.setExpressionHandler(methodSecurityExpressionHandler);
|
||||
@@ -114,10 +109,9 @@ final class PrePostMethodSecurityConfiguration {
|
||||
this.expressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setAuthorizationEventPublisher(AuthorizationEventPublisher eventPublisher) {
|
||||
this.preAuthorizeAuthorizationMethodInterceptor.setAuthorizationEventPublisher(eventPublisher);
|
||||
this.postAuthorizeAuthorizaitonMethodInterceptor.setAuthorizationEventPublisher(eventPublisher);
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.expressionHandler.setApplicationContext(context);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-4
@@ -42,8 +42,6 @@ import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
||||
import org.springframework.security.web.session.DisableEncodeUrlFilter;
|
||||
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
|
||||
/**
|
||||
@@ -126,8 +124,6 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>
|
||||
* The ordering of the Filters is:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link ForceEagerSessionCreationFilter}</li>
|
||||
* <li>{@link DisableEncodeUrlFilter}</li>
|
||||
* <li>{@link ChannelProcessingFilter}</li>
|
||||
* <li>{@link SecurityContextPersistenceFilter}</li>
|
||||
* <li>{@link LogoutFilter}</li>
|
||||
|
||||
-6
@@ -37,7 +37,6 @@ import org.springframework.security.web.authentication.ui.DefaultLoginPageGenera
|
||||
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;
|
||||
import org.springframework.security.web.context.SecurityContextHolderFilter;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
||||
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
||||
import org.springframework.security.web.csrf.CsrfFilter;
|
||||
@@ -46,8 +45,6 @@ import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
||||
import org.springframework.security.web.session.DisableEncodeUrlFilter;
|
||||
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
@@ -70,12 +67,9 @@ final class FilterOrderRegistration {
|
||||
|
||||
FilterOrderRegistration() {
|
||||
Step order = new Step(INITIAL_ORDER, ORDER_STEP);
|
||||
put(DisableEncodeUrlFilter.class, order.next());
|
||||
put(ForceEagerSessionCreationFilter.class, order.next());
|
||||
put(ChannelProcessingFilter.class, order.next());
|
||||
order.next(); // gh-8105
|
||||
put(WebAsyncManagerIntegrationFilter.class, order.next());
|
||||
put(SecurityContextHolderFilter.class, order.next());
|
||||
put(SecurityContextPersistenceFilter.class, order.next());
|
||||
put(HeaderWriterFilter.class, order.next());
|
||||
put(CorsFilter.class, order.next());
|
||||
|
||||
-7
@@ -2889,15 +2889,8 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected DefaultSecurityFilterChain performBuild() {
|
||||
ExpressionUrlAuthorizationConfigurer<?> expressionConfigurer = getConfigurer(
|
||||
ExpressionUrlAuthorizationConfigurer.class);
|
||||
AuthorizeHttpRequestsConfigurer<?> httpConfigurer = getConfigurer(AuthorizeHttpRequestsConfigurer.class);
|
||||
boolean oneConfigurerPresent = expressionConfigurer == null ^ httpConfigurer == null;
|
||||
Assert.state((expressionConfigurer == null && httpConfigurer == null) || oneConfigurerPresent,
|
||||
"authorizeHttpRequests cannot be used in conjunction with authorizeRequests. Please select just one.");
|
||||
this.filters.sort(OrderComparator.INSTANCE);
|
||||
List<Filter> sortedFilters = new ArrayList<>(this.filters.size());
|
||||
for (Filter filter : this.filters) {
|
||||
|
||||
+2
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -264,6 +264,7 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
* @deprecated Use {@link #privilegeEvaluator(WebInvocationPrivilegeEvaluator)}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public WebSecurity securityInterceptor(FilterSecurityInterceptor securityInterceptor) {
|
||||
this.filterSecurityInterceptor = securityInterceptor;
|
||||
return this;
|
||||
@@ -279,19 +280,6 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the handler to handle
|
||||
* {@link org.springframework.security.web.firewall.RequestRejectedException}
|
||||
* @param requestRejectedHandler
|
||||
* @return the {@link WebSecurity} for further customizations
|
||||
* @since 5.7
|
||||
*/
|
||||
public WebSecurity requestRejectedHandler(RequestRejectedHandler requestRejectedHandler) {
|
||||
Assert.notNull(requestRejectedHandler, "requestRejectedHandler cannot be null");
|
||||
this.requestRejectedHandler = requestRejectedHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Filter performBuild() throws Exception {
|
||||
Assert.state(!this.securityFilterChainBuilders.isEmpty(),
|
||||
|
||||
+1
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.security.config.annotation.web.configuration;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -25,13 +24,11 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
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.DefaultLoginPageConfigurer;
|
||||
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
||||
|
||||
@@ -100,7 +97,6 @@ class HttpSecurityConfiguration {
|
||||
.apply(new DefaultLoginPageConfigurer<>());
|
||||
http.logout(withDefaults());
|
||||
// @formatter:on
|
||||
applyDefaultConfigurers(http);
|
||||
return http;
|
||||
}
|
||||
|
||||
@@ -109,15 +105,6 @@ class HttpSecurityConfiguration {
|
||||
: this.authenticationConfiguration.getAuthenticationManager();
|
||||
}
|
||||
|
||||
private void applyDefaultConfigurers(HttpSecurity http) throws Exception {
|
||||
ClassLoader classLoader = this.context.getClassLoader();
|
||||
List<AbstractHttpConfigurer> defaultHttpConfigurers = SpringFactoriesLoader
|
||||
.loadFactories(AbstractHttpConfigurer.class, classLoader);
|
||||
for (AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
|
||||
http.apply(configurer);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Class<?>, Object> createSharedObjects() {
|
||||
Map<Class<?>, Object> sharedObjects = new HashMap<>();
|
||||
sharedObjects.put(ApplicationContext.class, this.context);
|
||||
|
||||
+31
-142
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,14 +16,10 @@
|
||||
|
||||
package org.springframework.security.config.annotation.web.configuration;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
@@ -71,22 +67,17 @@ class SecurityReactorContextConfiguration {
|
||||
|
||||
private static final String SECURITY_REACTOR_CONTEXT_OPERATOR_KEY = "org.springframework.security.SECURITY_REACTOR_CONTEXT_OPERATOR";
|
||||
|
||||
private static final Map<Object, Supplier<Object>> CONTEXT_ATTRIBUTE_VALUE_LOADERS = new HashMap<>();
|
||||
|
||||
static {
|
||||
CONTEXT_ATTRIBUTE_VALUE_LOADERS.put(HttpServletRequest.class,
|
||||
SecurityReactorContextSubscriberRegistrar::getRequest);
|
||||
CONTEXT_ATTRIBUTE_VALUE_LOADERS.put(HttpServletResponse.class,
|
||||
SecurityReactorContextSubscriberRegistrar::getResponse);
|
||||
CONTEXT_ATTRIBUTE_VALUE_LOADERS.put(Authentication.class,
|
||||
SecurityReactorContextSubscriberRegistrar::getAuthentication);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Function<? super Publisher<Object>, ? extends Publisher<Object>> lifter = Operators
|
||||
.liftPublisher((pub, sub) -> createSubscriberIfNecessary(sub));
|
||||
Hooks.onLastOperator(SECURITY_REACTOR_CONTEXT_OPERATOR_KEY, lifter::apply);
|
||||
Hooks.onLastOperator(SECURITY_REACTOR_CONTEXT_OPERATOR_KEY, (pub) -> {
|
||||
if (!contextAttributesAvailable()) {
|
||||
// No need to decorate so return original Publisher
|
||||
return pub;
|
||||
}
|
||||
return lifter.apply(pub);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,30 +93,36 @@ class SecurityReactorContextConfiguration {
|
||||
return new SecurityReactorContextSubscriber<>(delegate, getContextAttributes());
|
||||
}
|
||||
|
||||
private static boolean contextAttributesAvailable() {
|
||||
return SecurityContextHolder.getContext().getAuthentication() != null
|
||||
|| RequestContextHolder.getRequestAttributes() instanceof ServletRequestAttributes;
|
||||
}
|
||||
|
||||
private static Map<Object, Object> getContextAttributes() {
|
||||
return new LoadingMap<>(CONTEXT_ATTRIBUTE_VALUE_LOADERS);
|
||||
}
|
||||
|
||||
private static HttpServletRequest getRequest() {
|
||||
HttpServletRequest servletRequest = null;
|
||||
HttpServletResponse servletResponse = null;
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
if (requestAttributes instanceof ServletRequestAttributes) {
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
|
||||
return servletRequestAttributes.getRequest();
|
||||
servletRequest = servletRequestAttributes.getRequest();
|
||||
servletResponse = servletRequestAttributes.getResponse(); // possible null
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static HttpServletResponse getResponse() {
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
if (requestAttributes instanceof ServletRequestAttributes) {
|
||||
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
|
||||
return servletRequestAttributes.getResponse(); // possible null
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication == null && servletRequest == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<Object, Object> contextAttributes = new HashMap<>();
|
||||
if (servletRequest != null) {
|
||||
contextAttributes.put(HttpServletRequest.class, servletRequest);
|
||||
}
|
||||
if (servletResponse != null) {
|
||||
contextAttributes.put(HttpServletResponse.class, servletResponse);
|
||||
}
|
||||
if (authentication != null) {
|
||||
contextAttributes.put(Authentication.class, authentication);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Authentication getAuthentication() {
|
||||
return SecurityContextHolder.getContext().getAuthentication();
|
||||
return contextAttributes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -178,112 +175,4 @@ class SecurityReactorContextConfiguration {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A map that computes each value when {@link #get} is invoked
|
||||
*/
|
||||
static class LoadingMap<K, V> implements Map<K, V> {
|
||||
|
||||
private final Map<K, V> loaded = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<K, Supplier<V>> loaders;
|
||||
|
||||
LoadingMap(Map<K, Supplier<V>> loaders) {
|
||||
this.loaders = Collections.unmodifiableMap(new HashMap<>(loaders));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.loaders.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return this.loaders.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return this.loaders.containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return this.loaders.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
if (!this.loaders.containsKey(key)) {
|
||||
throw new IllegalArgumentException(
|
||||
"This map only supports the following keys: " + this.loaders.keySet());
|
||||
}
|
||||
return this.loaded.computeIfAbsent((K) key, (k) -> this.loaders.get(k).get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
if (!this.loaders.containsKey(key)) {
|
||||
throw new IllegalArgumentException(
|
||||
"This map only supports the following keys: " + this.loaders.keySet());
|
||||
}
|
||||
return this.loaded.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
if (!this.loaders.containsKey(key)) {
|
||||
throw new IllegalArgumentException(
|
||||
"This map only supports the following keys: " + this.loaders.keySet());
|
||||
}
|
||||
return this.loaded.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> m) {
|
||||
for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
|
||||
put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.loaded.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return this.loaded.containsValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
return this.loaded.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
return this.loaded.entrySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadingMap<?, ?> that = (LoadingMap<?, ?>) o;
|
||||
|
||||
return this.loaded.equals(that.loaded);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.loaded.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+11
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,6 +24,7 @@ import javax.servlet.Filter;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -142,20 +143,19 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
|
||||
* instances used to create the web configuration.
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} used to create a
|
||||
* {@link WebSecurity} instance
|
||||
* @param beanFactory the bean factory to use to retrieve the relevant
|
||||
* @param webSecurityConfigurers the
|
||||
* {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>} instances used to
|
||||
* create the web configuration
|
||||
* @throws Exception
|
||||
*/
|
||||
@Autowired(required = false)
|
||||
public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,
|
||||
ConfigurableListableBeanFactory beanFactory) throws Exception {
|
||||
@Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers)
|
||||
throws Exception {
|
||||
this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
|
||||
if (this.debugEnabled != null) {
|
||||
this.webSecurity.debug(this.debugEnabled);
|
||||
}
|
||||
List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers = new AutowiredWebSecurityConfigurersIgnoreParents(
|
||||
beanFactory).getWebSecurityConfigurers();
|
||||
webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);
|
||||
Integer previousOrder = null;
|
||||
Object previousConfig = null;
|
||||
@@ -189,6 +189,12 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
|
||||
return new RsaKeyConversionServicePostProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
Map<String, Object> enableWebSecurityAttrMap = importMetadata
|
||||
|
||||
+1
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -89,12 +89,8 @@ import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @see EnableWebSecurity
|
||||
* @deprecated Use a {@link org.springframework.security.web.SecurityFilterChain} Bean to
|
||||
* configure {@link HttpSecurity} or a {@link WebSecurityCustomizer} Bean to configure
|
||||
* {@link WebSecurity}
|
||||
*/
|
||||
@Order(100)
|
||||
@Deprecated
|
||||
public abstract class WebSecurityConfigurerAdapter implements WebSecurityConfigurer<WebSecurity> {
|
||||
|
||||
private final Log logger = LogFactory.getLog(WebSecurityConfigurerAdapter.class);
|
||||
|
||||
-12
@@ -38,7 +38,6 @@ import org.springframework.security.web.authentication.SavedRequestAwareAuthenti
|
||||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
||||
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
|
||||
@@ -147,11 +146,6 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
|
||||
return getSelf();
|
||||
}
|
||||
|
||||
public T securityContextRepository(SecurityContextRepository securityContextRepository) {
|
||||
this.authFilter.setSecurityContextRepository(securityContextRepository);
|
||||
return getSelf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the {@link RequestMatcher} given a loginProcessingUrl
|
||||
* @param loginProcessingUrl creates the {@link RequestMatcher} based upon the
|
||||
@@ -293,12 +287,6 @@ public abstract class AbstractAuthenticationFilterConfigurer<B extends HttpSecur
|
||||
if (rememberMeServices != null) {
|
||||
this.authFilter.setRememberMeServices(rememberMeServices);
|
||||
}
|
||||
SecurityContextConfigurer securityContextConfigurer = http.getConfigurer(SecurityContextConfigurer.class);
|
||||
if (securityContextConfigurer != null && securityContextConfigurer.isRequireExplicitSave()) {
|
||||
SecurityContextRepository securityContextRepository = securityContextConfigurer
|
||||
.getSecurityContextRepository();
|
||||
this.authFilter.setSecurityContextRepository(securityContextRepository);
|
||||
}
|
||||
F filter = postProcess(this.authFilter);
|
||||
http.addFilter(filter);
|
||||
}
|
||||
|
||||
+2
-45
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,9 +25,7 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorityAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
@@ -36,7 +34,6 @@ import org.springframework.security.web.access.intercept.RequestAuthorizationCon
|
||||
import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;
|
||||
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -49,25 +46,14 @@ import org.springframework.util.Assert;
|
||||
public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
extends AbstractHttpConfigurer<AuthorizeHttpRequestsConfigurer<H>, H> {
|
||||
|
||||
static final AuthorizationManager<RequestAuthorizationContext> permitAllAuthorizationManager = (a,
|
||||
o) -> new AuthorizationDecision(true);
|
||||
|
||||
private final AuthorizationManagerRequestMatcherRegistry registry;
|
||||
|
||||
private final AuthorizationEventPublisher publisher;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
* @param context the {@link ApplicationContext} to use
|
||||
*/
|
||||
public AuthorizeHttpRequestsConfigurer(ApplicationContext context) {
|
||||
this.registry = new AuthorizationManagerRequestMatcherRegistry(context);
|
||||
if (context.getBeanNamesForType(AuthorizationEventPublisher.class).length > 0) {
|
||||
this.publisher = context.getBean(AuthorizationEventPublisher.class);
|
||||
}
|
||||
else {
|
||||
this.publisher = new SpringAuthorizationEventPublisher(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,8 +70,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
public void configure(H http) {
|
||||
AuthorizationManager<HttpServletRequest> authorizationManager = this.registry.createAuthorizationManager();
|
||||
AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);
|
||||
authorizationFilter.setAuthorizationEventPublisher(this.publisher);
|
||||
authorizationFilter.setShouldFilterAllDispatcherTypes(this.registry.shouldFilterAllDispatcherTypes);
|
||||
http.addFilter(postProcess(authorizationFilter));
|
||||
}
|
||||
|
||||
@@ -97,12 +81,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
AuthorizationManagerRequestMatcherRegistry addFirst(RequestMatcher matcher,
|
||||
AuthorizationManager<RequestAuthorizationContext> manager) {
|
||||
this.registry.addFirst(matcher, manager);
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registry for mapping a {@link RequestMatcher} to an {@link AuthorizationManager}.
|
||||
*
|
||||
@@ -118,8 +96,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
|
||||
private int mappingCount;
|
||||
|
||||
private boolean shouldFilterAllDispatcherTypes = false;
|
||||
|
||||
private AuthorizationManagerRequestMatcherRegistry(ApplicationContext context) {
|
||||
setApplicationContext(context);
|
||||
}
|
||||
@@ -130,12 +106,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
this.mappingCount++;
|
||||
}
|
||||
|
||||
private void addFirst(RequestMatcher matcher, AuthorizationManager<RequestAuthorizationContext> manager) {
|
||||
this.unmappedMatchers = null;
|
||||
this.managerBuilder.mappings((m) -> m.add(0, new RequestMatcherEntry<>(matcher, manager)));
|
||||
this.mappingCount++;
|
||||
}
|
||||
|
||||
private AuthorizationManager<HttpServletRequest> createAuthorizationManager() {
|
||||
Assert.state(this.unmappedMatchers == null,
|
||||
() -> "An incomplete mapping was found for " + this.unmappedMatchers
|
||||
@@ -173,19 +143,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether all dispatcher types should be filtered.
|
||||
* @param shouldFilter should filter all dispatcher types. Default is
|
||||
* {@code false}
|
||||
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||
* customizations
|
||||
* @since 5.7
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry shouldFilterAllDispatcherTypes(boolean shouldFilter) {
|
||||
this.shouldFilterAllDispatcherTypes = shouldFilter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link HttpSecurityBuilder} when done using the
|
||||
* {@link AuthorizeHttpRequestsConfigurer}. This is useful for method chaining.
|
||||
@@ -252,7 +209,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
* customizations
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry permitAll() {
|
||||
return access(permitAllAuthorizationManager);
|
||||
return access((a, o) -> new AuthorizationDecision(true));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -30,9 +30,7 @@ import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.web.DefaultRedirectStrategy;
|
||||
import org.springframework.security.web.PortMapper;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl;
|
||||
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
||||
import org.springframework.security.web.access.channel.ChannelProcessor;
|
||||
@@ -77,7 +75,6 @@ import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
*
|
||||
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
|
||||
* @author Rob Winch
|
||||
* @author Onur Kagan Ozcan
|
||||
* @since 3.2
|
||||
*/
|
||||
public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
@@ -89,8 +86,6 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
|
||||
private List<ChannelProcessor> channelProcessors;
|
||||
|
||||
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
|
||||
|
||||
private final ChannelRequestMatcherRegistry REGISTRY;
|
||||
|
||||
/**
|
||||
@@ -128,11 +123,9 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
if (portMapper != null) {
|
||||
RetryWithHttpEntryPoint httpEntryPoint = new RetryWithHttpEntryPoint();
|
||||
httpEntryPoint.setPortMapper(portMapper);
|
||||
httpEntryPoint.setRedirectStrategy(this.redirectStrategy);
|
||||
insecureChannelProcessor.setEntryPoint(httpEntryPoint);
|
||||
RetryWithHttpsEntryPoint httpsEntryPoint = new RetryWithHttpsEntryPoint();
|
||||
httpsEntryPoint.setPortMapper(portMapper);
|
||||
httpsEntryPoint.setRedirectStrategy(this.redirectStrategy);
|
||||
secureChannelProcessor.setEntryPoint(httpsEntryPoint);
|
||||
}
|
||||
insecureChannelProcessor = postProcess(insecureChannelProcessor);
|
||||
@@ -192,17 +185,6 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RedirectStrategy} instances to use in
|
||||
* {@link RetryWithHttpEntryPoint} and {@link RetryWithHttpsEntryPoint}
|
||||
* @param redirectStrategy
|
||||
* @return the {@link ChannelSecurityConfigurer} for further customizations
|
||||
*/
|
||||
public ChannelRequestMatcherRegistry redirectStrategy(RedirectStrategy redirectStrategy) {
|
||||
ChannelSecurityConfigurer.this.redirectStrategy = redirectStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link SecurityBuilder} when done using the
|
||||
* {@link SecurityConfigurer}. This is useful for method chaining.
|
||||
|
||||
+3
-3
@@ -237,8 +237,8 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
|
||||
/**
|
||||
* Gets the default {@link AccessDeniedHandler} from the
|
||||
* {@link ExceptionHandlingConfigurer#getAccessDeniedHandler(HttpSecurityBuilder)} or
|
||||
* create a {@link AccessDeniedHandlerImpl} if not available.
|
||||
* {@link ExceptionHandlingConfigurer#getAccessDeniedHandler()} or create a
|
||||
* {@link AccessDeniedHandlerImpl} if not available.
|
||||
* @param http the {@link HttpSecurityBuilder}
|
||||
* @return the {@link AccessDeniedHandler}
|
||||
*/
|
||||
@@ -247,7 +247,7 @@ public final class CsrfConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
ExceptionHandlingConfigurer<H> exceptionConfig = http.getConfigurer(ExceptionHandlingConfigurer.class);
|
||||
AccessDeniedHandler handler = null;
|
||||
if (exceptionConfig != null) {
|
||||
handler = exceptionConfig.getAccessDeniedHandler(http);
|
||||
handler = exceptionConfig.getAccessDeniedHandler();
|
||||
}
|
||||
if (handler == null) {
|
||||
handler = new AccessDeniedHandlerImpl();
|
||||
|
||||
+3
-1
@@ -369,7 +369,9 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that URLs requires a specific IP Address or subnet.
|
||||
* Specify that URLs requires a specific IP Address or <a href=
|
||||
* "https://forum.spring.io/showthread.php?102783-How-to-use-hasIpAddress&p=343971#post343971"
|
||||
* >subnet</a>.
|
||||
* @param ipaddressExpression the ipaddress (i.e. 192.168.1.79) or local subnet
|
||||
* (i.e. 192.168.0/24)
|
||||
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further
|
||||
|
||||
+1
-228
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -31,9 +31,6 @@ import org.springframework.security.web.header.HeaderWriter;
|
||||
import org.springframework.security.web.header.HeaderWriterFilter;
|
||||
import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
|
||||
import org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter;
|
||||
import org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter;
|
||||
import org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter;
|
||||
import org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter;
|
||||
import org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter;
|
||||
import org.springframework.security.web.header.writers.HpkpHeaderWriter;
|
||||
import org.springframework.security.web.header.writers.HstsHeaderWriter;
|
||||
@@ -100,12 +97,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
|
||||
private final PermissionsPolicyConfig permissionsPolicy = new PermissionsPolicyConfig();
|
||||
|
||||
private final CrossOriginOpenerPolicyConfig crossOriginOpenerPolicy = new CrossOriginOpenerPolicyConfig();
|
||||
|
||||
private final CrossOriginEmbedderPolicyConfig crossOriginEmbedderPolicy = new CrossOriginEmbedderPolicyConfig();
|
||||
|
||||
private final CrossOriginResourcePolicyConfig crossOriginResourcePolicy = new CrossOriginResourcePolicyConfig();
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
@@ -401,9 +392,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
addIfNotNull(writers, this.referrerPolicy.writer);
|
||||
addIfNotNull(writers, this.featurePolicy.writer);
|
||||
addIfNotNull(writers, this.permissionsPolicy.writer);
|
||||
addIfNotNull(writers, this.crossOriginOpenerPolicy.writer);
|
||||
addIfNotNull(writers, this.crossOriginEmbedderPolicy.writer);
|
||||
addIfNotNull(writers, this.crossOriginResourcePolicy.writer);
|
||||
writers.addAll(this.headerWriters);
|
||||
return writers;
|
||||
}
|
||||
@@ -556,129 +544,6 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
return this.permissionsPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuration for <a href=
|
||||
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
|
||||
* Cross-Origin-Opener-Policy</a> header.
|
||||
* <p>
|
||||
* Configuration is provided to the {@link CrossOriginOpenerPolicyHeaderWriter} which
|
||||
* responsible for writing the header.
|
||||
* </p>
|
||||
* @return the {@link CrossOriginOpenerPolicyConfig} for additional confniguration
|
||||
* @since 5.7
|
||||
* @see CrossOriginOpenerPolicyHeaderWriter
|
||||
*/
|
||||
public CrossOriginOpenerPolicyConfig crossOriginOpenerPolicy() {
|
||||
this.crossOriginOpenerPolicy.writer = new CrossOriginOpenerPolicyHeaderWriter();
|
||||
return this.crossOriginOpenerPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuration for <a href=
|
||||
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy">
|
||||
* Cross-Origin-Opener-Policy</a> header.
|
||||
* <p>
|
||||
* Calling this method automatically enables (includes) the
|
||||
* {@code Cross-Origin-Opener-Policy} header in the response using the supplied
|
||||
* policy.
|
||||
* <p>
|
||||
* <p>
|
||||
* Configuration is provided to the {@link CrossOriginOpenerPolicyHeaderWriter} which
|
||||
* responsible for writing the header.
|
||||
* </p>
|
||||
* @return the {@link HeadersConfigurer} for additional customizations
|
||||
* @since 5.7
|
||||
* @see CrossOriginOpenerPolicyHeaderWriter
|
||||
*/
|
||||
public HeadersConfigurer<H> crossOriginOpenerPolicy(
|
||||
Customizer<CrossOriginOpenerPolicyConfig> crossOriginOpenerPolicyCustomizer) {
|
||||
this.crossOriginOpenerPolicy.writer = new CrossOriginOpenerPolicyHeaderWriter();
|
||||
crossOriginOpenerPolicyCustomizer.customize(this.crossOriginOpenerPolicy);
|
||||
return HeadersConfigurer.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuration for <a href=
|
||||
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
|
||||
* Cross-Origin-Embedder-Policy</a> header.
|
||||
* <p>
|
||||
* Configuration is provided to the {@link CrossOriginEmbedderPolicyHeaderWriter}
|
||||
* which is responsible for writing the header.
|
||||
* </p>
|
||||
* @return the {@link CrossOriginEmbedderPolicyConfig} for additional customizations
|
||||
* @since 5.7
|
||||
* @see CrossOriginEmbedderPolicyHeaderWriter
|
||||
*/
|
||||
public CrossOriginEmbedderPolicyConfig crossOriginEmbedderPolicy() {
|
||||
this.crossOriginEmbedderPolicy.writer = new CrossOriginEmbedderPolicyHeaderWriter();
|
||||
return this.crossOriginEmbedderPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuration for <a href=
|
||||
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy">
|
||||
* Cross-Origin-Embedder-Policy</a> header.
|
||||
* <p>
|
||||
* Calling this method automatically enables (includes) the
|
||||
* {@code Cross-Origin-Embedder-Policy} header in the response using the supplied
|
||||
* policy.
|
||||
* <p>
|
||||
* <p>
|
||||
* Configuration is provided to the {@link CrossOriginEmbedderPolicyHeaderWriter}
|
||||
* which is responsible for writing the header.
|
||||
* </p>
|
||||
* @return the {@link HeadersConfigurer} for additional customizations
|
||||
* @since 5.7
|
||||
* @see CrossOriginEmbedderPolicyHeaderWriter
|
||||
*/
|
||||
public HeadersConfigurer<H> crossOriginEmbedderPolicy(
|
||||
Customizer<CrossOriginEmbedderPolicyConfig> crossOriginEmbedderPolicyCustomizer) {
|
||||
this.crossOriginEmbedderPolicy.writer = new CrossOriginEmbedderPolicyHeaderWriter();
|
||||
crossOriginEmbedderPolicyCustomizer.customize(this.crossOriginEmbedderPolicy);
|
||||
return HeadersConfigurer.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuration for <a href=
|
||||
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
|
||||
* Cross-Origin-Resource-Policy</a> header.
|
||||
* <p>
|
||||
* Configuration is provided to the {@link CrossOriginResourcePolicyHeaderWriter}
|
||||
* which is responsible for writing the header:
|
||||
* </p>
|
||||
* @return the {@link HeadersConfigurer} for additional customizations
|
||||
* @since 5.7
|
||||
* @see CrossOriginResourcePolicyHeaderWriter
|
||||
*/
|
||||
public CrossOriginResourcePolicyConfig crossOriginResourcePolicy() {
|
||||
this.crossOriginResourcePolicy.writer = new CrossOriginResourcePolicyHeaderWriter();
|
||||
return this.crossOriginResourcePolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows configuration for <a href=
|
||||
* "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Resource-Policy">
|
||||
* Cross-Origin-Resource-Policy</a> header.
|
||||
* <p>
|
||||
* Calling this method automatically enables (includes) the
|
||||
* {@code Cross-Origin-Resource-Policy} header in the response using the supplied
|
||||
* policy.
|
||||
* <p>
|
||||
* <p>
|
||||
* Configuration is provided to the {@link CrossOriginResourcePolicyHeaderWriter}
|
||||
* which is responsible for writing the header:
|
||||
* </p>
|
||||
* @return the {@link HeadersConfigurer} for additional customizations
|
||||
* @since 5.7
|
||||
* @see CrossOriginResourcePolicyHeaderWriter
|
||||
*/
|
||||
public HeadersConfigurer<H> crossOriginResourcePolicy(
|
||||
Customizer<CrossOriginResourcePolicyConfig> crossOriginResourcePolicyCustomizer) {
|
||||
this.crossOriginResourcePolicy.writer = new CrossOriginResourcePolicyHeaderWriter();
|
||||
crossOriginResourcePolicyCustomizer.customize(this.crossOriginResourcePolicy);
|
||||
return HeadersConfigurer.this;
|
||||
}
|
||||
|
||||
public final class ContentTypeOptionsConfig {
|
||||
|
||||
private XContentTypeOptionsHeaderWriter writer;
|
||||
@@ -1277,96 +1142,4 @@ public class HeadersConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
|
||||
}
|
||||
|
||||
public final class CrossOriginOpenerPolicyConfig {
|
||||
|
||||
private CrossOriginOpenerPolicyHeaderWriter writer;
|
||||
|
||||
public CrossOriginOpenerPolicyConfig() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the policy to be used in the {@code Cross-Origin-Opener-Policy} header
|
||||
* @param openerPolicy a {@code Cross-Origin-Opener-Policy}
|
||||
* @return the {@link CrossOriginOpenerPolicyConfig} for additional configuration
|
||||
* @throws IllegalArgumentException if openerPolicy is null
|
||||
*/
|
||||
public CrossOriginOpenerPolicyConfig policy(
|
||||
CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy openerPolicy) {
|
||||
this.writer.setPolicy(openerPolicy);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows completing configuration of Cross Origin Opener Policy and continuing
|
||||
* configuration of headers.
|
||||
* @return the {@link HeadersConfigurer} for additional configuration
|
||||
*/
|
||||
public HeadersConfigurer<H> and() {
|
||||
return HeadersConfigurer.this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public final class CrossOriginEmbedderPolicyConfig {
|
||||
|
||||
private CrossOriginEmbedderPolicyHeaderWriter writer;
|
||||
|
||||
public CrossOriginEmbedderPolicyConfig() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the policy to be used in the {@code Cross-Origin-Embedder-Policy} header
|
||||
* @param embedderPolicy a {@code Cross-Origin-Embedder-Policy}
|
||||
* @return the {@link CrossOriginEmbedderPolicyConfig} for additional
|
||||
* configuration
|
||||
* @throws IllegalArgumentException if embedderPolicy is null
|
||||
*/
|
||||
public CrossOriginEmbedderPolicyConfig policy(
|
||||
CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy embedderPolicy) {
|
||||
this.writer.setPolicy(embedderPolicy);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows completing configuration of Cross-Origin-Embedder-Policy and continuing
|
||||
* configuration of headers.
|
||||
* @return the {@link HeadersConfigurer} for additional configuration
|
||||
*/
|
||||
public HeadersConfigurer<H> and() {
|
||||
return HeadersConfigurer.this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public final class CrossOriginResourcePolicyConfig {
|
||||
|
||||
private CrossOriginResourcePolicyHeaderWriter writer;
|
||||
|
||||
public CrossOriginResourcePolicyConfig() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the policy to be used in the {@code Cross-Origin-Resource-Policy} header
|
||||
* @param resourcePolicy a {@code Cross-Origin-Resource-Policy}
|
||||
* @return the {@link CrossOriginResourcePolicyConfig} for additional
|
||||
* configuration
|
||||
* @throws IllegalArgumentException if resourcePolicy is null
|
||||
*/
|
||||
public CrossOriginResourcePolicyConfig policy(
|
||||
CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy resourcePolicy) {
|
||||
this.writer.setPolicy(resourcePolicy);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows completing configuration of Cross-Origin-Resource-Policy and continuing
|
||||
* configuration of headers.
|
||||
* @return the {@link HeadersConfigurer} for additional configuration
|
||||
*/
|
||||
public HeadersConfigurer<H> and() {
|
||||
return HeadersConfigurer.this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-15
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -48,22 +48,11 @@ final class PermitAllSupport {
|
||||
RequestMatcher... requestMatchers) {
|
||||
ExpressionUrlAuthorizationConfigurer<?> configurer = http
|
||||
.getConfigurer(ExpressionUrlAuthorizationConfigurer.class);
|
||||
AuthorizeHttpRequestsConfigurer<?> httpConfigurer = http.getConfigurer(AuthorizeHttpRequestsConfigurer.class);
|
||||
|
||||
boolean oneConfigurerPresent = configurer == null ^ httpConfigurer == null;
|
||||
Assert.state(oneConfigurerPresent,
|
||||
"permitAll only works with either HttpSecurity.authorizeRequests() or HttpSecurity.authorizeHttpRequests(). "
|
||||
+ "Please define one or the other but not both.");
|
||||
|
||||
Assert.state(configurer != null, "permitAll only works with HttpSecurity.authorizeRequests()");
|
||||
for (RequestMatcher matcher : requestMatchers) {
|
||||
if (matcher != null) {
|
||||
if (configurer != null) {
|
||||
configurer.getRegistry().addMapping(0, new UrlMapping(matcher,
|
||||
SecurityConfig.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
|
||||
}
|
||||
else {
|
||||
httpConfigurer.addFirst(matcher, AuthorizeHttpRequestsConfigurer.permitAllAuthorizationManager);
|
||||
}
|
||||
configurer.getRegistry().addMapping(0, new UrlMapping(matcher,
|
||||
SecurityConfig.createList(ExpressionUrlAuthorizationConfigurer.permitAll)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-33
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,8 +18,6 @@ package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.RememberMeAuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
@@ -153,10 +151,8 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* when a remember me token is valid. The default is to use the
|
||||
* {@link UserDetailsService} found by invoking
|
||||
* {@link HttpSecurity#getSharedObject(Class)} which is set when using
|
||||
* {@link WebSecurityConfigurerAdapter#configure(AuthenticationManagerBuilder)}. When
|
||||
* using a {@link org.springframework.security.web.SecurityFilterChain} bean, the
|
||||
* default is to look for a {@link UserDetailsService} bean. Alternatively, one can
|
||||
* populate {@link #rememberMeServices(RememberMeServices)}.
|
||||
* {@link WebSecurityConfigurerAdapter#configure(AuthenticationManagerBuilder)}.
|
||||
* Alternatively, one can populate {@link #rememberMeServices(RememberMeServices)}.
|
||||
* @param userDetailsService the {@link UserDetailsService} to configure
|
||||
* @return the {@link RememberMeConfigurer} for further customization
|
||||
* @see AbstractRememberMeServices
|
||||
@@ -399,16 +395,15 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link UserDetailsService} to use. Either the explicitly configured
|
||||
* {@link UserDetailsService} from {@link #userDetailsService(UserDetailsService)}, a
|
||||
* shared object from {@link HttpSecurity#getSharedObject(Class)} or the
|
||||
* {@link UserDetailsService} bean.
|
||||
* Gets the {@link UserDetailsService} to use. Either the explicitly configure
|
||||
* {@link UserDetailsService} from {@link #userDetailsService(UserDetailsService)} or
|
||||
* a shared object from {@link HttpSecurity#getSharedObject(Class)}.
|
||||
* @param http {@link HttpSecurity} to get the shared {@link UserDetailsService}
|
||||
* @return the {@link UserDetailsService} to use
|
||||
*/
|
||||
private UserDetailsService getUserDetailsService(H http) {
|
||||
if (this.userDetailsService == null) {
|
||||
this.userDetailsService = getSharedOrBean(http, UserDetailsService.class);
|
||||
this.userDetailsService = http.getSharedObject(UserDetailsService.class);
|
||||
}
|
||||
Assert.state(this.userDetailsService != null,
|
||||
() -> "userDetailsService cannot be null. Invoke " + RememberMeConfigurer.class.getSimpleName()
|
||||
@@ -436,25 +431,4 @@ public final class RememberMeConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
return this.key;
|
||||
}
|
||||
|
||||
private <C> C getSharedOrBean(H http, Class<C> type) {
|
||||
C shared = http.getSharedObject(type);
|
||||
if (shared != null) {
|
||||
return shared;
|
||||
}
|
||||
return getBeanOrNull(type);
|
||||
}
|
||||
|
||||
private <T> T getBeanOrNull(Class<T> type) {
|
||||
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return context.getBean(type);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-39
@@ -22,10 +22,8 @@ import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextHolderFilter;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
|
||||
|
||||
/**
|
||||
* Allows persisting and restoring of the {@link SecurityContext} found on the
|
||||
@@ -64,8 +62,6 @@ import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
|
||||
public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
extends AbstractHttpConfigurer<SecurityContextConfigurer<H>, H> {
|
||||
|
||||
private boolean requireExplicitSave;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @see HttpSecurity#securityContext()
|
||||
@@ -83,46 +79,23 @@ public final class SecurityContextConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
return this;
|
||||
}
|
||||
|
||||
public SecurityContextConfigurer<H> requireExplicitSave(boolean requireExplicitSave) {
|
||||
this.requireExplicitSave = requireExplicitSave;
|
||||
return this;
|
||||
}
|
||||
|
||||
boolean isRequireExplicitSave() {
|
||||
return this.requireExplicitSave;
|
||||
}
|
||||
|
||||
SecurityContextRepository getSecurityContextRepository() {
|
||||
SecurityContextRepository securityContextRepository = getBuilder()
|
||||
.getSharedObject(SecurityContextRepository.class);
|
||||
if (securityContextRepository == null) {
|
||||
securityContextRepository = new HttpSessionSecurityContextRepository();
|
||||
}
|
||||
return securityContextRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void configure(H http) {
|
||||
SecurityContextRepository securityContextRepository = getSecurityContextRepository();
|
||||
if (this.requireExplicitSave) {
|
||||
SecurityContextHolderFilter securityContextHolderFilter = postProcess(
|
||||
new SecurityContextHolderFilter(securityContextRepository));
|
||||
http.addFilter(securityContextHolderFilter);
|
||||
SecurityContextRepository securityContextRepository = http.getSharedObject(SecurityContextRepository.class);
|
||||
if (securityContextRepository == null) {
|
||||
securityContextRepository = new HttpSessionSecurityContextRepository();
|
||||
}
|
||||
else {
|
||||
SecurityContextPersistenceFilter securityContextFilter = new SecurityContextPersistenceFilter(
|
||||
securityContextRepository);
|
||||
SessionManagementConfigurer<?> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);
|
||||
SessionCreationPolicy sessionCreationPolicy = (sessionManagement != null)
|
||||
? sessionManagement.getSessionCreationPolicy() : null;
|
||||
if (SessionCreationPolicy.ALWAYS == sessionCreationPolicy) {
|
||||
securityContextFilter.setForceEagerSessionCreation(true);
|
||||
http.addFilter(postProcess(new ForceEagerSessionCreationFilter()));
|
||||
}
|
||||
securityContextFilter = postProcess(securityContextFilter);
|
||||
http.addFilter(securityContextFilter);
|
||||
SecurityContextPersistenceFilter securityContextFilter = new SecurityContextPersistenceFilter(
|
||||
securityContextRepository);
|
||||
SessionManagementConfigurer<?> sessionManagement = http.getConfigurer(SessionManagementConfigurer.class);
|
||||
SessionCreationPolicy sessionCreationPolicy = (sessionManagement != null)
|
||||
? sessionManagement.getSessionCreationPolicy() : null;
|
||||
if (SessionCreationPolicy.ALWAYS == sessionCreationPolicy) {
|
||||
securityContextFilter.setForceEagerSessionCreation(true);
|
||||
}
|
||||
securityContextFilter = postProcess(securityContextFilter);
|
||||
http.addFilter(securityContextFilter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-14
@@ -52,8 +52,6 @@ import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
||||
import org.springframework.security.web.session.DisableEncodeUrlFilter;
|
||||
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
|
||||
import org.springframework.security.web.session.InvalidSessionStrategy;
|
||||
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
@@ -203,12 +201,6 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
* {@link HttpServletResponse#encodeRedirectURL(String)} or
|
||||
* {@link HttpServletResponse#encodeURL(String)}, otherwise disallows HTTP sessions to
|
||||
* be included in the URL. This prevents leaking information to external domains.
|
||||
* <p>
|
||||
* This is achieved by guarding {@link HttpServletResponse#encodeURL} and
|
||||
* {@link HttpServletResponse#encodeRedirectURL} invocations. Any code that also
|
||||
* overrides either of these two methods, like
|
||||
* {@link org.springframework.web.servlet.resource.ResourceUrlEncodingFilter}, needs
|
||||
* to come after the security filter chain or risk being skipped.
|
||||
* @param enableSessionUrlRewriting true if should allow the JSESSIONID to be
|
||||
* rewritten into the URLs, else false (default)
|
||||
* @return the {@link SessionManagementConfigurer} for further customization
|
||||
@@ -378,12 +370,6 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
concurrentSessionFilter = postProcess(concurrentSessionFilter);
|
||||
http.addFilter(concurrentSessionFilter);
|
||||
}
|
||||
if (!this.enableSessionUrlRewriting) {
|
||||
http.addFilter(new DisableEncodeUrlFilter());
|
||||
}
|
||||
if (this.sessionPolicy == SessionCreationPolicy.ALWAYS) {
|
||||
http.addFilter(new ForceEagerSessionCreationFilter());
|
||||
}
|
||||
}
|
||||
|
||||
private ConcurrentSessionFilter createConcurrencyFilter(H http) {
|
||||
|
||||
+3
-30
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,19 +18,15 @@ package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.authentication.AuthenticationDetailsSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
@@ -145,9 +141,7 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
|
||||
/**
|
||||
* Specifies the {@link AuthenticationUserDetailsService} to use. If not specified,
|
||||
* the shared {@link UserDetailsService} will be used to create a
|
||||
* {@link UserDetailsByNameServiceWrapper}. If a {@link SecurityFilterChain} bean is
|
||||
* used instead of the {@link WebSecurityConfigurerAdapter}, then the
|
||||
* {@link UserDetailsService} bean will be used by default.
|
||||
* {@link UserDetailsByNameServiceWrapper}.
|
||||
* @param authenticationUserDetailsService the
|
||||
* {@link AuthenticationUserDetailsService} to use
|
||||
* @return the {@link X509Configurer} for further customizations
|
||||
@@ -206,30 +200,9 @@ public final class X509Configurer<H extends HttpSecurityBuilder<H>>
|
||||
private AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> getAuthenticationUserDetailsService(
|
||||
H http) {
|
||||
if (this.authenticationUserDetailsService == null) {
|
||||
userDetailsService(getSharedOrBean(http, UserDetailsService.class));
|
||||
userDetailsService(http.getSharedObject(UserDetailsService.class));
|
||||
}
|
||||
return this.authenticationUserDetailsService;
|
||||
}
|
||||
|
||||
private <C> C getSharedOrBean(H http, Class<C> type) {
|
||||
C shared = http.getSharedObject(type);
|
||||
if (shared != null) {
|
||||
return shared;
|
||||
}
|
||||
return getBeanOrNull(type);
|
||||
}
|
||||
|
||||
private <T> T getBeanOrNull(Class<T> type) {
|
||||
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return context.getBean(type);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-15
@@ -18,9 +18,6 @@ package org.springframework.security.config.annotation.web.configurers.oauth2.se
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -28,7 +25,6 @@ import javax.servlet.http.HttpServletRequest;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationManagerResolver;
|
||||
@@ -55,9 +51,6 @@ import org.springframework.security.oauth2.server.resource.web.DefaultBearerToke
|
||||
import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.access.AccessDeniedHandler;
|
||||
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
|
||||
import org.springframework.security.web.access.DelegatingAccessDeniedHandler;
|
||||
import org.springframework.security.web.csrf.CsrfException;
|
||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.NegatedRequestMatcher;
|
||||
@@ -160,19 +153,12 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
|
||||
|
||||
private OpaqueTokenConfigurer opaqueTokenConfigurer;
|
||||
|
||||
private AccessDeniedHandler accessDeniedHandler = new DelegatingAccessDeniedHandler(
|
||||
new LinkedHashMap<>(createAccessDeniedHandlers()), new BearerTokenAccessDeniedHandler());
|
||||
private AccessDeniedHandler accessDeniedHandler = new BearerTokenAccessDeniedHandler();
|
||||
|
||||
private AuthenticationEntryPoint authenticationEntryPoint = new BearerTokenAuthenticationEntryPoint();
|
||||
|
||||
private BearerTokenRequestMatcher requestMatcher = new BearerTokenRequestMatcher();
|
||||
|
||||
private static Map<Class<? extends AccessDeniedException>, AccessDeniedHandler> createAccessDeniedHandlers() {
|
||||
Map<Class<? extends AccessDeniedException>, AccessDeniedHandler> handlers = new HashMap<>();
|
||||
handlers.put(CsrfException.class, new AccessDeniedHandlerImpl());
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public OAuth2ResourceServerConfigurer(ApplicationContext context) {
|
||||
Assert.notNull(context, "context cannot be null");
|
||||
this.context = context;
|
||||
|
||||
+54
-74
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,6 +19,8 @@ package org.springframework.security.config.annotation.web.configurers.saml2;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
|
||||
import org.opensaml.core.Version;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
@@ -48,7 +50,6 @@ import org.springframework.security.saml2.provider.service.web.RelyingPartyRegis
|
||||
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.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
|
||||
import org.springframework.security.web.authentication.AuthenticationConverter;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
@@ -114,12 +115,10 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
|
||||
private String loginPage;
|
||||
|
||||
private String authenticationRequestUri = "/saml2/authenticate/{registrationId}";
|
||||
|
||||
private Saml2AuthenticationRequestResolver authenticationRequestResolver;
|
||||
|
||||
private String loginProcessingUrl = Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;
|
||||
|
||||
private AuthenticationRequestEndpointConfig authenticationRequestEndpoint = new AuthenticationRequestEndpointConfig();
|
||||
|
||||
private RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;
|
||||
|
||||
private AuthenticationConverter authenticationConverter;
|
||||
@@ -177,20 +176,6 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this {@link Saml2AuthenticationRequestResolver} for generating SAML 2.0
|
||||
* Authentication Requests.
|
||||
* @param authenticationRequestResolver
|
||||
* @return the {@link Saml2LoginConfigurer} for further configuration
|
||||
* @since 5.7
|
||||
*/
|
||||
public Saml2LoginConfigurer<B> authenticationRequestResolver(
|
||||
Saml2AuthenticationRequestResolver authenticationRequestResolver) {
|
||||
Assert.notNull(authenticationRequestResolver, "authenticationRequestResolver cannot be null");
|
||||
this.authenticationRequestResolver = authenticationRequestResolver;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies the URL to validate the credentials. If specified a custom URL, consider
|
||||
* specifying a custom {@link AuthenticationConverter} via
|
||||
@@ -215,7 +200,7 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
*
|
||||
* Initializes this filter chain for SAML 2 Login. The following actions are taken:
|
||||
* <ul>
|
||||
* <li>The WebSSO endpoint has CSRF disabled, typically {@code /login/saml2/sso}</li>
|
||||
@@ -241,8 +226,8 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
super.init(http);
|
||||
}
|
||||
else {
|
||||
Map<String, String> providerUrlMap = getIdentityProviderUrlMap(this.authenticationRequestUri,
|
||||
this.relyingPartyRegistrationRepository);
|
||||
Map<String, String> providerUrlMap = getIdentityProviderUrlMap(
|
||||
this.authenticationRequestEndpoint.filterProcessingUrl, this.relyingPartyRegistrationRepository);
|
||||
boolean singleProvider = providerUrlMap.size() == 1;
|
||||
if (singleProvider) {
|
||||
// Setup auto-redirect to provider login page
|
||||
@@ -262,16 +247,14 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
*
|
||||
* During the {@code configure} phase, a
|
||||
* {@link Saml2WebSsoAuthenticationRequestFilter} is added to handle SAML 2.0
|
||||
* AuthNRequest redirects
|
||||
*/
|
||||
@Override
|
||||
public void configure(B http) throws Exception {
|
||||
Saml2WebSsoAuthenticationRequestFilter filter = getAuthenticationRequestFilter(http);
|
||||
filter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));
|
||||
http.addFilter(postProcess(filter));
|
||||
http.addFilter(this.authenticationRequestEndpoint.build(http));
|
||||
super.configure(http);
|
||||
if (this.authenticationManager == null) {
|
||||
registerDefaultAuthenticationProvider(http);
|
||||
@@ -281,11 +264,6 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
}
|
||||
}
|
||||
|
||||
private RelyingPartyRegistrationResolver relyingPartyRegistrationResolver(B http) {
|
||||
RelyingPartyRegistrationRepository registrations = relyingPartyRegistrationRepository(http);
|
||||
return new DefaultRelyingPartyRegistrationResolver(registrations);
|
||||
}
|
||||
|
||||
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository(B http) {
|
||||
if (this.relyingPartyRegistrationRepository == null) {
|
||||
this.relyingPartyRegistrationRepository = getSharedOrBean(http, RelyingPartyRegistrationRepository.class);
|
||||
@@ -298,46 +276,6 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
saml2WebSsoAuthenticationFilter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));
|
||||
}
|
||||
|
||||
private Saml2WebSsoAuthenticationRequestFilter getAuthenticationRequestFilter(B http) {
|
||||
Saml2AuthenticationRequestResolver authenticationRequestResolver = getAuthenticationRequestResolver(http);
|
||||
if (authenticationRequestResolver != null) {
|
||||
return new Saml2WebSsoAuthenticationRequestFilter(authenticationRequestResolver);
|
||||
}
|
||||
return new Saml2WebSsoAuthenticationRequestFilter(getAuthenticationRequestContextResolver(http),
|
||||
getAuthenticationRequestFactory(http));
|
||||
}
|
||||
|
||||
private Saml2AuthenticationRequestResolver getAuthenticationRequestResolver(B http) {
|
||||
if (this.authenticationRequestResolver != null) {
|
||||
return this.authenticationRequestResolver;
|
||||
}
|
||||
return getBeanOrNull(http, Saml2AuthenticationRequestResolver.class);
|
||||
}
|
||||
|
||||
private Saml2AuthenticationRequestFactory getAuthenticationRequestFactory(B http) {
|
||||
Saml2AuthenticationRequestFactory resolver = getSharedOrBean(http, Saml2AuthenticationRequestFactory.class);
|
||||
if (resolver != null) {
|
||||
return resolver;
|
||||
}
|
||||
if (version().startsWith("4")) {
|
||||
return new OpenSaml4AuthenticationRequestFactory();
|
||||
}
|
||||
else {
|
||||
return new OpenSamlAuthenticationRequestFactory();
|
||||
}
|
||||
}
|
||||
|
||||
private Saml2AuthenticationRequestContextResolver getAuthenticationRequestContextResolver(B http) {
|
||||
Saml2AuthenticationRequestContextResolver resolver = getBeanOrNull(http,
|
||||
Saml2AuthenticationRequestContextResolver.class);
|
||||
if (resolver != null) {
|
||||
return resolver;
|
||||
}
|
||||
RelyingPartyRegistrationResolver registrationResolver = new DefaultRelyingPartyRegistrationResolver(
|
||||
this.relyingPartyRegistrationRepository);
|
||||
return new DefaultSaml2AuthenticationRequestContextResolver(registrationResolver);
|
||||
}
|
||||
|
||||
private AuthenticationConverter getAuthenticationConverter(B http) {
|
||||
if (this.authenticationConverter != null) {
|
||||
return this.authenticationConverter;
|
||||
@@ -386,8 +324,8 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
return;
|
||||
}
|
||||
loginPageGeneratingFilter.setSaml2LoginEnabled(true);
|
||||
loginPageGeneratingFilter.setSaml2AuthenticationUrlToProviderName(
|
||||
this.getIdentityProviderUrlMap(this.authenticationRequestUri, this.relyingPartyRegistrationRepository));
|
||||
loginPageGeneratingFilter.setSaml2AuthenticationUrlToProviderName(this.getIdentityProviderUrlMap(
|
||||
this.authenticationRequestEndpoint.filterProcessingUrl, this.relyingPartyRegistrationRepository));
|
||||
loginPageGeneratingFilter.setLoginPageUrl(this.getLoginPage());
|
||||
loginPageGeneratingFilter.setFailureUrl(this.getFailureUrl());
|
||||
}
|
||||
@@ -441,4 +379,46 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
|
||||
}
|
||||
}
|
||||
|
||||
private final class AuthenticationRequestEndpointConfig {
|
||||
|
||||
private String filterProcessingUrl = "/saml2/authenticate/{registrationId}";
|
||||
|
||||
private AuthenticationRequestEndpointConfig() {
|
||||
}
|
||||
|
||||
private Filter build(B http) {
|
||||
Saml2AuthenticationRequestFactory authenticationRequestResolver = getResolver(http);
|
||||
Saml2AuthenticationRequestContextResolver contextResolver = getContextResolver(http);
|
||||
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().startsWith("4")) {
|
||||
return new OpenSaml4AuthenticationRequestFactory();
|
||||
}
|
||||
return new OpenSamlAuthenticationRequestFactory();
|
||||
}
|
||||
return resolver;
|
||||
}
|
||||
|
||||
private Saml2AuthenticationRequestContextResolver getContextResolver(B http) {
|
||||
Saml2AuthenticationRequestContextResolver resolver = getBeanOrNull(http,
|
||||
Saml2AuthenticationRequestContextResolver.class);
|
||||
if (resolver == null) {
|
||||
RelyingPartyRegistrationResolver relyingPartyRegistrationResolver = new DefaultRelyingPartyRegistrationResolver(
|
||||
Saml2LoginConfigurer.this.relyingPartyRegistrationRepository);
|
||||
return new DefaultSaml2AuthenticationRequestContextResolver(relyingPartyRegistrationResolver);
|
||||
}
|
||||
return resolver;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-31
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -22,7 +22,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;
|
||||
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProviderBuilder;
|
||||
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;
|
||||
@@ -42,7 +41,6 @@ import org.springframework.web.reactive.result.method.annotation.ArgumentResolve
|
||||
* This {@code Configuration} is imported by {@link EnableWebFluxSecurity}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Alavudin Kuttikkattil
|
||||
* @since 5.1
|
||||
*/
|
||||
final class ReactiveOAuth2ClientImportSelector implements ImportSelector {
|
||||
@@ -66,12 +64,14 @@ final class ReactiveOAuth2ClientImportSelector implements ImportSelector {
|
||||
|
||||
private ReactiveOAuth2AuthorizedClientService authorizedClientService;
|
||||
|
||||
private ReactiveOAuth2AuthorizedClientManager authorizedClientManager;
|
||||
|
||||
@Override
|
||||
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
|
||||
ReactiveOAuth2AuthorizedClientManager authorizedClientManager = getAuthorizedClientManager();
|
||||
if (authorizedClientManager != null) {
|
||||
if (this.authorizedClientRepository != null && this.clientRegistrationRepository != null) {
|
||||
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder
|
||||
.builder().authorizationCode().refreshToken().clientCredentials().password().build();
|
||||
DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(
|
||||
this.clientRegistrationRepository, getAuthorizedClientRepository());
|
||||
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
|
||||
configurer.addCustomResolver(new OAuth2AuthorizedClientArgumentResolver(authorizedClientManager));
|
||||
}
|
||||
}
|
||||
@@ -93,13 +93,6 @@ final class ReactiveOAuth2ClientImportSelector implements ImportSelector {
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setAuthorizedClientManager(List<ReactiveOAuth2AuthorizedClientManager> authorizedClientManager) {
|
||||
if (authorizedClientManager.size() == 1) {
|
||||
this.authorizedClientManager = authorizedClientManager.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
private ServerOAuth2AuthorizedClientRepository getAuthorizedClientRepository() {
|
||||
if (this.authorizedClientRepository != null) {
|
||||
return this.authorizedClientRepository;
|
||||
@@ -110,23 +103,6 @@ final class ReactiveOAuth2ClientImportSelector implements ImportSelector {
|
||||
return null;
|
||||
}
|
||||
|
||||
private ReactiveOAuth2AuthorizedClientManager getAuthorizedClientManager() {
|
||||
if (this.authorizedClientManager != null) {
|
||||
return this.authorizedClientManager;
|
||||
}
|
||||
ReactiveOAuth2AuthorizedClientManager authorizedClientManager = null;
|
||||
if (this.authorizedClientRepository != null && this.clientRegistrationRepository != null) {
|
||||
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder
|
||||
.builder().authorizationCode().refreshToken().clientCredentials().password().build();
|
||||
DefaultReactiveOAuth2AuthorizedClientManager defaultReactiveOAuth2AuthorizedClientManager = new DefaultReactiveOAuth2AuthorizedClientManager(
|
||||
this.clientRegistrationRepository, getAuthorizedClientRepository());
|
||||
defaultReactiveOAuth2AuthorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
|
||||
authorizedClientManager = defaultReactiveOAuth2AuthorizedClientManager;
|
||||
}
|
||||
|
||||
return authorizedClientManager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-3
@@ -102,9 +102,7 @@ public class AuthenticationManagerBeanDefinitionParser implements BeanDefinition
|
||||
pc.getRegistry().registerAlias(id, alias);
|
||||
pc.getReaderContext().fireAliasRegistered(id, alias, pc.extractSource(element));
|
||||
}
|
||||
if (!BeanIds.AUTHENTICATION_MANAGER.equals(id)
|
||||
&& !pc.getRegistry().containsBeanDefinition(BeanIds.AUTHENTICATION_MANAGER)
|
||||
&& !pc.getRegistry().isAlias(BeanIds.AUTHENTICATION_MANAGER)) {
|
||||
if (!BeanIds.AUTHENTICATION_MANAGER.equals(id)) {
|
||||
pc.getRegistry().registerAlias(id, BeanIds.AUTHENTICATION_MANAGER);
|
||||
pc.getReaderContext().fireAliasRegistered(id, BeanIds.AUTHENTICATION_MANAGER, pc.extractSource(element));
|
||||
}
|
||||
|
||||
+15
-122
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -164,8 +164,6 @@ final class AuthenticationConfigBuilder {
|
||||
@SuppressWarnings("rawtypes")
|
||||
private ManagedList logoutHandlers;
|
||||
|
||||
private BeanMetadataElement logoutSuccessHandler;
|
||||
|
||||
private BeanDefinition loginPageGenerationFilter;
|
||||
|
||||
private BeanDefinition logoutPageGenerationFilter;
|
||||
@@ -204,20 +202,6 @@ final class AuthenticationConfigBuilder {
|
||||
|
||||
private BeanDefinition oauth2LoginLinks;
|
||||
|
||||
private BeanDefinition saml2AuthenticationUrlToProviderName;
|
||||
|
||||
private BeanDefinition saml2AuthorizationRequestFilter;
|
||||
|
||||
private String saml2AuthenticationFilterId;
|
||||
|
||||
private String saml2AuthenticationRequestFilterId;
|
||||
|
||||
private String saml2LogoutFilterId;
|
||||
|
||||
private String saml2LogoutRequestFilterId;
|
||||
|
||||
private String saml2LogoutResponseFilterId;
|
||||
|
||||
private boolean oauth2ClientEnabled;
|
||||
|
||||
private BeanDefinition authorizationRequestRedirectFilter;
|
||||
@@ -236,8 +220,8 @@ final class AuthenticationConfigBuilder {
|
||||
|
||||
AuthenticationConfigBuilder(Element element, boolean forceAutoConfig, ParserContext pc,
|
||||
SessionCreationPolicy sessionPolicy, BeanReference requestCache, BeanReference authenticationManager,
|
||||
BeanReference authenticationFilterSecurityContextRepositoryRef, BeanReference sessionStrategy,
|
||||
BeanReference portMapper, BeanReference portResolver, BeanMetadataElement csrfLogoutHandler) {
|
||||
BeanReference sessionStrategy, BeanReference portMapper, BeanReference portResolver,
|
||||
BeanMetadataElement csrfLogoutHandler) {
|
||||
this.httpElt = element;
|
||||
this.pc = pc;
|
||||
this.requestCache = requestCache;
|
||||
@@ -251,16 +235,12 @@ final class AuthenticationConfigBuilder {
|
||||
createRememberMeFilter(authenticationManager);
|
||||
createBasicFilter(authenticationManager);
|
||||
createBearerTokenAuthenticationFilter(authenticationManager);
|
||||
createFormLoginFilter(sessionStrategy, authenticationManager, authenticationFilterSecurityContextRepositoryRef);
|
||||
createOAuth2ClientFilters(sessionStrategy, requestCache, authenticationManager,
|
||||
authenticationFilterSecurityContextRepositoryRef);
|
||||
createOpenIDLoginFilter(sessionStrategy, authenticationManager,
|
||||
authenticationFilterSecurityContextRepositoryRef);
|
||||
createSaml2LoginFilter(authenticationManager, authenticationFilterSecurityContextRepositoryRef);
|
||||
createFormLoginFilter(sessionStrategy, authenticationManager);
|
||||
createOAuth2ClientFilters(sessionStrategy, requestCache, authenticationManager);
|
||||
createOpenIDLoginFilter(sessionStrategy, authenticationManager);
|
||||
createX509Filter(authenticationManager);
|
||||
createJeeFilter(authenticationManager);
|
||||
createLogoutFilter();
|
||||
createSaml2LogoutFilter();
|
||||
createLoginPageFilterIfNeeded();
|
||||
createUserDetailsServiceFactory();
|
||||
createExceptionTranslationFilter();
|
||||
@@ -292,8 +272,7 @@ final class AuthenticationConfigBuilder {
|
||||
this.rememberMeProviderRef = new RuntimeBeanReference(id);
|
||||
}
|
||||
|
||||
void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authManager,
|
||||
BeanReference authenticationFilterSecurityContextRepositoryRef) {
|
||||
void createFormLoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
|
||||
Element formLoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.FORM_LOGIN);
|
||||
RootBeanDefinition formFilter = null;
|
||||
if (formLoginElt != null || this.autoConfig) {
|
||||
@@ -309,10 +288,6 @@ final class AuthenticationConfigBuilder {
|
||||
if (formFilter != null) {
|
||||
formFilter.getPropertyValues().addPropertyValue("allowSessionCreation", this.allowSessionCreation);
|
||||
formFilter.getPropertyValues().addPropertyValue("authenticationManager", authManager);
|
||||
if (authenticationFilterSecurityContextRepositoryRef != null) {
|
||||
formFilter.getPropertyValues().addPropertyValue("securityContextRepository",
|
||||
authenticationFilterSecurityContextRepositoryRef);
|
||||
}
|
||||
// Id is required by login page filter
|
||||
this.formFilterId = this.pc.getReaderContext().generateBeanName(formFilter);
|
||||
this.pc.registerBeanComponent(new BeanComponentDefinition(formFilter, this.formFilterId));
|
||||
@@ -321,15 +296,13 @@ final class AuthenticationConfigBuilder {
|
||||
}
|
||||
|
||||
void createOAuth2ClientFilters(BeanReference sessionStrategy, BeanReference requestCache,
|
||||
BeanReference authenticationManager, BeanReference authenticationFilterSecurityContextRepositoryRef) {
|
||||
createOAuth2LoginFilter(sessionStrategy, authenticationManager,
|
||||
authenticationFilterSecurityContextRepositoryRef);
|
||||
createOAuth2ClientFilter(requestCache, authenticationManager, authenticationFilterSecurityContextRepositoryRef);
|
||||
BeanReference authenticationManager) {
|
||||
createOAuth2LoginFilter(sessionStrategy, authenticationManager);
|
||||
createOAuth2ClientFilter(requestCache, authenticationManager);
|
||||
registerOAuth2ClientPostProcessors();
|
||||
}
|
||||
|
||||
void createOAuth2LoginFilter(BeanReference sessionStrategy, BeanReference authManager,
|
||||
BeanReference authenticationFilterSecurityContextRepositoryRef) {
|
||||
void createOAuth2LoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
|
||||
Element oauth2LoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_LOGIN);
|
||||
if (oauth2LoginElt == null) {
|
||||
return;
|
||||
@@ -341,10 +314,6 @@ final class AuthenticationConfigBuilder {
|
||||
BeanDefinition defaultAuthorizedClientRepository = parser.getDefaultAuthorizedClientRepository();
|
||||
registerDefaultAuthorizedClientRepositoryIfNecessary(defaultAuthorizedClientRepository);
|
||||
oauth2LoginFilterBean.getPropertyValues().addPropertyValue("authenticationManager", authManager);
|
||||
if (authenticationFilterSecurityContextRepositoryRef != null) {
|
||||
oauth2LoginFilterBean.getPropertyValues().addPropertyValue("securityContextRepository",
|
||||
authenticationFilterSecurityContextRepositoryRef);
|
||||
}
|
||||
|
||||
// retrieve the other bean result
|
||||
BeanDefinition oauth2LoginAuthProvider = parser.getOAuth2LoginAuthenticationProvider();
|
||||
@@ -374,15 +343,14 @@ final class AuthenticationConfigBuilder {
|
||||
this.oauth2LoginOidcAuthenticationProviderRef = new RuntimeBeanReference(oauth2LoginOidcAuthProviderId);
|
||||
}
|
||||
|
||||
void createOAuth2ClientFilter(BeanReference requestCache, BeanReference authenticationManager,
|
||||
BeanReference authenticationFilterSecurityContextRepositoryRef) {
|
||||
void createOAuth2ClientFilter(BeanReference requestCache, BeanReference authenticationManager) {
|
||||
Element oauth2ClientElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OAUTH2_CLIENT);
|
||||
if (oauth2ClientElt == null) {
|
||||
return;
|
||||
}
|
||||
this.oauth2ClientEnabled = true;
|
||||
OAuth2ClientBeanDefinitionParser parser = new OAuth2ClientBeanDefinitionParser(requestCache,
|
||||
authenticationManager, authenticationFilterSecurityContextRepositoryRef);
|
||||
authenticationManager);
|
||||
parser.parse(oauth2ClientElt, this.pc);
|
||||
BeanDefinition defaultAuthorizedClientRepository = parser.getDefaultAuthorizedClientRepository();
|
||||
registerDefaultAuthorizedClientRepositoryIfNecessary(defaultAuthorizedClientRepository);
|
||||
@@ -427,8 +395,7 @@ final class AuthenticationConfigBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
void createOpenIDLoginFilter(BeanReference sessionStrategy, BeanReference authManager,
|
||||
BeanReference authenticationFilterSecurityContextRepositoryRef) {
|
||||
void createOpenIDLoginFilter(BeanReference sessionStrategy, BeanReference authManager) {
|
||||
Element openIDLoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.OPENID_LOGIN);
|
||||
RootBeanDefinition openIDFilter = null;
|
||||
if (openIDLoginElt != null) {
|
||||
@@ -437,10 +404,6 @@ final class AuthenticationConfigBuilder {
|
||||
if (openIDFilter != null) {
|
||||
openIDFilter.getPropertyValues().addPropertyValue("allowSessionCreation", this.allowSessionCreation);
|
||||
openIDFilter.getPropertyValues().addPropertyValue("authenticationManager", authManager);
|
||||
if (authenticationFilterSecurityContextRepositoryRef != null) {
|
||||
openIDFilter.getPropertyValues().addPropertyValue("securityContextRepository",
|
||||
authenticationFilterSecurityContextRepositoryRef);
|
||||
}
|
||||
// Required by login page filter
|
||||
this.openIDFilterId = this.pc.getReaderContext().generateBeanName(openIDFilter);
|
||||
this.pc.registerBeanComponent(new BeanComponentDefinition(openIDFilter, this.openIDFilterId));
|
||||
@@ -449,31 +412,6 @@ final class AuthenticationConfigBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
private void createSaml2LoginFilter(BeanReference authenticationManager,
|
||||
BeanReference authenticationFilterSecurityContextRepositoryRef) {
|
||||
Element saml2LoginElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.SAML2_LOGIN);
|
||||
if (saml2LoginElt == null) {
|
||||
return;
|
||||
}
|
||||
Saml2LoginBeanDefinitionParser parser = new Saml2LoginBeanDefinitionParser(this.csrfIgnoreRequestMatchers,
|
||||
this.portMapper, this.portResolver, this.requestCache, this.allowSessionCreation, authenticationManager,
|
||||
authenticationFilterSecurityContextRepositoryRef, this.authenticationProviders,
|
||||
this.defaultEntryPointMappings);
|
||||
BeanDefinition saml2WebSsoAuthenticationFilter = parser.parse(saml2LoginElt, this.pc);
|
||||
this.saml2AuthorizationRequestFilter = parser.getSaml2WebSsoAuthenticationRequestFilter();
|
||||
|
||||
this.saml2AuthenticationFilterId = this.pc.getReaderContext().generateBeanName(saml2WebSsoAuthenticationFilter);
|
||||
this.saml2AuthenticationRequestFilterId = this.pc.getReaderContext()
|
||||
.generateBeanName(this.saml2AuthorizationRequestFilter);
|
||||
this.saml2AuthenticationUrlToProviderName = parser.getSaml2AuthenticationUrlToProviderName();
|
||||
|
||||
// register the component
|
||||
this.pc.registerBeanComponent(
|
||||
new BeanComponentDefinition(saml2WebSsoAuthenticationFilter, this.saml2AuthenticationFilterId));
|
||||
this.pc.registerBeanComponent(new BeanComponentDefinition(this.saml2AuthorizationRequestFilter,
|
||||
this.saml2AuthenticationRequestFilterId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses OpenID 1.0 and 2.0 - related parts of configuration xmls
|
||||
* @param sessionStrategy sessionStrategy
|
||||
@@ -728,12 +666,6 @@ final class AuthenticationConfigBuilder {
|
||||
loginPageFilter.addPropertyValue("Oauth2LoginEnabled", true);
|
||||
loginPageFilter.addPropertyValue("Oauth2AuthenticationUrlToClientName", this.oauth2LoginLinks);
|
||||
}
|
||||
if (this.saml2AuthenticationFilterId != null) {
|
||||
loginPageFilter.addConstructorArgReference(this.saml2AuthenticationFilterId);
|
||||
loginPageFilter.addPropertyValue("saml2LoginEnabled", true);
|
||||
loginPageFilter.addPropertyValue("saml2AuthenticationUrlToProviderName",
|
||||
this.saml2AuthenticationUrlToProviderName);
|
||||
}
|
||||
this.loginPageGenerationFilter = loginPageFilter.getBeanDefinition();
|
||||
this.logoutPageGenerationFilter = logoutPageFilter.getBeanDefinition();
|
||||
}
|
||||
@@ -750,33 +682,9 @@ final class AuthenticationConfigBuilder {
|
||||
this.rememberMeServicesId, this.csrfLogoutHandler);
|
||||
this.logoutFilter = logoutParser.parse(logoutElt, this.pc);
|
||||
this.logoutHandlers = logoutParser.getLogoutHandlers();
|
||||
this.logoutSuccessHandler = logoutParser.getLogoutSuccessHandler();
|
||||
}
|
||||
}
|
||||
|
||||
private void createSaml2LogoutFilter() {
|
||||
Element saml2LogoutElt = DomUtils.getChildElementByTagName(this.httpElt, Elements.SAML2_LOGOUT);
|
||||
if (saml2LogoutElt == null) {
|
||||
return;
|
||||
}
|
||||
Saml2LogoutBeanDefinitionParser parser = new Saml2LogoutBeanDefinitionParser(this.logoutHandlers,
|
||||
this.logoutSuccessHandler);
|
||||
parser.parse(saml2LogoutElt, this.pc);
|
||||
BeanDefinition saml2LogoutFilter = parser.getLogoutFilter();
|
||||
BeanDefinition saml2LogoutRequestFilter = parser.getLogoutRequestFilter();
|
||||
BeanDefinition saml2LogoutResponseFilter = parser.getLogoutResponseFilter();
|
||||
this.saml2LogoutFilterId = this.pc.getReaderContext().generateBeanName(saml2LogoutFilter);
|
||||
this.saml2LogoutRequestFilterId = this.pc.getReaderContext().generateBeanName(saml2LogoutRequestFilter);
|
||||
this.saml2LogoutResponseFilterId = this.pc.getReaderContext().generateBeanName(saml2LogoutResponseFilter);
|
||||
|
||||
// register the component
|
||||
this.pc.registerBeanComponent(new BeanComponentDefinition(saml2LogoutFilter, this.saml2LogoutFilterId));
|
||||
this.pc.registerBeanComponent(
|
||||
new BeanComponentDefinition(saml2LogoutRequestFilter, this.saml2LogoutRequestFilterId));
|
||||
this.pc.registerBeanComponent(
|
||||
new BeanComponentDefinition(saml2LogoutResponseFilter, this.saml2LogoutResponseFilterId));
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
ManagedList getLogoutHandlers() {
|
||||
if (this.logoutHandlers == null && this.rememberMeProviderRef != null) {
|
||||
@@ -932,8 +840,7 @@ final class AuthenticationConfigBuilder {
|
||||
if (formLoginElt != null && this.oauth2LoginEntryPoint != null) {
|
||||
return this.formEntryPoint;
|
||||
}
|
||||
// If form login was enabled through auto-config, and Oauth2 login & Saml2
|
||||
// login was not
|
||||
// If form login was enabled through auto-config, and Oauth2 login was not
|
||||
// enabled then use form login
|
||||
if (this.oauth2LoginEntryPoint == null) {
|
||||
return this.formEntryPoint;
|
||||
@@ -1016,20 +923,6 @@ final class AuthenticationConfigBuilder {
|
||||
filters.add(new OrderDecorator(this.authorizationCodeGrantFilter,
|
||||
SecurityFilters.OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER));
|
||||
}
|
||||
if (this.saml2AuthenticationFilterId != null) {
|
||||
filters.add(new OrderDecorator(new RuntimeBeanReference(this.saml2AuthenticationFilterId),
|
||||
SecurityFilters.SAML2_AUTHENTICATION_FILTER));
|
||||
filters.add(new OrderDecorator(new RuntimeBeanReference(this.saml2AuthenticationRequestFilterId),
|
||||
SecurityFilters.SAML2_AUTHENTICATION_REQUEST_FILTER));
|
||||
}
|
||||
if (this.saml2LogoutFilterId != null) {
|
||||
filters.add(new OrderDecorator(new RuntimeBeanReference(this.saml2LogoutFilterId),
|
||||
SecurityFilters.SAML2_LOGOUT_FILTER));
|
||||
filters.add(new OrderDecorator(new RuntimeBeanReference(this.saml2LogoutRequestFilterId),
|
||||
SecurityFilters.SAML2_LOGOUT_REQUEST_FILTER));
|
||||
filters.add(new OrderDecorator(new RuntimeBeanReference(this.saml2LogoutResponseFilterId),
|
||||
SecurityFilters.SAML2_LOGOUT_RESPONSE_FILTER));
|
||||
}
|
||||
filters.add(new OrderDecorator(this.etf, SecurityFilters.EXCEPTION_TRANSLATION_FILTER));
|
||||
return filters;
|
||||
}
|
||||
|
||||
+1
-82
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -36,9 +36,6 @@ import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.security.web.header.HeaderWriterFilter;
|
||||
import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
|
||||
import org.springframework.security.web.header.writers.ContentSecurityPolicyHeaderWriter;
|
||||
import org.springframework.security.web.header.writers.CrossOriginEmbedderPolicyHeaderWriter;
|
||||
import org.springframework.security.web.header.writers.CrossOriginOpenerPolicyHeaderWriter;
|
||||
import org.springframework.security.web.header.writers.CrossOriginResourcePolicyHeaderWriter;
|
||||
import org.springframework.security.web.header.writers.FeaturePolicyHeaderWriter;
|
||||
import org.springframework.security.web.header.writers.HpkpHeaderWriter;
|
||||
import org.springframework.security.web.header.writers.HstsHeaderWriter;
|
||||
@@ -125,12 +122,6 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String PERMISSIONS_POLICY_ELEMENT = "permissions-policy";
|
||||
|
||||
private static final String CROSS_ORIGIN_OPENER_POLICY_ELEMENT = "cross-origin-opener-policy";
|
||||
|
||||
private static final String CROSS_ORIGIN_EMBEDDER_POLICY_ELEMENT = "cross-origin-embedder-policy";
|
||||
|
||||
private static final String CROSS_ORIGIN_RESOURCE_POLICY_ELEMENT = "cross-origin-resource-policy";
|
||||
|
||||
private static final String ALLOW_FROM = "ALLOW-FROM";
|
||||
|
||||
private ManagedList<BeanMetadataElement> headerWriters;
|
||||
@@ -153,9 +144,6 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
|
||||
parseReferrerPolicyElement(element, parserContext);
|
||||
parseFeaturePolicyElement(element, parserContext);
|
||||
parsePermissionsPolicyElement(element, parserContext);
|
||||
parseCrossOriginOpenerPolicy(disabled, element);
|
||||
parseCrossOriginEmbedderPolicy(disabled, element);
|
||||
parseCrossOriginResourcePolicy(disabled, element);
|
||||
parseHeaderElements(element);
|
||||
boolean noWriters = this.headerWriters.isEmpty();
|
||||
if (disabled && !noWriters) {
|
||||
@@ -388,75 +376,6 @@ public class HeadersBeanDefinitionParser implements BeanDefinitionParser {
|
||||
this.headerWriters.add(headersWriter.getBeanDefinition());
|
||||
}
|
||||
|
||||
private void parseCrossOriginOpenerPolicy(boolean elementDisabled, Element element) {
|
||||
if (elementDisabled || element == null) {
|
||||
return;
|
||||
}
|
||||
CrossOriginOpenerPolicyHeaderWriter writer = new CrossOriginOpenerPolicyHeaderWriter();
|
||||
Element crossOriginOpenerPolicyElement = DomUtils.getChildElementByTagName(element,
|
||||
CROSS_ORIGIN_OPENER_POLICY_ELEMENT);
|
||||
if (crossOriginOpenerPolicyElement != null) {
|
||||
addCrossOriginOpenerPolicy(crossOriginOpenerPolicyElement, writer);
|
||||
}
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(CrossOriginOpenerPolicyHeaderWriter.class, () -> writer);
|
||||
this.headerWriters.add(builder.getBeanDefinition());
|
||||
}
|
||||
|
||||
private void parseCrossOriginEmbedderPolicy(boolean elementDisabled, Element element) {
|
||||
if (elementDisabled || element == null) {
|
||||
return;
|
||||
}
|
||||
CrossOriginEmbedderPolicyHeaderWriter writer = new CrossOriginEmbedderPolicyHeaderWriter();
|
||||
Element crossOriginEmbedderPolicyElement = DomUtils.getChildElementByTagName(element,
|
||||
CROSS_ORIGIN_EMBEDDER_POLICY_ELEMENT);
|
||||
if (crossOriginEmbedderPolicyElement != null) {
|
||||
addCrossOriginEmbedderPolicy(crossOriginEmbedderPolicyElement, writer);
|
||||
}
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(CrossOriginEmbedderPolicyHeaderWriter.class, () -> writer);
|
||||
this.headerWriters.add(builder.getBeanDefinition());
|
||||
}
|
||||
|
||||
private void parseCrossOriginResourcePolicy(boolean elementDisabled, Element element) {
|
||||
if (elementDisabled || element == null) {
|
||||
return;
|
||||
}
|
||||
CrossOriginResourcePolicyHeaderWriter writer = new CrossOriginResourcePolicyHeaderWriter();
|
||||
Element crossOriginResourcePolicyElement = DomUtils.getChildElementByTagName(element,
|
||||
CROSS_ORIGIN_RESOURCE_POLICY_ELEMENT);
|
||||
if (crossOriginResourcePolicyElement != null) {
|
||||
addCrossOriginResourcePolicy(crossOriginResourcePolicyElement, writer);
|
||||
}
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
||||
.genericBeanDefinition(CrossOriginResourcePolicyHeaderWriter.class, () -> writer);
|
||||
this.headerWriters.add(builder.getBeanDefinition());
|
||||
}
|
||||
|
||||
private void addCrossOriginResourcePolicy(Element crossOriginResourcePolicyElement,
|
||||
CrossOriginResourcePolicyHeaderWriter writer) {
|
||||
String policy = crossOriginResourcePolicyElement.getAttribute(ATT_POLICY);
|
||||
if (StringUtils.hasText(policy)) {
|
||||
writer.setPolicy(CrossOriginResourcePolicyHeaderWriter.CrossOriginResourcePolicy.from(policy));
|
||||
}
|
||||
}
|
||||
|
||||
private void addCrossOriginEmbedderPolicy(Element crossOriginEmbedderPolicyElement,
|
||||
CrossOriginEmbedderPolicyHeaderWriter writer) {
|
||||
String policy = crossOriginEmbedderPolicyElement.getAttribute(ATT_POLICY);
|
||||
if (StringUtils.hasText(policy)) {
|
||||
writer.setPolicy(CrossOriginEmbedderPolicyHeaderWriter.CrossOriginEmbedderPolicy.from(policy));
|
||||
}
|
||||
}
|
||||
|
||||
private void addCrossOriginOpenerPolicy(Element crossOriginOpenerPolicyElement,
|
||||
CrossOriginOpenerPolicyHeaderWriter writer) {
|
||||
String policy = crossOriginOpenerPolicyElement.getAttribute(ATT_POLICY);
|
||||
if (StringUtils.hasText(policy)) {
|
||||
writer.setPolicy(CrossOriginOpenerPolicyHeaderWriter.CrossOriginOpenerPolicy.from(policy));
|
||||
}
|
||||
}
|
||||
|
||||
private void attrNotAllowed(ParserContext context, String attrName, String otherAttrName, Element element) {
|
||||
context.getReaderContext().error("Only one of '" + attrName + "' or '" + otherAttrName + "' can be set.",
|
||||
element);
|
||||
|
||||
+17
-78
@@ -59,7 +59,6 @@ import org.springframework.security.web.authentication.session.RegisterSessionAu
|
||||
import org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy;
|
||||
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.context.NullSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextHolderFilter;
|
||||
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
|
||||
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
||||
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
|
||||
@@ -68,8 +67,6 @@ import org.springframework.security.web.savedrequest.NullRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
|
||||
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
|
||||
import org.springframework.security.web.session.ConcurrentSessionFilter;
|
||||
import org.springframework.security.web.session.DisableEncodeUrlFilter;
|
||||
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
|
||||
import org.springframework.security.web.session.SessionManagementFilter;
|
||||
import org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy;
|
||||
import org.springframework.security.web.session.SimpleRedirectSessionInformationExpiredStrategy;
|
||||
@@ -107,8 +104,6 @@ class HttpConfigurationBuilder {
|
||||
|
||||
private static final String ATT_SECURITY_CONTEXT_REPOSITORY = "security-context-repository-ref";
|
||||
|
||||
private static final String ATT_SECURITY_CONTEXT_EXPLICIT_SAVE = "security-context-explicit-save";
|
||||
|
||||
private static final String ATT_INVALID_SESSION_STRATEGY_REF = "invalid-session-strategy-ref";
|
||||
|
||||
private static final String ATT_DISABLE_URL_REWRITING = "disable-url-rewriting";
|
||||
@@ -149,8 +144,6 @@ class HttpConfigurationBuilder {
|
||||
|
||||
private BeanDefinition securityContextPersistenceFilter;
|
||||
|
||||
private BeanDefinition forceEagerSessionCreationFilter;
|
||||
|
||||
private BeanReference contextRepoRef;
|
||||
|
||||
private BeanReference sessionRegistryRef;
|
||||
@@ -183,8 +176,6 @@ class HttpConfigurationBuilder {
|
||||
|
||||
private BeanDefinition csrfFilter;
|
||||
|
||||
private BeanDefinition disableUrlRewriteFilter;
|
||||
|
||||
private BeanDefinition wellKnownChangePasswordRedirectFilter;
|
||||
|
||||
private BeanMetadataElement csrfLogoutHandler;
|
||||
@@ -210,10 +201,8 @@ class HttpConfigurationBuilder {
|
||||
String createSession = element.getAttribute(ATT_CREATE_SESSION);
|
||||
this.sessionPolicy = !StringUtils.hasText(createSession) ? SessionCreationPolicy.IF_REQUIRED
|
||||
: createPolicy(createSession);
|
||||
createForceEagerSessionCreationFilter();
|
||||
createDisableEncodeUrlFilter();
|
||||
createCsrfFilter();
|
||||
createSecurityPersistence();
|
||||
createSecurityContextPersistenceFilter();
|
||||
createSessionManagementFilters();
|
||||
createWebAsyncManagerFilter();
|
||||
createRequestCacheFilter();
|
||||
@@ -289,51 +278,19 @@ class HttpConfigurationBuilder {
|
||||
return lowerCase ? path.toLowerCase() : path;
|
||||
}
|
||||
|
||||
BeanReference getSecurityContextRepositoryForAuthenticationFilters() {
|
||||
return (isExplicitSave()) ? this.contextRepoRef : null;
|
||||
}
|
||||
|
||||
private void createSecurityPersistence() {
|
||||
createSecurityContextRepository();
|
||||
if (isExplicitSave()) {
|
||||
createSecurityContextHolderFilter();
|
||||
}
|
||||
else {
|
||||
createSecurityContextPersistenceFilter();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isExplicitSave() {
|
||||
String explicitSaveAttr = this.httpElt.getAttribute(ATT_SECURITY_CONTEXT_EXPLICIT_SAVE);
|
||||
return Boolean.parseBoolean(explicitSaveAttr);
|
||||
}
|
||||
|
||||
private void createForceEagerSessionCreationFilter() {
|
||||
if (this.sessionPolicy == SessionCreationPolicy.ALWAYS) {
|
||||
this.forceEagerSessionCreationFilter = new RootBeanDefinition(ForceEagerSessionCreationFilter.class);
|
||||
}
|
||||
}
|
||||
|
||||
private void createSecurityContextPersistenceFilter() {
|
||||
BeanDefinitionBuilder scpf = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextPersistenceFilter.class);
|
||||
switch (this.sessionPolicy) {
|
||||
case ALWAYS:
|
||||
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
|
||||
break;
|
||||
case NEVER:
|
||||
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
|
||||
break;
|
||||
default:
|
||||
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
|
||||
}
|
||||
scpf.addConstructorArgValue(this.contextRepoRef);
|
||||
|
||||
this.securityContextPersistenceFilter = scpf.getBeanDefinition();
|
||||
}
|
||||
|
||||
private void createSecurityContextRepository() {
|
||||
String repoRef = this.httpElt.getAttribute(ATT_SECURITY_CONTEXT_REPOSITORY);
|
||||
if (!StringUtils.hasText(repoRef)) {
|
||||
String disableUrlRewriting = this.httpElt.getAttribute(ATT_DISABLE_URL_REWRITING);
|
||||
if (!StringUtils.hasText(disableUrlRewriting)) {
|
||||
disableUrlRewriting = "true";
|
||||
}
|
||||
if (StringUtils.hasText(repoRef)) {
|
||||
if (this.sessionPolicy == SessionCreationPolicy.ALWAYS) {
|
||||
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
else {
|
||||
BeanDefinitionBuilder contextRepo;
|
||||
if (this.sessionPolicy == SessionCreationPolicy.STATELESS) {
|
||||
contextRepo = BeanDefinitionBuilder.rootBeanDefinition(NullSecurityContextRepository.class);
|
||||
@@ -343,14 +300,17 @@ class HttpConfigurationBuilder {
|
||||
switch (this.sessionPolicy) {
|
||||
case ALWAYS:
|
||||
contextRepo.addPropertyValue("allowSessionCreation", Boolean.TRUE);
|
||||
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.TRUE);
|
||||
break;
|
||||
case NEVER:
|
||||
contextRepo.addPropertyValue("allowSessionCreation", Boolean.FALSE);
|
||||
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
|
||||
break;
|
||||
default:
|
||||
contextRepo.addPropertyValue("allowSessionCreation", Boolean.TRUE);
|
||||
scpf.addPropertyValue("forceEagerSessionCreation", Boolean.FALSE);
|
||||
}
|
||||
if (isDisableUrlRewriting()) {
|
||||
if ("true".equals(disableUrlRewriting)) {
|
||||
contextRepo.addPropertyValue("disableUrlRewriting", Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
@@ -360,17 +320,9 @@ class HttpConfigurationBuilder {
|
||||
}
|
||||
|
||||
this.contextRepoRef = new RuntimeBeanReference(repoRef);
|
||||
}
|
||||
scpf.addConstructorArgValue(this.contextRepoRef);
|
||||
|
||||
private boolean isDisableUrlRewriting() {
|
||||
String disableUrlRewriting = this.httpElt.getAttribute(ATT_DISABLE_URL_REWRITING);
|
||||
return !"false".equals(disableUrlRewriting);
|
||||
}
|
||||
|
||||
private void createSecurityContextHolderFilter() {
|
||||
BeanDefinitionBuilder filter = BeanDefinitionBuilder.rootBeanDefinition(SecurityContextHolderFilter.class);
|
||||
filter.addConstructorArgValue(this.contextRepoRef);
|
||||
this.securityContextPersistenceFilter = filter.getBeanDefinition();
|
||||
this.securityContextPersistenceFilter = scpf.getBeanDefinition();
|
||||
}
|
||||
|
||||
private void createSessionManagementFilters() {
|
||||
@@ -733,12 +685,6 @@ class HttpConfigurationBuilder {
|
||||
|
||||
}
|
||||
|
||||
private void createDisableEncodeUrlFilter() {
|
||||
if (isDisableUrlRewriting()) {
|
||||
this.disableUrlRewriteFilter = new RootBeanDefinition(DisableEncodeUrlFilter.class);
|
||||
}
|
||||
}
|
||||
|
||||
private void createCsrfFilter() {
|
||||
Element elmt = DomUtils.getChildElementByTagName(this.httpElt, Elements.CSRF);
|
||||
this.csrfParser = new CsrfBeanDefinitionParser();
|
||||
@@ -778,13 +724,6 @@ class HttpConfigurationBuilder {
|
||||
|
||||
List<OrderDecorator> getFilters() {
|
||||
List<OrderDecorator> filters = new ArrayList<>();
|
||||
if (this.forceEagerSessionCreationFilter != null) {
|
||||
filters.add(new OrderDecorator(this.forceEagerSessionCreationFilter,
|
||||
SecurityFilters.FORCE_EAGER_SESSION_FILTER));
|
||||
}
|
||||
if (this.disableUrlRewriteFilter != null) {
|
||||
filters.add(new OrderDecorator(this.disableUrlRewriteFilter, SecurityFilters.DISABLE_ENCODE_URL_FILTER));
|
||||
}
|
||||
if (this.cpf != null) {
|
||||
filters.add(new OrderDecorator(this.cpf, SecurityFilters.CHANNEL_FILTER));
|
||||
}
|
||||
|
||||
+1
-3
@@ -144,11 +144,9 @@ public class HttpSecurityBeanDefinitionParser implements BeanDefinitionParser {
|
||||
boolean forceAutoConfig = isDefaultHttpConfig(element);
|
||||
HttpConfigurationBuilder httpBldr = new HttpConfigurationBuilder(element, forceAutoConfig, pc, portMapper,
|
||||
portResolver, authenticationManager);
|
||||
httpBldr.getSecurityContextRepositoryForAuthenticationFilters();
|
||||
AuthenticationConfigBuilder authBldr = new AuthenticationConfigBuilder(element, forceAutoConfig, pc,
|
||||
httpBldr.getSessionCreationPolicy(), httpBldr.getRequestCache(), authenticationManager,
|
||||
httpBldr.getSecurityContextRepositoryForAuthenticationFilters(), httpBldr.getSessionStrategy(),
|
||||
portMapper, portResolver, httpBldr.getCsrfLogoutHandler());
|
||||
httpBldr.getSessionStrategy(), portMapper, portResolver, httpBldr.getCsrfLogoutHandler());
|
||||
httpBldr.setLogoutHandlers(authBldr.getLogoutHandlers());
|
||||
httpBldr.setEntryPoint(authBldr.getEntryPointBean());
|
||||
httpBldr.setAccessDeniedHandler(authBldr.getAccessDeniedHandlerBean());
|
||||
|
||||
-7
@@ -59,8 +59,6 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private boolean csrfEnabled;
|
||||
|
||||
private BeanMetadataElement logoutSuccessHandler;
|
||||
|
||||
LogoutBeanDefinitionParser(String loginPageUrl, String rememberMeServices, BeanMetadataElement csrfLogoutHandler) {
|
||||
this.defaultLogoutUrl = loginPageUrl + "?logout";
|
||||
this.rememberMeServices = rememberMeServices;
|
||||
@@ -100,7 +98,6 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser {
|
||||
pc.extractSource(element));
|
||||
}
|
||||
builder.addConstructorArgReference(successHandlerRef);
|
||||
this.logoutSuccessHandler = new RuntimeBeanReference(successHandlerRef);
|
||||
}
|
||||
else {
|
||||
// Use the logout URL if no handler set
|
||||
@@ -140,8 +137,4 @@ class LogoutBeanDefinitionParser implements BeanDefinitionParser {
|
||||
return this.logoutHandlers;
|
||||
}
|
||||
|
||||
BeanMetadataElement getLogoutSuccessHandler() {
|
||||
return this.logoutSuccessHandler;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-12
@@ -50,8 +50,6 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private final BeanReference authenticationManager;
|
||||
|
||||
private final BeanReference authenticationFilterSecurityContextRepositoryRef;
|
||||
|
||||
private BeanDefinition defaultAuthorizedClientRepository;
|
||||
|
||||
private BeanDefinition authorizationRequestRedirectFilter;
|
||||
@@ -60,11 +58,9 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private BeanDefinition authorizationCodeAuthenticationProvider;
|
||||
|
||||
OAuth2ClientBeanDefinitionParser(BeanReference requestCache, BeanReference authenticationManager,
|
||||
BeanReference authenticationFilterSecurityContextRepositoryRef) {
|
||||
OAuth2ClientBeanDefinitionParser(BeanReference requestCache, BeanReference authenticationManager) {
|
||||
this.requestCache = requestCache;
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.authenticationFilterSecurityContextRepositoryRef = authenticationFilterSecurityContextRepositoryRef;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,16 +92,11 @@ final class OAuth2ClientBeanDefinitionParser implements BeanDefinitionParser {
|
||||
this.authorizationRequestRedirectFilter = authorizationRequestRedirectFilterBuilder
|
||||
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository)
|
||||
.addPropertyValue("requestCache", this.requestCache).getBeanDefinition();
|
||||
BeanDefinitionBuilder authorizationCodeGrantFilterBldr = BeanDefinitionBuilder
|
||||
this.authorizationCodeGrantFilter = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(OAuth2AuthorizationCodeGrantFilter.class)
|
||||
.addConstructorArgValue(clientRegistrationRepository).addConstructorArgValue(authorizedClientRepository)
|
||||
.addConstructorArgValue(this.authenticationManager)
|
||||
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository);
|
||||
if (this.authenticationFilterSecurityContextRepositoryRef != null) {
|
||||
authorizationCodeGrantFilterBldr.addPropertyValue("securityContextRepository",
|
||||
this.authenticationFilterSecurityContextRepositoryRef);
|
||||
}
|
||||
this.authorizationCodeGrantFilter = authorizationCodeGrantFilterBldr.getBeanDefinition();
|
||||
.addPropertyValue("authorizationRequestRepository", authorizationRequestRepository).getBeanDefinition();
|
||||
|
||||
BeanMetadataElement accessTokenResponseClient = getAccessTokenResponseClient(authorizationCodeGrantElt);
|
||||
this.authorizationCodeAuthenticationProvider = BeanDefinitionBuilder
|
||||
|
||||
-323
@@ -1,323 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.http;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.security.config.Elements;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
|
||||
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
|
||||
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
|
||||
import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter;
|
||||
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* SAML 2.0 Login {@link BeanDefinitionParser}
|
||||
*
|
||||
* @author Marcus da Coregio
|
||||
* @since 5.7
|
||||
*/
|
||||
final class Saml2LoginBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String DEFAULT_LOGIN_URI = DefaultLoginPageGeneratingFilter.DEFAULT_LOGIN_PAGE_URL;
|
||||
|
||||
private static final String DEFAULT_AUTHENTICATION_REQUEST_PROCESSING_URL = "/saml2/authenticate/{registrationId}";
|
||||
|
||||
private static final String ATT_LOGIN_PROCESSING_URL = "login-processing-url";
|
||||
|
||||
private static final String ATT_LOGIN_PAGE = "login-page";
|
||||
|
||||
private static final String ELT_RELYING_PARTY_REGISTRATION = "relying-party-registration";
|
||||
|
||||
private static final String ELT_REGISTRATION_ID = "registration-id";
|
||||
|
||||
private static final String ATT_AUTHENTICATION_FAILURE_HANDLER_REF = "authentication-failure-handler-ref";
|
||||
|
||||
private static final String ATT_AUTHENTICATION_SUCCESS_HANDLER_REF = "authentication-success-handler-ref";
|
||||
|
||||
private static final String ATT_AUTHENTICATION_MANAGER_REF = "authentication-manager-ref";
|
||||
|
||||
private final List<BeanDefinition> csrfIgnoreRequestMatchers;
|
||||
|
||||
private final BeanReference portMapper;
|
||||
|
||||
private final BeanReference portResolver;
|
||||
|
||||
private final BeanReference requestCache;
|
||||
|
||||
private final boolean allowSessionCreation;
|
||||
|
||||
private final BeanReference authenticationManager;
|
||||
|
||||
private final BeanReference authenticationFilterSecurityContextRepositoryRef;
|
||||
|
||||
private final List<BeanReference> authenticationProviders;
|
||||
|
||||
private final Map<BeanDefinition, BeanMetadataElement> entryPoints;
|
||||
|
||||
private String loginProcessingUrl = Saml2WebSsoAuthenticationFilter.DEFAULT_FILTER_PROCESSES_URI;
|
||||
|
||||
private BeanDefinition saml2WebSsoAuthenticationRequestFilter;
|
||||
|
||||
private BeanDefinition saml2AuthenticationUrlToProviderName;
|
||||
|
||||
Saml2LoginBeanDefinitionParser(List<BeanDefinition> csrfIgnoreRequestMatchers, BeanReference portMapper,
|
||||
BeanReference portResolver, BeanReference requestCache, boolean allowSessionCreation,
|
||||
BeanReference authenticationManager, BeanReference authenticationFilterSecurityContextRepositoryRef,
|
||||
List<BeanReference> authenticationProviders, Map<BeanDefinition, BeanMetadataElement> entryPoints) {
|
||||
this.csrfIgnoreRequestMatchers = csrfIgnoreRequestMatchers;
|
||||
this.portMapper = portMapper;
|
||||
this.portResolver = portResolver;
|
||||
this.requestCache = requestCache;
|
||||
this.allowSessionCreation = allowSessionCreation;
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.authenticationFilterSecurityContextRepositoryRef = authenticationFilterSecurityContextRepositoryRef;
|
||||
this.authenticationProviders = authenticationProviders;
|
||||
this.entryPoints = entryPoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanDefinition parse(Element element, ParserContext pc) {
|
||||
String loginProcessingUrl = element.getAttribute(ATT_LOGIN_PROCESSING_URL);
|
||||
if (StringUtils.hasText(loginProcessingUrl)) {
|
||||
this.loginProcessingUrl = loginProcessingUrl;
|
||||
}
|
||||
BeanDefinition saml2LoginBeanConfig = BeanDefinitionBuilder.rootBeanDefinition(Saml2LoginBeanConfig.class)
|
||||
.getBeanDefinition();
|
||||
String saml2LoginBeanConfigId = pc.getReaderContext().generateBeanName(saml2LoginBeanConfig);
|
||||
pc.registerBeanComponent(new BeanComponentDefinition(saml2LoginBeanConfig, saml2LoginBeanConfigId));
|
||||
registerDefaultCsrfOverride();
|
||||
BeanMetadataElement relyingPartyRegistrationRepository = Saml2LoginBeanDefinitionParserUtils
|
||||
.getRelyingPartyRegistrationRepository(element);
|
||||
BeanMetadataElement authenticationRequestRepository = Saml2LoginBeanDefinitionParserUtils
|
||||
.getAuthenticationRequestRepository(element);
|
||||
BeanMetadataElement authenticationRequestResolver = Saml2LoginBeanDefinitionParserUtils
|
||||
.getAuthenticationRequestResolver(element);
|
||||
if (authenticationRequestResolver == null) {
|
||||
authenticationRequestResolver = Saml2LoginBeanDefinitionParserUtils
|
||||
.createDefaultAuthenticationRequestResolver(relyingPartyRegistrationRepository);
|
||||
}
|
||||
BeanMetadataElement authenticationConverter = Saml2LoginBeanDefinitionParserUtils
|
||||
.getAuthenticationConverter(element);
|
||||
if (authenticationConverter == null) {
|
||||
if (!this.loginProcessingUrl.contains("{registrationId}")) {
|
||||
pc.getReaderContext().error("loginProcessingUrl must contain {registrationId} path variable", element);
|
||||
}
|
||||
authenticationConverter = Saml2LoginBeanDefinitionParserUtils
|
||||
.createDefaultAuthenticationConverter(relyingPartyRegistrationRepository);
|
||||
}
|
||||
// Configure the Saml2WebSsoAuthenticationFilter
|
||||
BeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Saml2WebSsoAuthenticationFilter.class)
|
||||
.addConstructorArgValue(authenticationConverter).addConstructorArgValue(this.loginProcessingUrl)
|
||||
.addPropertyValue("authenticationRequestRepository", authenticationRequestRepository);
|
||||
resolveLoginPage(element, pc);
|
||||
resolveAuthenticationSuccessHandler(element, saml2WebSsoAuthenticationFilterBuilder);
|
||||
resolveAuthenticationFailureHandler(element, saml2WebSsoAuthenticationFilterBuilder);
|
||||
resolveAuthenticationManager(element, saml2WebSsoAuthenticationFilterBuilder);
|
||||
resolveSecurityContextRepository(element, saml2WebSsoAuthenticationFilterBuilder);
|
||||
// Configure the Saml2WebSsoAuthenticationRequestFilter
|
||||
this.saml2WebSsoAuthenticationRequestFilter = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Saml2WebSsoAuthenticationRequestFilter.class)
|
||||
.addConstructorArgValue(authenticationRequestResolver)
|
||||
.addPropertyValue("authenticationRequestRepository", authenticationRequestRepository)
|
||||
.getBeanDefinition();
|
||||
BeanDefinition saml2AuthenticationProvider = Saml2LoginBeanDefinitionParserUtils.createAuthenticationProvider();
|
||||
this.authenticationProviders.add(
|
||||
new RuntimeBeanReference(pc.getReaderContext().registerWithGeneratedName(saml2AuthenticationProvider)));
|
||||
this.saml2AuthenticationUrlToProviderName = BeanDefinitionBuilder.rootBeanDefinition(Map.class)
|
||||
.setFactoryMethodOnBean("getAuthenticationUrlToProviderName", saml2LoginBeanConfigId)
|
||||
.getBeanDefinition();
|
||||
return saml2WebSsoAuthenticationFilterBuilder.getBeanDefinition();
|
||||
}
|
||||
|
||||
private void resolveAuthenticationManager(Element element,
|
||||
BeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder) {
|
||||
String authenticationManagerRef = element.getAttribute(ATT_AUTHENTICATION_MANAGER_REF);
|
||||
if (StringUtils.hasText(authenticationManagerRef)) {
|
||||
saml2WebSsoAuthenticationFilterBuilder.addPropertyReference("authenticationManager",
|
||||
authenticationManagerRef);
|
||||
}
|
||||
else {
|
||||
saml2WebSsoAuthenticationFilterBuilder.addPropertyValue("authenticationManager",
|
||||
this.authenticationManager);
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveSecurityContextRepository(Element element,
|
||||
BeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder) {
|
||||
if (this.authenticationFilterSecurityContextRepositoryRef != null) {
|
||||
saml2WebSsoAuthenticationFilterBuilder.addPropertyValue("securityContextRepository",
|
||||
this.authenticationFilterSecurityContextRepositoryRef);
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveLoginPage(Element element, ParserContext parserContext) {
|
||||
String loginPage = element.getAttribute(ATT_LOGIN_PAGE);
|
||||
Object source = parserContext.extractSource(element);
|
||||
BeanDefinition saml2LoginAuthenticationEntryPoint = null;
|
||||
if (StringUtils.hasText(loginPage)) {
|
||||
WebConfigUtils.validateHttpRedirect(loginPage, parserContext, source);
|
||||
saml2LoginAuthenticationEntryPoint = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(LoginUrlAuthenticationEntryPoint.class).addConstructorArgValue(loginPage)
|
||||
.addPropertyValue("portMapper", this.portMapper).addPropertyValue("portResolver", this.portResolver)
|
||||
.getBeanDefinition();
|
||||
}
|
||||
else {
|
||||
Map<String, String> identityProviderUrlMap = getIdentityProviderUrlMap(element);
|
||||
if (identityProviderUrlMap.size() == 1) {
|
||||
String loginUrl = identityProviderUrlMap.entrySet().iterator().next().getKey();
|
||||
saml2LoginAuthenticationEntryPoint = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(LoginUrlAuthenticationEntryPoint.class).addConstructorArgValue(loginUrl)
|
||||
.addPropertyValue("portMapper", this.portMapper)
|
||||
.addPropertyValue("portResolver", this.portResolver).getBeanDefinition();
|
||||
}
|
||||
}
|
||||
if (saml2LoginAuthenticationEntryPoint != null) {
|
||||
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(AntPathRequestMatcher.class);
|
||||
requestMatcherBuilder.addConstructorArgValue(this.loginProcessingUrl);
|
||||
BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
|
||||
this.entryPoints.put(requestMatcher, saml2LoginAuthenticationEntryPoint);
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveAuthenticationFailureHandler(Element element,
|
||||
BeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder) {
|
||||
String authenticationFailureHandlerRef = element.getAttribute(ATT_AUTHENTICATION_FAILURE_HANDLER_REF);
|
||||
if (StringUtils.hasText(authenticationFailureHandlerRef)) {
|
||||
saml2WebSsoAuthenticationFilterBuilder.addPropertyReference("authenticationFailureHandler",
|
||||
authenticationFailureHandlerRef);
|
||||
}
|
||||
else {
|
||||
BeanDefinitionBuilder failureHandlerBuilder = BeanDefinitionBuilder.rootBeanDefinition(
|
||||
"org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler");
|
||||
failureHandlerBuilder.addConstructorArgValue(
|
||||
DEFAULT_LOGIN_URI + "?" + DefaultLoginPageGeneratingFilter.ERROR_PARAMETER_NAME);
|
||||
failureHandlerBuilder.addPropertyValue("allowSessionCreation", this.allowSessionCreation);
|
||||
saml2WebSsoAuthenticationFilterBuilder.addPropertyValue("authenticationFailureHandler",
|
||||
failureHandlerBuilder.getBeanDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveAuthenticationSuccessHandler(Element element,
|
||||
BeanDefinitionBuilder saml2WebSsoAuthenticationFilterBuilder) {
|
||||
String authenticationSuccessHandlerRef = element.getAttribute(ATT_AUTHENTICATION_SUCCESS_HANDLER_REF);
|
||||
if (StringUtils.hasText(authenticationSuccessHandlerRef)) {
|
||||
saml2WebSsoAuthenticationFilterBuilder.addPropertyReference("authenticationSuccessHandler",
|
||||
authenticationSuccessHandlerRef);
|
||||
}
|
||||
else {
|
||||
BeanDefinitionBuilder successHandlerBuilder = BeanDefinitionBuilder.rootBeanDefinition(
|
||||
"org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler")
|
||||
.addPropertyValue("requestCache", this.requestCache);
|
||||
saml2WebSsoAuthenticationFilterBuilder.addPropertyValue("authenticationSuccessHandler",
|
||||
successHandlerBuilder.getBeanDefinition());
|
||||
}
|
||||
}
|
||||
|
||||
private void registerDefaultCsrfOverride() {
|
||||
BeanDefinitionBuilder requestMatcherBuilder = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(AntPathRequestMatcher.class);
|
||||
requestMatcherBuilder.addConstructorArgValue(this.loginProcessingUrl);
|
||||
BeanDefinition requestMatcher = requestMatcherBuilder.getBeanDefinition();
|
||||
this.csrfIgnoreRequestMatchers.add(requestMatcher);
|
||||
}
|
||||
|
||||
private Map<String, String> getIdentityProviderUrlMap(Element element) {
|
||||
Map<String, String> idps = new LinkedHashMap<>();
|
||||
Element relyingPartyRegistrationsElt = DomUtils.getChildElementByTagName(
|
||||
element.getOwnerDocument().getDocumentElement(), Elements.RELYING_PARTY_REGISTRATIONS);
|
||||
String authenticationRequestProcessingUrl = DEFAULT_AUTHENTICATION_REQUEST_PROCESSING_URL;
|
||||
if (relyingPartyRegistrationsElt != null) {
|
||||
List<Element> relyingPartyRegList = DomUtils.getChildElementsByTagName(relyingPartyRegistrationsElt,
|
||||
ELT_RELYING_PARTY_REGISTRATION);
|
||||
for (Element relyingPartyReg : relyingPartyRegList) {
|
||||
String registrationId = relyingPartyReg.getAttribute(ELT_REGISTRATION_ID);
|
||||
idps.put(authenticationRequestProcessingUrl.replace("{registrationId}", registrationId),
|
||||
registrationId);
|
||||
}
|
||||
}
|
||||
return idps;
|
||||
}
|
||||
|
||||
BeanDefinition getSaml2WebSsoAuthenticationRequestFilter() {
|
||||
return this.saml2WebSsoAuthenticationRequestFilter;
|
||||
}
|
||||
|
||||
BeanDefinition getSaml2AuthenticationUrlToProviderName() {
|
||||
return this.saml2AuthenticationUrlToProviderName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper bean class to provide configuration from applicationContext
|
||||
*/
|
||||
public static class Saml2LoginBeanConfig implements ApplicationContextAware {
|
||||
|
||||
private ApplicationContext context;
|
||||
|
||||
@SuppressWarnings({ "unchecked", "unused" })
|
||||
Map<String, String> getAuthenticationUrlToProviderName() {
|
||||
Iterable<RelyingPartyRegistration> relyingPartyRegistrations = null;
|
||||
RelyingPartyRegistrationRepository relyingPartyRegistrationRepository = this.context
|
||||
.getBean(RelyingPartyRegistrationRepository.class);
|
||||
ResolvableType type = ResolvableType.forInstance(relyingPartyRegistrationRepository).as(Iterable.class);
|
||||
if (type != ResolvableType.NONE
|
||||
&& RelyingPartyRegistration.class.isAssignableFrom(type.resolveGenerics()[0])) {
|
||||
relyingPartyRegistrations = (Iterable<RelyingPartyRegistration>) relyingPartyRegistrationRepository;
|
||||
}
|
||||
if (relyingPartyRegistrations == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
String authenticationRequestProcessingUrl = DEFAULT_AUTHENTICATION_REQUEST_PROCESSING_URL;
|
||||
Map<String, String> saml2AuthenticationUrlToProviderName = new HashMap<>();
|
||||
relyingPartyRegistrations.forEach((registration) -> saml2AuthenticationUrlToProviderName.put(
|
||||
authenticationRequestProcessingUrl.replace("{registrationId}", registration.getRegistrationId()),
|
||||
registration.getRegistrationId()));
|
||||
return saml2AuthenticationUrlToProviderName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-107
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.http;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
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.HttpSessionSaml2AuthenticationRequestRepository;
|
||||
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Marcus da Coregio
|
||||
* @since 5.7
|
||||
*/
|
||||
final class Saml2LoginBeanDefinitionParserUtils {
|
||||
|
||||
private static final String ATT_RELYING_PARTY_REGISTRATION_REPOSITORY_REF = "relying-party-registration-repository-ref";
|
||||
|
||||
private static final String ATT_AUTHENTICATION_REQUEST_REPOSITORY_REF = "authentication-request-repository-ref";
|
||||
|
||||
private static final String ATT_AUTHENTICATION_REQUEST_RESOLVER_REF = "authentication-request-resolver-ref";
|
||||
|
||||
private static final String ATT_AUTHENTICATION_CONVERTER = "authentication-converter-ref";
|
||||
|
||||
private Saml2LoginBeanDefinitionParserUtils() {
|
||||
}
|
||||
|
||||
static BeanMetadataElement getRelyingPartyRegistrationRepository(Element element) {
|
||||
String relyingPartyRegistrationRepositoryRef = element
|
||||
.getAttribute(ATT_RELYING_PARTY_REGISTRATION_REPOSITORY_REF);
|
||||
if (StringUtils.hasText(relyingPartyRegistrationRepositoryRef)) {
|
||||
return new RuntimeBeanReference(relyingPartyRegistrationRepositoryRef);
|
||||
}
|
||||
return new RuntimeBeanReference(RelyingPartyRegistrationRepository.class);
|
||||
}
|
||||
|
||||
static BeanMetadataElement getAuthenticationRequestRepository(Element element) {
|
||||
String authenticationRequestRepositoryRef = element.getAttribute(ATT_AUTHENTICATION_REQUEST_REPOSITORY_REF);
|
||||
if (StringUtils.hasText(authenticationRequestRepositoryRef)) {
|
||||
return new RuntimeBeanReference(authenticationRequestRepositoryRef);
|
||||
}
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(HttpSessionSaml2AuthenticationRequestRepository.class)
|
||||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
static BeanMetadataElement getAuthenticationRequestResolver(Element element) {
|
||||
String authenticationRequestContextResolver = element.getAttribute(ATT_AUTHENTICATION_REQUEST_RESOLVER_REF);
|
||||
if (StringUtils.hasText(authenticationRequestContextResolver)) {
|
||||
return new RuntimeBeanReference(authenticationRequestContextResolver);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static BeanMetadataElement createDefaultAuthenticationRequestResolver(
|
||||
BeanMetadataElement relyingPartyRegistrationRepository) {
|
||||
BeanMetadataElement defaultRelyingPartyRegistrationResolver = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(DefaultRelyingPartyRegistrationResolver.class)
|
||||
.addConstructorArgValue(relyingPartyRegistrationRepository).getBeanDefinition();
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(
|
||||
"org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver")
|
||||
.addConstructorArgValue(defaultRelyingPartyRegistrationResolver).getBeanDefinition();
|
||||
}
|
||||
|
||||
static BeanDefinition createAuthenticationProvider() {
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(
|
||||
"org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider")
|
||||
.getBeanDefinition();
|
||||
}
|
||||
|
||||
static BeanMetadataElement getAuthenticationConverter(Element element) {
|
||||
String authenticationConverter = element.getAttribute(ATT_AUTHENTICATION_CONVERTER);
|
||||
if (StringUtils.hasText(authenticationConverter)) {
|
||||
return new RuntimeBeanReference(authenticationConverter);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static BeanDefinition createDefaultAuthenticationConverter(BeanMetadataElement relyingPartyRegistrationRepository) {
|
||||
AbstractBeanDefinition resolver = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(DefaultRelyingPartyRegistrationResolver.class)
|
||||
.addConstructorArgValue(relyingPartyRegistrationRepository).getBeanDefinition();
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(Saml2AuthenticationTokenConverter.class)
|
||||
.addConstructorArgValue(resolver).getBeanDefinition();
|
||||
}
|
||||
|
||||
}
|
||||
-236
@@ -1,236 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.http;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
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.web.DefaultRelyingPartyRegistrationResolver;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter;
|
||||
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.LogoutSuccessEventPublishingLogoutHandler;
|
||||
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
|
||||
import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
|
||||
import org.springframework.security.web.util.matcher.AndRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* SAML 2.0 Single Logout {@link BeanDefinitionParser}
|
||||
*
|
||||
* @author Marcus da Coregio
|
||||
* @since 5.7
|
||||
*/
|
||||
final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String ATT_LOGOUT_REQUEST_URL = "logout-request-url";
|
||||
|
||||
private static final String ATT_LOGOUT_RESPONSE_URL = "logout-response-url";
|
||||
|
||||
private static final String ATT_LOGOUT_URL = "logout-url";
|
||||
|
||||
private List<BeanMetadataElement> logoutHandlers;
|
||||
|
||||
private String logoutUrl = "/logout";
|
||||
|
||||
private String logoutRequestUrl = "/logout/saml2/slo";
|
||||
|
||||
private String logoutResponseUrl = "/logout/saml2/slo";
|
||||
|
||||
private BeanMetadataElement logoutSuccessHandler;
|
||||
|
||||
private BeanDefinition logoutRequestFilter;
|
||||
|
||||
private BeanDefinition logoutResponseFilter;
|
||||
|
||||
private BeanDefinition logoutFilter;
|
||||
|
||||
Saml2LogoutBeanDefinitionParser(ManagedList<BeanMetadataElement> logoutHandlers,
|
||||
BeanMetadataElement logoutSuccessHandler) {
|
||||
this.logoutHandlers = logoutHandlers;
|
||||
this.logoutSuccessHandler = logoutSuccessHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanDefinition parse(Element element, ParserContext pc) {
|
||||
String logoutUrl = element.getAttribute(ATT_LOGOUT_URL);
|
||||
if (StringUtils.hasText(logoutUrl)) {
|
||||
this.logoutUrl = logoutUrl;
|
||||
}
|
||||
String logoutRequestUrl = element.getAttribute(ATT_LOGOUT_REQUEST_URL);
|
||||
if (StringUtils.hasText(logoutRequestUrl)) {
|
||||
this.logoutRequestUrl = logoutRequestUrl;
|
||||
}
|
||||
String logoutResponseUrl = element.getAttribute(ATT_LOGOUT_RESPONSE_URL);
|
||||
if (StringUtils.hasText(logoutResponseUrl)) {
|
||||
this.logoutResponseUrl = logoutResponseUrl;
|
||||
}
|
||||
WebConfigUtils.validateHttpRedirect(this.logoutUrl, pc, element);
|
||||
WebConfigUtils.validateHttpRedirect(this.logoutRequestUrl, pc, element);
|
||||
WebConfigUtils.validateHttpRedirect(this.logoutResponseUrl, pc, element);
|
||||
if (CollectionUtils.isEmpty(this.logoutHandlers)) {
|
||||
this.logoutHandlers = createDefaultLogoutHandlers();
|
||||
}
|
||||
if (this.logoutSuccessHandler == null) {
|
||||
this.logoutSuccessHandler = createDefaultLogoutSuccessHandler();
|
||||
}
|
||||
BeanMetadataElement relyingPartyRegistrationRepository = Saml2LogoutBeanDefinitionParserUtils
|
||||
.getRelyingPartyRegistrationRepository(element);
|
||||
BeanMetadataElement registrations = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(DefaultRelyingPartyRegistrationResolver.class)
|
||||
.addConstructorArgValue(relyingPartyRegistrationRepository).getBeanDefinition();
|
||||
BeanMetadataElement logoutResponseResolver = Saml2LogoutBeanDefinitionParserUtils
|
||||
.getLogoutResponseResolver(element, registrations);
|
||||
BeanMetadataElement logoutRequestValidator = Saml2LogoutBeanDefinitionParserUtils
|
||||
.getLogoutRequestValidator(element);
|
||||
BeanMetadataElement logoutRequestMatcher = createSaml2LogoutRequestMatcher();
|
||||
this.logoutRequestFilter = BeanDefinitionBuilder.rootBeanDefinition(Saml2LogoutRequestFilter.class)
|
||||
.addConstructorArgValue(registrations).addConstructorArgValue(logoutRequestValidator)
|
||||
.addConstructorArgValue(logoutResponseResolver).addConstructorArgValue(this.logoutHandlers)
|
||||
.addPropertyValue("logoutRequestMatcher", logoutRequestMatcher).getBeanDefinition();
|
||||
BeanMetadataElement logoutResponseValidator = Saml2LogoutBeanDefinitionParserUtils
|
||||
.getLogoutResponseValidator(element);
|
||||
BeanMetadataElement logoutRequestRepository = Saml2LogoutBeanDefinitionParserUtils
|
||||
.getLogoutRequestRepository(element);
|
||||
BeanMetadataElement logoutResponseMatcher = createSaml2LogoutResponseMatcher();
|
||||
this.logoutResponseFilter = BeanDefinitionBuilder.rootBeanDefinition(Saml2LogoutResponseFilter.class)
|
||||
.addConstructorArgValue(registrations).addConstructorArgValue(logoutResponseValidator)
|
||||
.addConstructorArgValue(this.logoutSuccessHandler)
|
||||
.addPropertyValue("logoutRequestMatcher", logoutResponseMatcher)
|
||||
.addPropertyValue("logoutRequestRepository", logoutRequestRepository).getBeanDefinition();
|
||||
BeanMetadataElement logoutRequestResolver = Saml2LogoutBeanDefinitionParserUtils
|
||||
.getLogoutRequestResolver(element, registrations);
|
||||
BeanMetadataElement saml2LogoutRequestSuccessHandler = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(Saml2RelyingPartyInitiatedLogoutSuccessHandler.class)
|
||||
.addConstructorArgValue(logoutRequestResolver).getBeanDefinition();
|
||||
this.logoutFilter = BeanDefinitionBuilder.rootBeanDefinition(LogoutFilter.class)
|
||||
.addConstructorArgValue(saml2LogoutRequestSuccessHandler).addConstructorArgValue(this.logoutHandlers)
|
||||
.addPropertyValue("logoutRequestMatcher", createLogoutRequestMatcher()).getBeanDefinition();
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<BeanMetadataElement> createDefaultLogoutHandlers() {
|
||||
List<BeanMetadataElement> handlers = new ManagedList<>();
|
||||
handlers.add(BeanDefinitionBuilder.rootBeanDefinition(SecurityContextLogoutHandler.class).getBeanDefinition());
|
||||
handlers.add(BeanDefinitionBuilder.rootBeanDefinition(LogoutSuccessEventPublishingLogoutHandler.class)
|
||||
.getBeanDefinition());
|
||||
return handlers;
|
||||
}
|
||||
|
||||
private static BeanMetadataElement createDefaultLogoutSuccessHandler() {
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(SimpleUrlLogoutSuccessHandler.class)
|
||||
.addPropertyValue("defaultTargetUrl", "/login?logout").getBeanDefinition();
|
||||
}
|
||||
|
||||
private BeanMetadataElement createLogoutRequestMatcher() {
|
||||
BeanMetadataElement logoutMatcher = BeanDefinitionBuilder.rootBeanDefinition(AntPathRequestMatcher.class)
|
||||
.addConstructorArgValue(this.logoutUrl).addConstructorArgValue("POST").getBeanDefinition();
|
||||
BeanMetadataElement saml2Matcher = BeanDefinitionBuilder.rootBeanDefinition(Saml2RequestMatcher.class)
|
||||
.getBeanDefinition();
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(AndRequestMatcher.class)
|
||||
.addConstructorArgValue(toManagedList(logoutMatcher, saml2Matcher)).getBeanDefinition();
|
||||
}
|
||||
|
||||
private BeanMetadataElement createSaml2LogoutRequestMatcher() {
|
||||
BeanMetadataElement logoutRequestMatcher = BeanDefinitionBuilder.rootBeanDefinition(AntPathRequestMatcher.class)
|
||||
.addConstructorArgValue(this.logoutRequestUrl).getBeanDefinition();
|
||||
BeanMetadataElement saml2RequestMatcher = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(ParameterRequestMatcher.class).addConstructorArgValue("SAMLRequest")
|
||||
.getBeanDefinition();
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(AndRequestMatcher.class)
|
||||
.addConstructorArgValue(toManagedList(logoutRequestMatcher, saml2RequestMatcher)).getBeanDefinition();
|
||||
}
|
||||
|
||||
private BeanMetadataElement createSaml2LogoutResponseMatcher() {
|
||||
BeanMetadataElement logoutResponseMatcher = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(AntPathRequestMatcher.class).addConstructorArgValue(this.logoutResponseUrl)
|
||||
.getBeanDefinition();
|
||||
BeanMetadataElement saml2ResponseMatcher = BeanDefinitionBuilder
|
||||
.rootBeanDefinition(ParameterRequestMatcher.class).addConstructorArgValue("SAMLResponse")
|
||||
.getBeanDefinition();
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(AndRequestMatcher.class)
|
||||
.addConstructorArgValue(toManagedList(logoutResponseMatcher, saml2ResponseMatcher)).getBeanDefinition();
|
||||
}
|
||||
|
||||
private static List<BeanMetadataElement> toManagedList(BeanMetadataElement... elements) {
|
||||
List<BeanMetadataElement> managedList = new ManagedList<>();
|
||||
managedList.addAll(Arrays.asList(elements));
|
||||
return managedList;
|
||||
}
|
||||
|
||||
BeanDefinition getLogoutRequestFilter() {
|
||||
return this.logoutRequestFilter;
|
||||
}
|
||||
|
||||
BeanDefinition getLogoutResponseFilter() {
|
||||
return this.logoutResponseFilter;
|
||||
}
|
||||
|
||||
BeanDefinition getLogoutFilter() {
|
||||
return this.logoutFilter;
|
||||
}
|
||||
|
||||
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 Saml2RequestMatcher implements RequestMatcher {
|
||||
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (authentication == null) {
|
||||
return false;
|
||||
}
|
||||
return authentication.getPrincipal() instanceof Saml2AuthenticatedPrincipal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.http;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.BeanMetadataElement;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
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.registration.RelyingPartyRegistrationRepository;
|
||||
import org.springframework.security.saml2.provider.service.web.authentication.logout.HttpSessionLogoutRequestRepository;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Marcus da Coregio
|
||||
* @since 5.7
|
||||
*/
|
||||
final class Saml2LogoutBeanDefinitionParserUtils {
|
||||
|
||||
private static final String ATT_RELYING_PARTY_REGISTRATION_REPOSITORY_REF = "relying-party-registration-repository-ref";
|
||||
|
||||
private static final String ATT_LOGOUT_REQUEST_VALIDATOR_REF = "logout-request-validator-ref";
|
||||
|
||||
private static final String ATT_LOGOUT_REQUEST_REPOSITORY_REF = "logout-request-repository-ref";
|
||||
|
||||
private static final String ATT_LOGOUT_REQUEST_RESOLVER_REF = "logout-request-resolver-ref";
|
||||
|
||||
private static final String ATT_LOGOUT_RESPONSE_RESOLVER_REF = "logout-response-resolver-ref";
|
||||
|
||||
private static final String ATT_LOGOUT_RESPONSE_VALIDATOR_REF = "logout-response-validator-ref";
|
||||
|
||||
private Saml2LogoutBeanDefinitionParserUtils() {
|
||||
}
|
||||
|
||||
static BeanMetadataElement getRelyingPartyRegistrationRepository(Element element) {
|
||||
String relyingPartyRegistrationRepositoryRef = element
|
||||
.getAttribute(ATT_RELYING_PARTY_REGISTRATION_REPOSITORY_REF);
|
||||
if (StringUtils.hasText(relyingPartyRegistrationRepositoryRef)) {
|
||||
return new RuntimeBeanReference(relyingPartyRegistrationRepositoryRef);
|
||||
}
|
||||
return new RuntimeBeanReference(RelyingPartyRegistrationRepository.class);
|
||||
}
|
||||
|
||||
static BeanMetadataElement getLogoutResponseResolver(Element element, BeanMetadataElement registrations) {
|
||||
String logoutResponseResolver = element.getAttribute(ATT_LOGOUT_RESPONSE_RESOLVER_REF);
|
||||
if (StringUtils.hasText(logoutResponseResolver)) {
|
||||
return new RuntimeBeanReference(logoutResponseResolver);
|
||||
}
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(
|
||||
"org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutResponseResolver")
|
||||
.addConstructorArgValue(registrations).getBeanDefinition();
|
||||
}
|
||||
|
||||
static BeanMetadataElement getLogoutRequestValidator(Element element) {
|
||||
String logoutRequestValidator = element.getAttribute(ATT_LOGOUT_REQUEST_VALIDATOR_REF);
|
||||
if (StringUtils.hasText(logoutRequestValidator)) {
|
||||
return new RuntimeBeanReference(logoutRequestValidator);
|
||||
}
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(OpenSamlLogoutRequestValidator.class).getBeanDefinition();
|
||||
}
|
||||
|
||||
static BeanMetadataElement getLogoutResponseValidator(Element element) {
|
||||
String logoutResponseValidator = element.getAttribute(ATT_LOGOUT_RESPONSE_VALIDATOR_REF);
|
||||
if (StringUtils.hasText(logoutResponseValidator)) {
|
||||
return new RuntimeBeanReference(logoutResponseValidator);
|
||||
}
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(OpenSamlLogoutResponseValidator.class).getBeanDefinition();
|
||||
}
|
||||
|
||||
static BeanMetadataElement getLogoutRequestRepository(Element element) {
|
||||
String logoutRequestRepository = element.getAttribute(ATT_LOGOUT_REQUEST_REPOSITORY_REF);
|
||||
if (StringUtils.hasText(logoutRequestRepository)) {
|
||||
return new RuntimeBeanReference(logoutRequestRepository);
|
||||
}
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(HttpSessionLogoutRequestRepository.class).getBeanDefinition();
|
||||
}
|
||||
|
||||
static BeanMetadataElement getLogoutRequestResolver(Element element, BeanMetadataElement registrations) {
|
||||
String logoutRequestResolver = element.getAttribute(ATT_LOGOUT_REQUEST_RESOLVER_REF);
|
||||
if (StringUtils.hasText(logoutRequestResolver)) {
|
||||
return new RuntimeBeanReference(logoutRequestResolver);
|
||||
}
|
||||
return BeanDefinitionBuilder.rootBeanDefinition(
|
||||
"org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutRequestResolver")
|
||||
.addConstructorArgValue(registrations).getBeanDefinition();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -29,10 +29,6 @@ enum SecurityFilters {
|
||||
|
||||
FIRST(Integer.MIN_VALUE),
|
||||
|
||||
DISABLE_ENCODE_URL_FILTER,
|
||||
|
||||
FORCE_EAGER_SESSION_FILTER,
|
||||
|
||||
CHANNEL_FILTER,
|
||||
|
||||
SECURITY_CONTEXT_FILTER,
|
||||
@@ -45,20 +41,12 @@ enum SecurityFilters {
|
||||
|
||||
CORS_FILTER,
|
||||
|
||||
SAML2_LOGOUT_REQUEST_FILTER,
|
||||
|
||||
SAML2_LOGOUT_RESPONSE_FILTER,
|
||||
|
||||
CSRF_FILTER,
|
||||
|
||||
SAML2_LOGOUT_FILTER,
|
||||
|
||||
LOGOUT_FILTER,
|
||||
|
||||
OAUTH2_AUTHORIZATION_REQUEST_FILTER,
|
||||
|
||||
SAML2_AUTHENTICATION_REQUEST_FILTER,
|
||||
|
||||
X509_FILTER,
|
||||
|
||||
PRE_AUTH_FILTER,
|
||||
@@ -67,8 +55,6 @@ enum SecurityFilters {
|
||||
|
||||
OAUTH2_LOGIN_FILTER,
|
||||
|
||||
SAML2_AUTHENTICATION_FILTER,
|
||||
|
||||
FORM_LOGIN_FILTER,
|
||||
|
||||
OPENID_FILTER,
|
||||
|
||||
-183
@@ -1,183 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.ldap;
|
||||
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.ldap.authentication.AbstractLdapAuthenticator;
|
||||
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
|
||||
import org.springframework.security.ldap.search.FilterBasedLdapUserSearch;
|
||||
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
|
||||
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
|
||||
|
||||
/**
|
||||
* Creates an {@link AuthenticationManager} that can perform LDAP authentication.
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.7
|
||||
*/
|
||||
public abstract class AbstractLdapAuthenticationManagerFactory<T extends AbstractLdapAuthenticator> {
|
||||
|
||||
AbstractLdapAuthenticationManagerFactory(BaseLdapPathContextSource contextSource) {
|
||||
this.contextSource = contextSource;
|
||||
}
|
||||
|
||||
private BaseLdapPathContextSource contextSource;
|
||||
|
||||
private String[] userDnPatterns;
|
||||
|
||||
private LdapAuthoritiesPopulator ldapAuthoritiesPopulator;
|
||||
|
||||
private GrantedAuthoritiesMapper authoritiesMapper;
|
||||
|
||||
private UserDetailsContextMapper userDetailsContextMapper;
|
||||
|
||||
private String userSearchFilter;
|
||||
|
||||
private String userSearchBase = "";
|
||||
|
||||
/**
|
||||
* Sets the {@link BaseLdapPathContextSource} used to perform LDAP authentication.
|
||||
* @param contextSource the {@link BaseLdapPathContextSource} used to perform LDAP
|
||||
* authentication
|
||||
*/
|
||||
public void setContextSource(BaseLdapPathContextSource contextSource) {
|
||||
this.contextSource = contextSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link BaseLdapPathContextSource} used to perform LDAP authentication.
|
||||
* @return the {@link BaseLdapPathContextSource} used to perform LDAP authentication
|
||||
*/
|
||||
protected final BaseLdapPathContextSource getContextSource() {
|
||||
return this.contextSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link LdapAuthoritiesPopulator} used to obtain a list of granted
|
||||
* authorities for an LDAP user.
|
||||
* @param ldapAuthoritiesPopulator the {@link LdapAuthoritiesPopulator} to use
|
||||
*/
|
||||
public void setLdapAuthoritiesPopulator(LdapAuthoritiesPopulator ldapAuthoritiesPopulator) {
|
||||
this.ldapAuthoritiesPopulator = ldapAuthoritiesPopulator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link GrantedAuthoritiesMapper} used for converting the authorities
|
||||
* loaded from storage to a new set of authorities which will be associated to the
|
||||
* {@link UsernamePasswordAuthenticationToken}.
|
||||
* @param authoritiesMapper the {@link GrantedAuthoritiesMapper} used for mapping the
|
||||
* user's authorities
|
||||
*/
|
||||
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
|
||||
this.authoritiesMapper = authoritiesMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a custom strategy to be used for creating the {@link UserDetails} which will
|
||||
* be stored as the principal in the {@link Authentication}.
|
||||
* @param userDetailsContextMapper the strategy instance
|
||||
*/
|
||||
public void setUserDetailsContextMapper(UserDetailsContextMapper userDetailsContextMapper) {
|
||||
this.userDetailsContextMapper = userDetailsContextMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* If your users are at a fixed location in the directory (i.e. you can work out the
|
||||
* DN directly from the username without doing a directory search), you can use this
|
||||
* attribute to map directly to the DN. It maps directly to the userDnPatterns
|
||||
* property of AbstractLdapAuthenticator. The value is a specific pattern used to
|
||||
* build the user's DN, for example "uid={0},ou=people". The key "{0}" must be present
|
||||
* and will be substituted with the username.
|
||||
* @param userDnPatterns the LDAP patterns for finding the usernames
|
||||
*/
|
||||
public void setUserDnPatterns(String... userDnPatterns) {
|
||||
this.userDnPatterns = userDnPatterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* The LDAP filter used to search for users (optional). For example "(uid={0})". The
|
||||
* substituted parameter is the user's login name.
|
||||
* @param userSearchFilter the LDAP filter used to search for users
|
||||
*/
|
||||
public void setUserSearchFilter(String userSearchFilter) {
|
||||
this.userSearchFilter = userSearchFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search base for user searches. Defaults to "". Only used with
|
||||
* {@link #setUserSearchFilter(String)}.
|
||||
* @param userSearchBase search base for user searches
|
||||
*/
|
||||
public void setUserSearchBase(String userSearchBase) {
|
||||
this.userSearchBase = userSearchBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured {@link AuthenticationManager} that can be used to perform
|
||||
* LDAP authentication.
|
||||
* @return the configured {@link AuthenticationManager}
|
||||
*/
|
||||
public final AuthenticationManager createAuthenticationManager() {
|
||||
LdapAuthenticationProvider ldapAuthenticationProvider = getProvider();
|
||||
return new ProviderManager(ldapAuthenticationProvider);
|
||||
}
|
||||
|
||||
private LdapAuthenticationProvider getProvider() {
|
||||
AbstractLdapAuthenticator authenticator = getAuthenticator();
|
||||
LdapAuthenticationProvider provider;
|
||||
if (this.ldapAuthoritiesPopulator != null) {
|
||||
provider = new LdapAuthenticationProvider(authenticator, this.ldapAuthoritiesPopulator);
|
||||
}
|
||||
else {
|
||||
provider = new LdapAuthenticationProvider(authenticator);
|
||||
}
|
||||
if (this.authoritiesMapper != null) {
|
||||
provider.setAuthoritiesMapper(this.authoritiesMapper);
|
||||
}
|
||||
if (this.userDetailsContextMapper != null) {
|
||||
provider.setUserDetailsContextMapper(this.userDetailsContextMapper);
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
private AbstractLdapAuthenticator getAuthenticator() {
|
||||
AbstractLdapAuthenticator authenticator = createDefaultLdapAuthenticator();
|
||||
if (this.userSearchFilter != null) {
|
||||
authenticator.setUserSearch(
|
||||
new FilterBasedLdapUserSearch(this.userSearchBase, this.userSearchFilter, this.contextSource));
|
||||
}
|
||||
if (this.userDnPatterns != null && this.userDnPatterns.length > 0) {
|
||||
authenticator.setUserDnPatterns(this.userDnPatterns);
|
||||
}
|
||||
authenticator.afterPropertiesSet();
|
||||
return authenticator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows subclasses to supply the default {@link AbstractLdapAuthenticator}.
|
||||
* @return the {@link AbstractLdapAuthenticator} that will be configured for LDAP
|
||||
* authentication
|
||||
*/
|
||||
protected abstract T createDefaultLdapAuthenticator();
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user