Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b512ea6352 | |||
| 076ef2cc74 | |||
| 3cedc1a2e4 | |||
| d32b23e497 | |||
| b58eef0d5d | |||
| 1202f08f3e | |||
| 8e315eae11 | |||
| 03a504b4ff | |||
| 3c458bc661 | |||
| 1bb9ed906e | |||
| fc9a3f95ec | |||
| 7f1700d043 | |||
| 56456601ab | |||
| 249ea55494 | |||
| 43f42d8e32 | |||
| 734d989c32 | |||
| 2f572278d4 | |||
| 816066ab60 | |||
| fb3f864d27 | |||
| ab29661a68 | |||
| 35abf87e2e | |||
| 4e3811cb29 |
@@ -0,0 +1,7 @@
|
||||
name: Build Release
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Build Release
|
||||
shell: bash
|
||||
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository publishAllPublicationsToDeploymentRepository
|
||||
@@ -0,0 +1,7 @@
|
||||
name: Test Release
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Test Release
|
||||
shell: bash
|
||||
run: ./gradlew build
|
||||
@@ -0,0 +1,17 @@
|
||||
workflow:
|
||||
generator:
|
||||
project:
|
||||
java:
|
||||
versions:
|
||||
primary: 25
|
||||
workflows:
|
||||
release-train:
|
||||
build:
|
||||
env:
|
||||
COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME
|
||||
COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD
|
||||
test:
|
||||
env:
|
||||
COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME
|
||||
COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
name: Merge Dependabot PR
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'docs-build'
|
||||
|
||||
run-name: Merge Dependabot PR ${{ github.ref_name }}
|
||||
|
||||
jobs:
|
||||
merge-dependabot-pr:
|
||||
permissions: write-all
|
||||
uses: spring-io/spring-github-workflows/.github/workflows/spring-merge-dependabot-pr.yml@0d3f15bb384839966a1ff5c4383731a2b747f24b # v7
|
||||
with:
|
||||
mergeArguments: --auto --rebase
|
||||
@@ -1,36 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch: # Manual trigger
|
||||
|
||||
env:
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
snapshot-test:
|
||||
name: Test Against Snapshots
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/test.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- java-version: 25
|
||||
toolchain: 25
|
||||
with:
|
||||
java-version: ${{ matrix.java-version }}
|
||||
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot,https://oss.sonatype.org/content/repositories/snapshots -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=7.0.+ -PreactorVersion=2025.+ -PspringDataVersion=2025.+ -PmicrometerVersion=1.+ --stacktrace
|
||||
secrets: inherit
|
||||
send-notification:
|
||||
name: Send Notification
|
||||
needs: [ snapshot-test ]
|
||||
if: ${{ !success() }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Notification
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
@@ -1,23 +0,0 @@
|
||||
name: Clean build artifacts
|
||||
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:
|
||||
GH_ACTIONS_REPO_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
run: |
|
||||
echo "Running clean build artifacts logic"
|
||||
output=$(curl -X GET -H "Authorization: token $GH_ACTIONS_REPO_TOKEN" https://api.github.com/repos/spring-projects/spring-security/actions/artifacts | grep '"id"' | cut -d : -f2 | sed 's/,*$//g')
|
||||
echo Output is $output
|
||||
for id in $output; do curl -X DELETE -H "Authorization: token $GH_ACTIONS_REPO_TOKEN" https://api.github.com/repos/spring-projects/spring-security/actions/artifacts/$id; done;
|
||||
@@ -1,17 +0,0 @@
|
||||
name: "CodeQL Advanced"
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule
|
||||
- cron: '0 5 * * *'
|
||||
permissions: read-all
|
||||
jobs:
|
||||
codeql-analysis-call:
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
uses: spring-io/github-actions/.github/workflows/codeql-analysis.yml@e415dadd0910c901e7a7fabd67bbb355b2324500 # 1
|
||||
@@ -8,45 +8,42 @@ on:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch: # Manual trigger
|
||||
|
||||
env:
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/build.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest ]
|
||||
jdk: [ 25 ]
|
||||
uses: spring-projects/spring-security-commercial/.github/workflows/build.yml@workflows/v1
|
||||
with:
|
||||
runs-on: ${{ matrix.os }}
|
||||
java-version: ${{ matrix.jdk }}
|
||||
distribution: temurin
|
||||
java-version: '25'
|
||||
secrets: inherit
|
||||
test:
|
||||
name: Test Against Snapshots
|
||||
uses: spring-projects/spring-security-commercial/.github/workflows/test.yml@workflows/v1
|
||||
with:
|
||||
java-version: '25'
|
||||
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot -PisOverrideVersionCatalog -PtestToolchain=25 -PspringFrameworkVersion=7.0.+ -PreactorVersion=2025.0.+ -PspringDataVersion=2026.0.+ -PmicrometerVersion=1.17.+ --stacktrace
|
||||
secrets: inherit
|
||||
compute-version:
|
||||
name: Compute Version
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
snapshot: ${{ steps.project-version.outputs.snapshot }}
|
||||
steps:
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 7.0.0
|
||||
- id: project-version
|
||||
name: Extract Project Version
|
||||
uses: spring-io/spring-release-actions/compute-version@a1f321783a0769dd2aea4fad6c2ae2f95a52b885 # 0.0.5
|
||||
deploy-artifacts:
|
||||
name: Deploy Artifacts
|
||||
needs: [ build ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
with:
|
||||
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
default-publish-milestones-central: true
|
||||
java-version: 25
|
||||
secrets: inherit
|
||||
deploy-schema:
|
||||
name: Deploy Schema
|
||||
needs: [ build ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@3f6cc7ffc137ca160061749d5f34dc30d5f36986 # v1.0.17
|
||||
with:
|
||||
should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
java-version: 25
|
||||
needs: [ build, test, compute-version ]
|
||||
if: needs.compute-version.outputs.snapshot == 'true'
|
||||
uses: spring-projects/spring-security-commercial/.github/workflows/deploy-artifacts.yml@workflows/v1
|
||||
secrets: inherit
|
||||
send-notification:
|
||||
name: Send Notification
|
||||
needs: [ deploy-artifacts, deploy-schema ]
|
||||
needs: [ deploy-artifacts ]
|
||||
if: ${{ !success() }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
name: Defer Issues
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
defer-issues:
|
||||
name: Defer Issues
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Compute Version
|
||||
id: compute-version
|
||||
uses: spring-io/spring-release-actions/compute-version@a1f321783a0769dd2aea4fad6c2ae2f95a52b885 # 0.0.5
|
||||
- name: Get Today's Release Version
|
||||
id: todays-release
|
||||
uses: spring-io/spring-release-actions/get-todays-release-version@a1f321783a0769dd2aea4fad6c2ae2f95a52b885 # 0.0.5
|
||||
with:
|
||||
snapshot-version: ${{ steps.compute-version.outputs.version }}
|
||||
milestone-repository: ${{ github.repository }}
|
||||
milestone-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Compute Next Version
|
||||
id: next-version
|
||||
uses: spring-io/spring-release-actions/compute-next-version@1b8671612c3eb3d9b9763e2d7b66f1a80d00ee95 # 0.0.6
|
||||
with:
|
||||
version: ${{ steps.todays-release.outputs.release-version }}
|
||||
- name: Schedule Next Milestone
|
||||
uses: spring-io/spring-release-actions/schedule-milestone@a1f321783a0769dd2aea4fad6c2ae2f95a52b885 # 0.0.5
|
||||
with:
|
||||
version: ${{ steps.next-version.outputs.version }}
|
||||
version-date: ${{ steps.next-version.outputs.version-date }}
|
||||
repository: ${{ github.repository }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Move Open Issues to Next Milestone
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CURRENT_MILESTONE: ${{ steps.todays-release.outputs.release-version }}
|
||||
NEXT_MILESTONE: ${{ steps.next-version.outputs.version }}
|
||||
run: |
|
||||
current_milestone_number=$(gh api repos/${{ github.repository }}/milestones \
|
||||
--jq ".[] | select(.title == \"$CURRENT_MILESTONE\") | .number")
|
||||
if [ -z "$current_milestone_number" ]; then
|
||||
echo "No milestone found for $CURRENT_MILESTONE"
|
||||
exit 0
|
||||
fi
|
||||
next_milestone_number=$(gh api repos/${{ github.repository }}/milestones \
|
||||
--jq ".[] | select(.title == \"$NEXT_MILESTONE\") | .number")
|
||||
if [ -z "$next_milestone_number" ]; then
|
||||
echo "No milestone found for $NEXT_MILESTONE"
|
||||
exit 1
|
||||
fi
|
||||
echo "Moving open issues from milestone '$CURRENT_MILESTONE' (#$current_milestone_number) to '$NEXT_MILESTONE' (#$next_milestone_number)"
|
||||
page=1
|
||||
while true; do
|
||||
issues=$(gh api "repos/${{ github.repository }}/issues?milestone=$current_milestone_number&state=open&per_page=100&page=$page" \
|
||||
--jq '.[].number')
|
||||
if [ -z "$issues" ]; then
|
||||
break
|
||||
fi
|
||||
for issue in $issues; do
|
||||
echo "Moving issue/PR #$issue to milestone $NEXT_MILESTONE"
|
||||
gh api repos/${{ github.repository }}/issues/$issue \
|
||||
--method PATCH \
|
||||
--field milestone=$next_milestone_number \
|
||||
--silent
|
||||
done
|
||||
page=$((page + 1))
|
||||
done
|
||||
echo "Done."
|
||||
@@ -0,0 +1,10 @@
|
||||
name: Dependabot PR Build
|
||||
|
||||
on: pull_request_target
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
uses: spring-projects/spring-security-commercial/.github/workflows/build-pull-request.yml@workflows/v1
|
||||
if: ${{ github.actor == 'dependabot[bot]' }}
|
||||
secrets: inherit
|
||||
@@ -1,33 +0,0 @@
|
||||
name: Deploy Docs
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- "gh-pages"
|
||||
- "dependabot/**"
|
||||
tags: '**'
|
||||
repository_dispatch:
|
||||
types: request-build-reference # legacy
|
||||
#schedule:
|
||||
#- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch:
|
||||
permissions: read-all
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
ref: docs-build
|
||||
fetch-depth: 1
|
||||
- name: Dispatch (partial build)
|
||||
if: github.ref_type == 'branch'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }}
|
||||
- name: Dispatch (full build)
|
||||
if: github.ref_type == 'tag'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD)
|
||||
@@ -1,34 +0,0 @@
|
||||
name: Execute Gradle Wrapper Upgrade
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # 2am UTC
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
pull-requests: write
|
||||
jobs:
|
||||
upgrade_wrapper:
|
||||
name: Execution
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Git configuration
|
||||
env:
|
||||
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
git config --global url."https://unused-username:${TOKEN}@github.com/".insteadOf "https://github.com/"
|
||||
git config --global user.name 'github-actions[bot]'
|
||||
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Set up JDK 25
|
||||
uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
|
||||
with:
|
||||
java-version: '25'
|
||||
distribution: 'temurin'
|
||||
- name: Set up Gradle
|
||||
uses: gradle/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
||||
- name: Upgrade Wrappers
|
||||
run: ./gradlew clean upgradeGradleWrapperAll --continue -Porg.gradle.java.installations.auto-download=false
|
||||
env:
|
||||
WRAPPER_UPGRADE_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,63 @@
|
||||
name: Merge Dependabot PR
|
||||
|
||||
on: pull_request_target
|
||||
|
||||
run-name: Merge Dependabot PR ${{ github.ref_name }}
|
||||
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
merge-dependabot-pr:
|
||||
name: Merge Dependabot PR
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'spring-projects/spring-security-commercial' }}
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
show-progress: false
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: Set Milestone to Dependabot Pull Request
|
||||
id: set-milestone
|
||||
run: |
|
||||
if test -f pom.xml
|
||||
then
|
||||
CURRENT_VERSION=$(mvn help:evaluate -Dexpression="project.version" -q -DforceStdout)
|
||||
else
|
||||
CURRENT_VERSION=$(cat gradle.properties | sed -n '/^version=/ { s/^version=//;p }')
|
||||
fi
|
||||
export CANDIDATE_VERSION=${CURRENT_VERSION/-SNAPSHOT}
|
||||
MILESTONE=$(gh api repos/$GITHUB_REPOSITORY/milestones --jq 'map(select(.due_on != null and (.title | startswith(env.CANDIDATE_VERSION)))) | .[0] | .title')
|
||||
|
||||
if [ -z $MILESTONE ]
|
||||
then
|
||||
gh run cancel ${{ github.run_id }}
|
||||
echo "::warning title=Cannot merge::No scheduled milestone for $CURRENT_VERSION version"
|
||||
else
|
||||
gh pr edit ${{ github.event.pull_request.number }} --milestone $MILESTONE
|
||||
echo mergeEnabled=true >> $GITHUB_OUTPUT
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Merge Dependabot pull request
|
||||
if: steps.set-milestone.outputs.mergeEnabled
|
||||
run: gh pr merge ${{ github.event.pull_request.number }} --auto --rebase
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
send-notification:
|
||||
name: Send Notification
|
||||
needs: [ merge-dependabot-pr ]
|
||||
if: ${{ failure() || cancelled() }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Notification
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
@@ -8,44 +8,6 @@ permissions:
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@c8668747d7c264864c8c7f7026d0d277d14a78dc # v2.0.6
|
||||
with:
|
||||
java-version: '25'
|
||||
distribution: 'temurin'
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew clean build -PskipCheckExpectedBranchVersion --continue --scan
|
||||
generate-docs:
|
||||
name: Generate Docs
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@c8668747d7c264864c8c7f7026d0d277d14a78dc # v2.0.6
|
||||
with:
|
||||
java-version: '25'
|
||||
distribution: 'temurin'
|
||||
- name: Run Antora
|
||||
run: ./gradlew -PbuildSrc.skipTests=true :spring-security-docs:antora
|
||||
- name: Upload Docs
|
||||
id: upload
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: docs
|
||||
path: docs/build/site
|
||||
overwrite: true
|
||||
send-notification:
|
||||
name: Send Notification
|
||||
needs: [ build, generate-docs ]
|
||||
if: ${{ failure() && github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'spring-projects/spring-security' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Notification
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
uses: spring-projects/spring-security-commercial/.github/workflows/build-pull-request.yml@workflows/v1
|
||||
if: ${{ github.actor != 'dependabot[bot]' }}
|
||||
secrets: inherit
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
name: Release Announcements - Publish
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: The version to publish
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
deploy-schema:
|
||||
name: Deploy Schema
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@3f6cc7ffc137ca160061749d5f34dc30d5f36986 # v1.0.17
|
||||
with:
|
||||
should-deploy-schema: true
|
||||
secrets: inherit
|
||||
publish-release-announcements:
|
||||
name: Publish Release Announcements
|
||||
needs: [ deploy-schema ]
|
||||
if: ${{ !github.event.repository.fork }}
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/release-announcements-publish.yml@d6c65d3013c0888e2c9cbae9f4beda610994776c # v1.0.16
|
||||
with:
|
||||
version: ${{ inputs.version }}
|
||||
secrets: inherit
|
||||
@@ -1,24 +0,0 @@
|
||||
name: Release Announcements - Stage
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '[0-9]+.[0-9]+.[0-9]+'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: The version to stage
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
stage-release-announcements:
|
||||
name: Stage Release Announcements
|
||||
if: ${{ !github.event.repository.fork }}
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/release-announcements-stage.yml@3f6cc7ffc137ca160061749d5f34dc30d5f36986 # v1.0.17
|
||||
with:
|
||||
version: ${{ inputs.version || github.ref_name }}
|
||||
secrets: inherit
|
||||
@@ -0,0 +1,92 @@
|
||||
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||
|
||||
name: "Release Train – Build"
|
||||
run-name: "${{ inputs.callback-ref }} – Build"
|
||||
"on":
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
callback:
|
||||
description: "Repository to which a callback should be made upon completion"
|
||||
required: true
|
||||
type: "string"
|
||||
callback-ref:
|
||||
description: "Ref in the callback repository to which a callback should be made upon completion"
|
||||
required: true
|
||||
type: "string"
|
||||
release-train-maven-repository-url:
|
||||
description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train"
|
||||
required: true
|
||||
type: "string"
|
||||
permissions:
|
||||
contents: "read"
|
||||
concurrency:
|
||||
group: "${{ github.workflow }}-${{ github.ref }}"
|
||||
jobs:
|
||||
build-release:
|
||||
name: "Build Release"
|
||||
runs-on: "ubuntu22-2-8"
|
||||
steps:
|
||||
- name: "Prevent Re-runs"
|
||||
id: "prevent-re-runs"
|
||||
run: |-
|
||||
if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then
|
||||
echo "Re-runs are prohibited. Use the 'Release Train – Retry' workflow to retry build failures"
|
||||
exit 1
|
||||
fi
|
||||
- name: "Set up Java"
|
||||
id: "set-up-java"
|
||||
uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0
|
||||
with:
|
||||
distribution: "liberica"
|
||||
java-version: "25"
|
||||
- name: "Check Out Code"
|
||||
id: "check-out-code"
|
||||
uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0
|
||||
- name: "Build Release"
|
||||
id: "build-release"
|
||||
uses: "./.github/actions/release-train-build"
|
||||
env:
|
||||
COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}"
|
||||
COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}"
|
||||
RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}"
|
||||
RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}"
|
||||
RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}"
|
||||
- name: "Upload Deployment Repository"
|
||||
id: "upload-deployment-repository"
|
||||
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
|
||||
with:
|
||||
name: "deployment-repository"
|
||||
path: "deployment-repository/**"
|
||||
- name: "Upload Deployment Spec"
|
||||
id: "upload-deployment-spec"
|
||||
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
|
||||
with:
|
||||
archive: "false"
|
||||
if-no-files-found: "ignore"
|
||||
name: "deployment-spec"
|
||||
path: ".github/actions/release-train-build/deployment-spec.yml"
|
||||
- name: "Save Build System Caches"
|
||||
id: "save-build-system-caches"
|
||||
uses: "actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0
|
||||
with:
|
||||
key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}"
|
||||
path: |-
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
- name: "Send Callback"
|
||||
id: "send-callback"
|
||||
if: "${{ !cancelled() }}"
|
||||
env:
|
||||
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||
run: |-
|
||||
gh workflow run callback \
|
||||
--repo ${{ inputs.callback }} \
|
||||
--ref ${{ inputs.callback-ref }} \
|
||||
--field commit-hash=${{ steps.check-out-code.outputs.commit }} \
|
||||
--field deployment-repository-artifact-identifier=${{ steps.upload-deployment-repository.outputs.artifact-id }} \
|
||||
--field deployment-spec-artifact-identifier=${{ steps.upload-deployment-spec.outputs.artifact-id }} \
|
||||
--field release-branch=${{ github.ref_name }} \
|
||||
--field release-repository=${{ github.repository }} \
|
||||
--field result=${{ job.status == 'success' && 'built' || 'build-failed' }} \
|
||||
--field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
@@ -0,0 +1,55 @@
|
||||
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||
|
||||
name: "Release Train – Join"
|
||||
run-name: "${{ inputs.release-train }} – Join"
|
||||
"on":
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
deployment-destination:
|
||||
description: "Destination to which the release should be deployed"
|
||||
options:
|
||||
- "Maven Central"
|
||||
- "Spring Enterprise"
|
||||
required: true
|
||||
type: "choice"
|
||||
release-train:
|
||||
description: "Release train"
|
||||
required: true
|
||||
type: "string"
|
||||
release-train-repository:
|
||||
default: "spring-io/release-train"
|
||||
description: "Release train repository"
|
||||
required: true
|
||||
type: "string"
|
||||
permissions:
|
||||
contents: "none"
|
||||
jobs:
|
||||
join-release-train:
|
||||
name: "Join Release Train"
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: "Join Release Train"
|
||||
id: "join-release-train"
|
||||
env:
|
||||
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||
run: |-
|
||||
run_url=$(
|
||||
gh workflow run join \
|
||||
--repo ${{ inputs.release-train-repository }} \
|
||||
--ref ${{ inputs.release-train }} \
|
||||
--field commit-hash=${{ github.sha }} \
|
||||
--field deployment-destination=${{ inputs.deployment-destination == 'Maven Central' && 'maven-central' || 'spring-enterprise' }} \
|
||||
--field release-branch=${{ github.ref_name }} \
|
||||
--field release-repository=${{ github.repository }}
|
||||
)
|
||||
echo "Dispatched workflow run. Waiting for $run_url to complete."
|
||||
run_id=${run_url##*/}
|
||||
watch_exit_code=0
|
||||
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
|
||||
if [[ $watch_exit_code -eq 0 ]]; then
|
||||
echo "Workflow run succeeded."
|
||||
else
|
||||
echo "Workflow run failed."
|
||||
fi
|
||||
exit $watch_exit_code
|
||||
@@ -0,0 +1,46 @@
|
||||
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||
|
||||
name: "Release Train – Leave"
|
||||
run-name: "${{ inputs.release-train }} – Leave"
|
||||
"on":
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release-train:
|
||||
description: "Release train"
|
||||
required: true
|
||||
type: "string"
|
||||
release-train-repository:
|
||||
default: "spring-io/release-train"
|
||||
description: "Release train repository"
|
||||
required: true
|
||||
type: "string"
|
||||
permissions:
|
||||
contents: "none"
|
||||
jobs:
|
||||
leave:
|
||||
name: "Leave"
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: "Leave"
|
||||
id: "leave"
|
||||
env:
|
||||
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||
run: |-
|
||||
run_url=$(
|
||||
gh workflow run leave \
|
||||
--repo ${{ inputs.release-train-repository }} \
|
||||
--ref ${{ inputs.release-train }} \
|
||||
--field release-branch=${{ github.ref_name }} \
|
||||
--field release-repository=${{ github.repository }}
|
||||
)
|
||||
echo "Dispatched workflow run. Waiting for $run_url to complete."
|
||||
run_id=${run_url##*/}
|
||||
watch_exit_code=0
|
||||
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
|
||||
if [[ $watch_exit_code -eq 0 ]]; then
|
||||
echo "Workflow run succeeded."
|
||||
else
|
||||
echo "Workflow run failed."
|
||||
fi
|
||||
exit $watch_exit_code
|
||||
@@ -0,0 +1,47 @@
|
||||
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||
|
||||
name: "Release Train – Ready"
|
||||
run-name: "${{ inputs.release-train }} – Ready"
|
||||
"on":
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release-train:
|
||||
description: "Release train"
|
||||
required: true
|
||||
type: "string"
|
||||
release-train-repository:
|
||||
default: "spring-io/release-train"
|
||||
description: "Release train repository"
|
||||
required: true
|
||||
type: "string"
|
||||
permissions:
|
||||
contents: "none"
|
||||
jobs:
|
||||
ready:
|
||||
name: "Ready"
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: "Ready"
|
||||
id: "ready"
|
||||
env:
|
||||
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||
run: |-
|
||||
run_url=$(
|
||||
gh workflow run ready \
|
||||
--repo ${{ inputs.release-train-repository }} \
|
||||
--ref ${{ inputs.release-train }} \
|
||||
--field commit-hash=${{ github.sha }} \
|
||||
--field release-branch=${{ github.ref_name }} \
|
||||
--field release-repository=${{ github.repository }}
|
||||
)
|
||||
echo "Dispatched workflow run. Waiting for $run_url to complete."
|
||||
run_id=${run_url##*/}
|
||||
watch_exit_code=0
|
||||
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
|
||||
if [[ $watch_exit_code -eq 0 ]]; then
|
||||
echo "Workflow run succeeded."
|
||||
else
|
||||
echo "Workflow run failed."
|
||||
fi
|
||||
exit $watch_exit_code
|
||||
@@ -0,0 +1,34 @@
|
||||
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||
|
||||
name: "Release Train – Retry"
|
||||
run-name: "${{ inputs.release-train }} – Retry"
|
||||
"on":
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release-train:
|
||||
description: "Release train"
|
||||
required: true
|
||||
type: "string"
|
||||
release-train-repository:
|
||||
default: "spring-io/release-train"
|
||||
description: "Release train repository"
|
||||
required: true
|
||||
type: "string"
|
||||
permissions:
|
||||
contents: "none"
|
||||
jobs:
|
||||
trigger-retry:
|
||||
name: "Trigger Retry"
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: "Trigger Retry"
|
||||
id: "trigger-retry"
|
||||
env:
|
||||
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||
run: |-
|
||||
gh workflow run retry \
|
||||
--repo ${{ inputs.release-train-repository }} \
|
||||
--ref ${{ inputs.release-train }} \
|
||||
--field release-branch=${{ github.ref_name }} \
|
||||
--field release-repository=${{ github.repository }}
|
||||
@@ -0,0 +1,83 @@
|
||||
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
|
||||
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
|
||||
|
||||
name: "Release Train – Test"
|
||||
run-name: "${{ inputs.callback-ref }} – Test"
|
||||
"on":
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
callback:
|
||||
description: "Repository to which a callback should be made upon completion"
|
||||
required: true
|
||||
type: "string"
|
||||
callback-ref:
|
||||
description: "Ref in the callback repository to which a callback should be made upon completion"
|
||||
required: true
|
||||
type: "string"
|
||||
release-train-maven-repository-url:
|
||||
description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train"
|
||||
required: true
|
||||
type: "string"
|
||||
permissions:
|
||||
contents: "read"
|
||||
concurrency:
|
||||
group: "${{ github.workflow }}-${{ github.ref }}"
|
||||
jobs:
|
||||
test-release:
|
||||
name: "Test Release"
|
||||
runs-on: "ubuntu22-2-8"
|
||||
steps:
|
||||
- name: "Prevent Re-runs"
|
||||
id: "prevent-re-runs"
|
||||
run: |-
|
||||
if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then
|
||||
echo "Re-runs are prohibited. Use the 'Release Train – Retry' workflow to retry test failures"
|
||||
exit 1
|
||||
fi
|
||||
- name: "Set up Java"
|
||||
id: "set-up-java"
|
||||
uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0
|
||||
with:
|
||||
distribution: "liberica"
|
||||
java-version: "25"
|
||||
- name: "Check Out Code"
|
||||
id: "check-out-code"
|
||||
uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0
|
||||
- name: "Restore Build System Caches"
|
||||
id: "restore-build-system-caches"
|
||||
uses: "actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0
|
||||
with:
|
||||
key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}"
|
||||
path: |-
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
- name: "Test Release"
|
||||
id: "test-release"
|
||||
uses: "./.github/actions/release-train-test"
|
||||
env:
|
||||
COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}"
|
||||
COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}"
|
||||
RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}"
|
||||
RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}"
|
||||
RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}"
|
||||
- name: "Send Callback"
|
||||
id: "send-callback"
|
||||
if: "${{ !cancelled() }}"
|
||||
env:
|
||||
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
|
||||
run: |-
|
||||
gh workflow run callback \
|
||||
--repo ${{ inputs.callback }} \
|
||||
--ref ${{ inputs.callback-ref }} \
|
||||
--field commit-hash=${{ steps.check-out-code.outputs.commit }} \
|
||||
--field release-branch=${{ github.ref_name }} \
|
||||
--field release-repository=${{ github.repository }} \
|
||||
--field result=${{ job.status == 'success' && 'tested' || 'test-failed' }} \
|
||||
--field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
- name: "Upload Build System Reports"
|
||||
id: "upload-build-system-reports"
|
||||
if: "${{ failure() }}"
|
||||
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
|
||||
with:
|
||||
name: "build-system-reports"
|
||||
path: "**/build/reports"
|
||||
@@ -1,37 +0,0 @@
|
||||
name: Update Antora UI Spring
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-antora-ui-spring:
|
||||
name: Update on Supported Branches
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
branch: [ '6.5.x', '7.0.x', '7.1.x', 'main' ]
|
||||
steps:
|
||||
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf # v0.0.22
|
||||
name: Update
|
||||
with:
|
||||
docs-branch: ${{ matrix.branch }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
antora-file-path: 'docs/antora-playbook.yml'
|
||||
update-antora-ui-spring-docs-build:
|
||||
name: Update on docs-build
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf # v0.0.22
|
||||
name: Update
|
||||
with:
|
||||
docs-branch: 'docs-build'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
+5
-8
@@ -27,6 +27,7 @@ apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
apply plugin: 'org.springframework.security.versions.verify-dependencies-versions'
|
||||
apply plugin: 'org.springframework.security.check-expected-branch-version'
|
||||
apply plugin: 'io.spring.security.release'
|
||||
apply from: 'commercial-settings.gradle'
|
||||
|
||||
group = 'org.springframework.security'
|
||||
description = 'Spring Security'
|
||||
@@ -41,10 +42,12 @@ repositories {
|
||||
}
|
||||
|
||||
springRelease {
|
||||
repositoryOwner = "spring-projects"
|
||||
repositoryName = "spring-security-commercial"
|
||||
weekOfMonth = 3
|
||||
dayOfWeek = 1
|
||||
referenceDocUrl = "https://docs.spring.io/spring-security/reference/{version}/index.html"
|
||||
apiDocUrl = "https://docs.spring.io/spring-security/reference/{version}/api/java/index.html"
|
||||
referenceDocUrl = "https://docs.spring.vmware.com/spring-security/reference/{version}/index.html"
|
||||
apiDocUrl = "https://docs.spring.vmware.com/spring-security/reference/{version}/api/java/index.html"
|
||||
replaceSnapshotVersionInReferenceDocUrl = true
|
||||
}
|
||||
|
||||
@@ -72,12 +75,6 @@ allprojects {
|
||||
}
|
||||
}
|
||||
|
||||
develocity {
|
||||
buildScan {
|
||||
termsOfUseUrl = 'https://gradle.com/help/legal-terms-of-use'
|
||||
termsOfUseAgree = 'yes'
|
||||
}
|
||||
}
|
||||
|
||||
nohttp {
|
||||
source.exclude "buildSrc/build/**", "**/build/**", "**/target/**", "javascript/.gradle/**", "javascript/package-lock.json", "javascript/node_modules/**", "javascript/build/**", "javascript/dist/**"
|
||||
|
||||
+10
-1
@@ -13,7 +13,16 @@ java {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
maven { url = 'https://repo.spring.io/snapshot' }
|
||||
if (project.hasProperty("artifactoryUsername") && project.hasProperty("artifactoryPassword")) {
|
||||
maven {
|
||||
name "spring-commercial-release"
|
||||
url "https://usw1.packages.broadcom.com/spring-enterprise-maven-prod-local"
|
||||
credentials {
|
||||
username project.artifactoryUsername
|
||||
password project.artifactoryPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
if (System.getenv("RELEASE_TRAIN_MAVEN_REPOSITORY_URL") != null) {
|
||||
maven {
|
||||
name = "Release Train"
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
subprojects {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
def repoUsername = project.findProperty("artifactoryUsername") ?: System.getenv("COMMERCIAL_REPO_USERNAME")
|
||||
def repoPassword = project.findProperty("artifactoryPassword") ?: System.getenv("COMMERCIAL_REPO_PASSWORD")
|
||||
if (repoUsername && repoPassword) {
|
||||
maven {
|
||||
name "spring-commercial-release"
|
||||
url "https://usw1.packages.broadcom.com/spring-enterprise-maven-prod-local"
|
||||
credentials {
|
||||
username repoUsername
|
||||
password repoPassword
|
||||
}
|
||||
}
|
||||
if ("$version".endsWith("-SNAPSHOT")) {
|
||||
maven {
|
||||
name "spring-commercial-snapshot"
|
||||
url "https://usw1.packages.broadcom.com/spring-enterprise-maven-dev-local"
|
||||
credentials {
|
||||
username repoUsername
|
||||
password repoPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-27
@@ -297,7 +297,6 @@ import org.springframework.web.util.pattern.PathPatternParser;
|
||||
* @author Ankur Pathak
|
||||
* @author Alexey Nesterov
|
||||
* @author Yanming Zhou
|
||||
* @author Iain Henderson
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ServerHttpSecurity {
|
||||
@@ -4139,8 +4138,6 @@ public class ServerHttpSecurity {
|
||||
|
||||
private ServerAuthenticationFailureHandler authenticationFailureHandler;
|
||||
|
||||
private ServerAuthenticationSuccessHandler authenticationSuccessHandler;
|
||||
|
||||
private ServerAccessDeniedHandler accessDeniedHandler = new BearerTokenServerAccessDeniedHandler();
|
||||
|
||||
private ServerAuthenticationConverter bearerTokenConverter = new ServerBearerTokenAuthenticationConverter();
|
||||
@@ -4189,20 +4186,6 @@ public class ServerHttpSecurity {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link ServerAuthenticationSuccessHandler} to use. The default
|
||||
* is {@link WebFilterChainServerAuthenticationSuccessHandler}
|
||||
* @param authenticationSuccessHandler the
|
||||
* {@link ServerAuthenticationSuccessHandler} to use
|
||||
* @return the {@link OAuth2ClientSpec} to customize
|
||||
* @since 7.2
|
||||
*/
|
||||
public OAuth2ResourceServerSpec authenticationSuccessHandler(
|
||||
ServerAuthenticationSuccessHandler authenticationSuccessHandler) {
|
||||
this.authenticationSuccessHandler = authenticationSuccessHandler;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link ServerAuthenticationConverter} to use for requests
|
||||
* authenticating with
|
||||
@@ -4271,7 +4254,6 @@ public class ServerHttpSecurity {
|
||||
AuthenticationWebFilter oauth2 = new AuthenticationWebFilter(this.authenticationManagerResolver);
|
||||
oauth2.setServerAuthenticationConverter(this.bearerTokenConverter);
|
||||
oauth2.setAuthenticationFailureHandler(authenticationFailureHandler());
|
||||
oauth2.setAuthenticationSuccessHandler(authenticationSuccessHandler());
|
||||
http.addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);
|
||||
}
|
||||
else if (this.jwt != null) {
|
||||
@@ -4331,13 +4313,6 @@ public class ServerHttpSecurity {
|
||||
return new ServerAuthenticationEntryPointFailureHandler(this.entryPoint);
|
||||
}
|
||||
|
||||
private ServerAuthenticationSuccessHandler authenticationSuccessHandler() {
|
||||
if (this.authenticationSuccessHandler != null) {
|
||||
return this.authenticationSuccessHandler;
|
||||
}
|
||||
return new WebFilterChainServerAuthenticationSuccessHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures JWT Resource Server Support
|
||||
*/
|
||||
@@ -4412,7 +4387,6 @@ public class ServerHttpSecurity {
|
||||
AuthenticationWebFilter oauth2 = new AuthenticationWebFilter(authenticationManager);
|
||||
oauth2.setServerAuthenticationConverter(OAuth2ResourceServerSpec.this.bearerTokenConverter);
|
||||
oauth2.setAuthenticationFailureHandler(authenticationFailureHandler());
|
||||
oauth2.setAuthenticationSuccessHandler(authenticationSuccessHandler());
|
||||
http.addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);
|
||||
}
|
||||
|
||||
@@ -4545,7 +4519,6 @@ public class ServerHttpSecurity {
|
||||
AuthenticationWebFilter oauth2 = new AuthenticationWebFilter(authenticationManager);
|
||||
oauth2.setServerAuthenticationConverter(OAuth2ResourceServerSpec.this.bearerTokenConverter);
|
||||
oauth2.setAuthenticationFailureHandler(authenticationFailureHandler());
|
||||
oauth2.setAuthenticationSuccessHandler(authenticationSuccessHandler());
|
||||
http.addFilterAt(oauth2, SecurityWebFiltersOrder.AUTHENTICATION);
|
||||
}
|
||||
|
||||
|
||||
-5
@@ -20,7 +20,6 @@ import org.springframework.security.authentication.ReactiveAuthenticationManager
|
||||
import org.springframework.security.web.server.ServerAuthenticationEntryPoint
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler
|
||||
import org.springframework.security.web.server.authorization.ServerAccessDeniedHandler
|
||||
import org.springframework.web.server.ServerWebExchange
|
||||
|
||||
@@ -36,8 +35,6 @@ import org.springframework.web.server.ServerWebExchange
|
||||
* @property bearerTokenConverter the [ServerAuthenticationConverter] to use for requests authenticating with
|
||||
* Bearer Tokens.
|
||||
* @property authenticationManagerResolver the [ReactiveAuthenticationManagerResolver] to use.
|
||||
* @property authenticationSuccessHandler the [ServerAuthenticationSuccessHandler] to use after
|
||||
* authentication success.
|
||||
*/
|
||||
@ServerSecurityMarker
|
||||
class ServerOAuth2ResourceServerDsl {
|
||||
@@ -46,7 +43,6 @@ class ServerOAuth2ResourceServerDsl {
|
||||
var authenticationEntryPoint: ServerAuthenticationEntryPoint? = null
|
||||
var bearerTokenConverter: ServerAuthenticationConverter? = null
|
||||
var authenticationManagerResolver: ReactiveAuthenticationManagerResolver<ServerWebExchange>? = null
|
||||
var authenticationSuccessHandler: ServerAuthenticationSuccessHandler? = null
|
||||
|
||||
private var jwt: ((ServerHttpSecurity.OAuth2ResourceServerSpec.JwtSpec) -> Unit)? = null
|
||||
private var opaqueToken: ((ServerHttpSecurity.OAuth2ResourceServerSpec.OpaqueTokenSpec) -> Unit)? = null
|
||||
@@ -119,7 +115,6 @@ class ServerOAuth2ResourceServerDsl {
|
||||
authenticationEntryPoint?.also { oauth2ResourceServer.authenticationEntryPoint(authenticationEntryPoint) }
|
||||
bearerTokenConverter?.also { oauth2ResourceServer.bearerTokenConverter(bearerTokenConverter) }
|
||||
authenticationManagerResolver?.also { oauth2ResourceServer.authenticationManagerResolver(authenticationManagerResolver!!) }
|
||||
authenticationSuccessHandler?.also { oauth2ResourceServer.authenticationSuccessHandler(authenticationSuccessHandler) }
|
||||
jwt?.also { oauth2ResourceServer.jwt(jwt) }
|
||||
opaqueToken?.also { oauth2ResourceServer.opaqueToken(opaqueToken) }
|
||||
}
|
||||
|
||||
+3
-3
@@ -25,12 +25,12 @@ public class MockSecurityContextHolderStrategy implements SecurityContextHolderS
|
||||
private SecurityContext context;
|
||||
|
||||
@Override
|
||||
public synchronized void clearContext() {
|
||||
public void clearContext() {
|
||||
this.context = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized SecurityContext getContext() {
|
||||
public SecurityContext getContext() {
|
||||
if (this.context == null) {
|
||||
this.context = createEmptyContext();
|
||||
}
|
||||
@@ -38,7 +38,7 @@ public class MockSecurityContextHolderStrategy implements SecurityContextHolderS
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setContext(SecurityContext context) {
|
||||
public void setContext(SecurityContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
|
||||
-180
@@ -73,11 +73,9 @@ import org.springframework.security.oauth2.server.resource.authentication.Reacti
|
||||
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
|
||||
import org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenAuthenticationConverter;
|
||||
import org.springframework.security.web.server.SecurityWebFilterChain;
|
||||
import org.springframework.security.web.server.WebFilterExchange;
|
||||
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler;
|
||||
import org.springframework.test.web.reactive.server.WebTestClient;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
@@ -373,79 +371,6 @@ public class OAuth2ResourceServerSpecTests {
|
||||
verify(handler).onAuthenticationFailure(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenUsingCustomAuthenticationSuccessHandlerThenUsesIsAccordingly() {
|
||||
this.spring.register(CustomAuthenticationSuccessHandlerAuthenticationManagerResolverConfig.class).autowire();
|
||||
ServerAuthenticationSuccessHandler handler = this.spring.getContext()
|
||||
.getBean(ServerAuthenticationSuccessHandler.class);
|
||||
ReactiveAuthenticationManager authenticationManager = this.spring.getContext()
|
||||
.getBean(ReactiveAuthenticationManager.class);
|
||||
given(authenticationManager.authenticate(any()))
|
||||
.willAnswer((input) -> Mono.just(input.getArgument(0, Authentication.class)));
|
||||
given(handler.onAuthenticationSuccess(any(), any())).willAnswer((input) -> {
|
||||
WebFilterExchange webFilterExchange = input.getArgument(0, WebFilterExchange.class);
|
||||
return webFilterExchange.getChain().filter(webFilterExchange.getExchange());
|
||||
});
|
||||
// @formatter:off
|
||||
this.client.get()
|
||||
.headers((headers) -> headers.setBearerAuth(this.messageReadToken))
|
||||
.exchange()
|
||||
.expectStatus().isUnauthorized();
|
||||
// @formatter:on
|
||||
verify(handler).onAuthenticationSuccess(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenUsingCustomAuthenticationSuccessHandlerWithJwtThenUsesIsAccordingly() {
|
||||
this.spring.register(CustomAuthenticationSuccessHandlerJwtConfig.class).autowire();
|
||||
ServerAuthenticationSuccessHandler handler = this.spring.getContext()
|
||||
.getBean(ServerAuthenticationSuccessHandler.class);
|
||||
ReactiveAuthenticationManager authenticationManager = this.spring.getContext()
|
||||
.getBean(ReactiveAuthenticationManager.class);
|
||||
given(authenticationManager.authenticate(any()))
|
||||
.willAnswer((input) -> Mono.just(input.getArgument(0, Authentication.class)));
|
||||
given(handler.onAuthenticationSuccess(any(), any())).willAnswer((input) -> {
|
||||
WebFilterExchange webFilterExchange = input.getArgument(0, WebFilterExchange.class);
|
||||
return webFilterExchange.getChain().filter(webFilterExchange.getExchange());
|
||||
});
|
||||
// @formatter:off
|
||||
this.client.get()
|
||||
.headers((headers) -> headers.setBearerAuth(this.messageReadToken))
|
||||
.exchange()
|
||||
.expectStatus().isUnauthorized();
|
||||
// @formatter:on
|
||||
verify(handler).onAuthenticationSuccess(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenUsingCustomAuthenticationSuccessHandlerWIthOpaqueTokenThenUsesIsAccordingly() {
|
||||
this.spring.register(CustomAuthenticationSuccessHandlerOpaqueTokenConfig.class, RootController.class)
|
||||
.autowire();
|
||||
this.spring.getContext()
|
||||
.getBean(MockWebServer.class)
|
||||
.setDispatcher(requiresAuth(this.clientId, this.clientSecret, this.active));
|
||||
ServerAuthenticationSuccessHandler handler = this.spring.getContext()
|
||||
.getBean(ServerAuthenticationSuccessHandler.class);
|
||||
ReactiveAuthenticationManager authenticationManager = this.spring.getContext()
|
||||
.getBean(ReactiveAuthenticationManager.class);
|
||||
given(authenticationManager.authenticate(any()))
|
||||
.willAnswer((input) -> Mono.just(input.getArgument(0, Authentication.class)));
|
||||
given(handler.onAuthenticationSuccess(any(), any())).willAnswer((input) -> {
|
||||
WebFilterExchange webFilterExchange = input.getArgument(0, WebFilterExchange.class);
|
||||
return webFilterExchange.getChain().filter(webFilterExchange.getExchange());
|
||||
});
|
||||
// @formatter:off
|
||||
this.client.get()
|
||||
.headers((headers) -> headers
|
||||
.setBearerAuth(this.messageReadToken)
|
||||
)
|
||||
.exchange()
|
||||
.expectStatus().isOk();
|
||||
// @formatter:on
|
||||
|
||||
verify(handler).onAuthenticationSuccess(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWhenSignedThenReturnsOk() {
|
||||
this.spring.register(PublicKeyConfig.class, RootController.class).autowire();
|
||||
@@ -1025,111 +950,6 @@ public class OAuth2ResourceServerSpecTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebFlux
|
||||
@EnableWebFluxSecurity
|
||||
static class CustomAuthenticationSuccessHandlerAuthenticationManagerResolverConfig {
|
||||
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())
|
||||
.oauth2ResourceServer((oauth2) -> oauth2
|
||||
.authenticationSuccessHandler(authenticationSuccessHandler())
|
||||
.authenticationManagerResolver((exchange) -> Mono.just(authenticationManager()))
|
||||
);
|
||||
// @formatter:on
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReactiveAuthenticationManager authenticationManager() {
|
||||
return mock(ReactiveAuthenticationManager.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
ServerAuthenticationSuccessHandler authenticationSuccessHandler() {
|
||||
return mock(ServerAuthenticationSuccessHandler.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebFlux
|
||||
@EnableWebFluxSecurity
|
||||
static class CustomAuthenticationSuccessHandlerJwtConfig {
|
||||
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())
|
||||
.oauth2ResourceServer((oauth2) -> oauth2
|
||||
.authenticationSuccessHandler(authenticationSuccessHandler())
|
||||
.jwt((jwt) -> jwt.authenticationManager(authenticationManager()))
|
||||
);
|
||||
// @formatter:on
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReactiveAuthenticationManager authenticationManager() {
|
||||
return mock(ReactiveAuthenticationManager.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
ServerAuthenticationSuccessHandler authenticationSuccessHandler() {
|
||||
return mock(ServerAuthenticationSuccessHandler.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebFlux
|
||||
@EnableWebFluxSecurity
|
||||
static class CustomAuthenticationSuccessHandlerOpaqueTokenConfig {
|
||||
|
||||
private MockWebServer mockWebServer = new MockWebServer();
|
||||
|
||||
@Bean
|
||||
SecurityWebFilterChain springSecurity(ServerHttpSecurity http) {
|
||||
String introspectionUri = mockWebServer().url("/introspect").toString();
|
||||
// @formatter:off
|
||||
http
|
||||
.authorizeExchange((authorize) -> authorize.anyExchange().authenticated())
|
||||
.oauth2ResourceServer((oauth2) -> oauth2
|
||||
.authenticationSuccessHandler(authenticationSuccessHandler())
|
||||
.opaqueToken((opaqueToken) -> opaqueToken
|
||||
.introspectionUri(introspectionUri)
|
||||
.introspectionClientCredentials("client", "secret"))
|
||||
);
|
||||
// @formatter:on
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReactiveAuthenticationManager authenticationManager() {
|
||||
return mock(ReactiveAuthenticationManager.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
ServerAuthenticationSuccessHandler authenticationSuccessHandler() {
|
||||
return mock(ServerAuthenticationSuccessHandler.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
MockWebServer mockWebServer() {
|
||||
return this.mockWebServer;
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
void shutdown() throws IOException {
|
||||
this.mockWebServer.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EnableWebFlux
|
||||
@EnableWebFluxSecurity
|
||||
static class CustomBearerTokenServerAuthenticationConverter {
|
||||
|
||||
-42
@@ -17,7 +17,6 @@
|
||||
package org.springframework.security.config.web.server
|
||||
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.verify
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -38,7 +37,6 @@ import org.springframework.security.web.server.SecurityWebFilterChain
|
||||
import org.springframework.security.web.server.WebFilterExchange
|
||||
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler
|
||||
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler
|
||||
import org.springframework.security.web.server.authorization.HttpStatusServerAccessDeniedHandler
|
||||
import org.springframework.test.web.reactive.server.WebTestClient
|
||||
import org.springframework.web.reactive.config.EnableWebFlux
|
||||
@@ -185,46 +183,6 @@ class ServerOAuth2ResourceServerDslTests {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `request when custom authentication success handler then success handler used`() {
|
||||
this.spring.register(AuthenticationSuccessHandlerConfig::class.java).autowire()
|
||||
every {
|
||||
AuthenticationSuccessHandlerConfig.SUCCESS_HANDLER.onAuthenticationSuccess(any(), any())
|
||||
} returns Mono.empty()
|
||||
|
||||
this.client.get()
|
||||
.uri("/")
|
||||
.headers { it.setBearerAuth(validJwt) }
|
||||
.exchange()
|
||||
|
||||
verify(exactly = 1) { AuthenticationSuccessHandlerConfig.SUCCESS_HANDLER.onAuthenticationSuccess(any(), any()) }
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebFluxSecurity
|
||||
@EnableWebFlux
|
||||
open class AuthenticationSuccessHandlerConfig {
|
||||
|
||||
companion object {
|
||||
val SUCCESS_HANDLER: ServerAuthenticationSuccessHandler = mockk()
|
||||
}
|
||||
|
||||
@Bean
|
||||
open fun springWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
|
||||
return http {
|
||||
authorizeExchange {
|
||||
authorize(anyExchange, authenticated)
|
||||
}
|
||||
oauth2ResourceServer {
|
||||
authenticationSuccessHandler = SUCCESS_HANDLER
|
||||
jwt {
|
||||
publicKey = publicKey()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `request when custom bearer token converter configured then custom converter used`() {
|
||||
this.spring.register(BearerTokenConverterConfig::class.java).autowire()
|
||||
|
||||
+1
-4
@@ -32,7 +32,6 @@ import org.springframework.util.Assert;
|
||||
* {@link java.util.ServiceLoader} mechanism when context-propagation is on the classpath.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @author Tadaya Tsuyukubo
|
||||
* @since 6.5
|
||||
* @see io.micrometer.context.ContextRegistry
|
||||
*/
|
||||
@@ -54,9 +53,7 @@ public final class SecurityContextHolderThreadLocalAccessor implements ThreadLoc
|
||||
@Override
|
||||
public void setValue(SecurityContext securityContext) {
|
||||
Assert.notNull(securityContext, "securityContext cannot be null");
|
||||
SecurityContext newContext = SecurityContextHolder.createEmptyContext();
|
||||
newContext.setAuthentication(securityContext.getAuthentication());
|
||||
SecurityContextHolder.setContext(newContext);
|
||||
SecurityContextHolder.setContext(securityContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-3
@@ -259,9 +259,7 @@ public final class InetAddressMatchers {
|
||||
if (address.isLoopbackAddress() || address.isLinkLocalAddress() || address.isSiteLocalAddress()) {
|
||||
return true;
|
||||
}
|
||||
if (address.isAnyLocalAddress()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
byte[] rawAddress = address.getAddress();
|
||||
|
||||
if (rawAddress.length == 16) {
|
||||
|
||||
+2
-49
@@ -16,18 +16,11 @@
|
||||
|
||||
package org.springframework.security.core.context;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.task.support.ContextPropagatingTaskDecorator;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
@@ -36,7 +29,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
||||
* Tests for {@link SecurityContextHolderThreadLocalAccessor}.
|
||||
*
|
||||
* @author Steve Riesenberg
|
||||
* @author Tadaya Tsuyukubo
|
||||
*/
|
||||
public class SecurityContextHolderThreadLocalAccessorTests {
|
||||
|
||||
@@ -73,11 +65,9 @@ public class SecurityContextHolderThreadLocalAccessorTests {
|
||||
@Test
|
||||
public void setValueWhenSecurityContextThenSetsSecurityContextHolder() {
|
||||
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
|
||||
Authentication authentication = new TestingAuthenticationToken("user", "password");
|
||||
securityContext.setAuthentication(authentication);
|
||||
securityContext.setAuthentication(new TestingAuthenticationToken("user", "password"));
|
||||
this.threadLocalAccessor.setValue(securityContext);
|
||||
assertThat(SecurityContextHolder.getContext()).isNotSameAs(securityContext);
|
||||
assertThat(SecurityContextHolder.getContext().getAuthentication()).isSameAs(authentication);
|
||||
assertThat(SecurityContextHolder.getContext()).isSameAs(securityContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -100,41 +90,4 @@ public class SecurityContextHolderThreadLocalAccessorTests {
|
||||
assertThat(SecurityContextHolder.getContext()).isEqualTo(emptyContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newSecurityContextInDifferentThread() throws Exception {
|
||||
Authentication authA = new TestingAuthenticationToken("foo", "password");
|
||||
Authentication authB = new TestingAuthenticationToken("bar", "password");
|
||||
|
||||
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
|
||||
securityContext.setAuthentication(authA);
|
||||
SecurityContextHolder.setContext(securityContext);
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicReference<SecurityContext> contextHolder = new AtomicReference<>();
|
||||
AtomicReference<Authentication> authHolder = new AtomicReference<>();
|
||||
Runnable runnable = () -> {
|
||||
SecurityContext context = SecurityContextHolder.getContext();
|
||||
contextHolder.set(context);
|
||||
authHolder.set(context.getAuthentication());
|
||||
context.setAuthentication(authB);
|
||||
latch.countDown();
|
||||
};
|
||||
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setTaskDecorator(new ContextPropagatingTaskDecorator());
|
||||
executor.afterPropertiesSet();
|
||||
|
||||
executor.execute(runnable);
|
||||
|
||||
boolean finished = latch.await(10, TimeUnit.SECONDS);
|
||||
assertThat(finished).isTrue();
|
||||
|
||||
assertThat(contextHolder.get()).isNotSameAs(securityContext);
|
||||
assertThat(authHolder.get()).isSameAs(authA);
|
||||
|
||||
SecurityContext current = SecurityContextHolder.getContext();
|
||||
assertThat(current).isSameAs(securityContext);
|
||||
assertThat(current.getAuthentication()).isSameAs(authA);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-5
@@ -276,17 +276,16 @@ class InetAddressMatchersTests {
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "127.0.0.1", "127.0.0.255", "0.0.0.0" })
|
||||
@ValueSource(strings = { "127.0.0.1", "127.0.0.255" })
|
||||
void matchesWhenIpv4LoopbackThenReturnsTrue(String address) throws Exception {
|
||||
InetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();
|
||||
assertThat(matcher.matches(InetAddress.getByName(address))).isTrue();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "::1", "::" })
|
||||
void matchesWhenIpv6LoopbackThenReturnsTrue(String address) throws Exception {
|
||||
@Test
|
||||
void matchesWhenIpv6LoopbackThenReturnsTrue() throws Exception {
|
||||
InetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();
|
||||
assertThat(matcher.matches(InetAddress.getByName(address))).isTrue();
|
||||
assertThat(matcher.matches(InetAddress.getByName("::1"))).isTrue();
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
|
||||
@@ -8,8 +8,3 @@
|
||||
== Web
|
||||
|
||||
* Since Spring Framework's `HttpMethod#valueOf` now normalizes casing, `StrictServerWebExchangeFirewall` no longer detects a non-canonical-case HTTP method (for example, `get` instead of `GET`) as a distinct value; such requests are processed as the canonical method instead of being rejected. Applications with a customized `ServerExchangeRejectedHandler` should be aware it is no longer invoked for this case.
|
||||
|
||||
== OAuth 2.0
|
||||
|
||||
* https://github.com/spring-projects/spring-security/pull/18895[gh-18895] - Add `authenticationSuccessHandler` to the Reactive Resource Server DSL
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
springBootVersion=4.2.0-SNAPSHOT
|
||||
version=7.2.0-SNAPSHOT
|
||||
version=7.2.0-M1
|
||||
samplesBranch=main
|
||||
org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError
|
||||
org.gradle.parallel=true
|
||||
|
||||
@@ -18,8 +18,8 @@ org-springframework = "7.1.0-M1"
|
||||
com-password4j = "1.8.4"
|
||||
|
||||
[libraries]
|
||||
ch-qos-logback-logback-classic = "ch.qos.logback:logback-classic:1.6.3"
|
||||
com-fasterxml-jackson-jackson-bom = "com.fasterxml.jackson:jackson-bom:2.22.2"
|
||||
ch-qos-logback-logback-classic = "ch.qos.logback:logback-classic:1.6.1"
|
||||
com-fasterxml-jackson-jackson-bom = "com.fasterxml.jackson:jackson-bom:2.22.1"
|
||||
com-google-inject-guice = "com.google.inject:guice:3.0"
|
||||
com-netflix-nebula-nebula-project-plugin = "com.netflix.nebula:nebula-project-plugin:8.2.0"
|
||||
com-nimbusds-nimbus-jose-jwt = "com.nimbusds:nimbus-jose-jwt:10.9.1"
|
||||
@@ -39,7 +39,7 @@ io-spring-javaformat-spring-javaformat-checkstyle = { module = "io.spring.javafo
|
||||
io-spring-javaformat-spring-javaformat-gradle-plugin = { module = "io.spring.javaformat:spring-javaformat-gradle-plugin", version.ref = "io-spring-javaformat" }
|
||||
io-spring-nohttp-nohttp-checkstyle = { module = "io.spring.nohttp:nohttp-checkstyle", version.ref = "io-spring-nohttp" }
|
||||
io-spring-nohttp-nohttp-gradle = { module = "io.spring.nohttp:nohttp-gradle", version.ref = "io-spring-nohttp" }
|
||||
io-spring-security-release-plugin = "io.spring.gradle:spring-security-release-plugin:1.0.17"
|
||||
io-spring-security-release-plugin = "io.spring.gradle:spring-security-release-plugin:1.0.15"
|
||||
jakarta-annotation-jakarta-annotation-api = "jakarta.annotation:jakarta.annotation-api:3.0.0"
|
||||
jakarta-inject-jakarta-inject-api = "jakarta.inject:jakarta.inject-api:2.0.1"
|
||||
jakarta-persistence-jakarta-persistence-api = "jakarta.persistence:jakarta.persistence-api:3.2.0"
|
||||
@@ -86,10 +86,10 @@ org-skyscreamer-jsonassert = "org.skyscreamer:jsonassert:1.5.3"
|
||||
org-slf4j-log4j-over-slf4j = "org.slf4j:log4j-over-slf4j:1.7.36"
|
||||
org-slf4j-slf4j-api = "org.slf4j:slf4j-api:2.0.18"
|
||||
org-springframework-data-spring-data-bom = "org.springframework.data:spring-data-bom:2026.1.0-M1"
|
||||
org-springframework-ldap-spring-ldap-core = "org.springframework.ldap:spring-ldap-core:4.1.1"
|
||||
org-springframework-ldap-spring-ldap-core = "org.springframework.ldap:spring-ldap-core:4.1.0"
|
||||
org-springframework-spring-framework-bom = { module = "org.springframework:spring-framework-bom", version.ref = "org-springframework" }
|
||||
org-synchronoss-cloud-nio-multipart-parser = "org.synchronoss.cloud:nio-multipart-parser:1.1.0"
|
||||
tools-jackson-jackson-bom = "tools.jackson:jackson-bom:3.2.2"
|
||||
tools-jackson-jackson-bom = "tools.jackson:jackson-bom:3.2.1"
|
||||
|
||||
com-google-code-gson-gson = "com.google.code.gson:gson:2.14.0"
|
||||
com-thaiopensource-trag = "com.thaiopensource:trang:20091111"
|
||||
|
||||
+3
-29
@@ -24,12 +24,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
@@ -82,7 +79,7 @@ public final class RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler
|
||||
.map((c) -> c.get(ServerWebExchange.class));
|
||||
// @formatter:on
|
||||
|
||||
private ServerSecurityContextRepository serverSecurityContextRepository = new NonRotatingWebSessionServerSecurityContextRepository();
|
||||
private ServerSecurityContextRepository serverSecurityContextRepository = new WebSessionServerSecurityContextRepository();
|
||||
|
||||
private ReactiveJwtDecoderFactory<ClientRegistration> jwtDecoderFactory = new ReactiveOidcIdTokenDecoderFactory();
|
||||
|
||||
@@ -144,7 +141,8 @@ public final class RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler
|
||||
|
||||
/**
|
||||
* Sets a {@link ServerSecurityContextRepository} to use for refreshing a
|
||||
* {@link SecurityContext}.
|
||||
* {@link SecurityContext}, defaults to
|
||||
* {@link WebSessionServerSecurityContextRepository}.
|
||||
* @param serverSecurityContextRepository the {@link ServerSecurityContextRepository}
|
||||
* to use
|
||||
*/
|
||||
@@ -318,28 +316,4 @@ public final class RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler
|
||||
return this.serverSecurityContextRepository.save(exchange, securityContext);
|
||||
}
|
||||
|
||||
private static final class NonRotatingWebSessionServerSecurityContextRepository
|
||||
implements ServerSecurityContextRepository {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(NonRotatingWebSessionServerSecurityContextRepository.class);
|
||||
|
||||
@Override
|
||||
public Mono<SecurityContext> load(ServerWebExchange exchange) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> save(ServerWebExchange exchange, @Nullable SecurityContext context) {
|
||||
Assert.notNull(context, "context cannot be null");
|
||||
// Save SecurityContext in WebSession without rotating session id.
|
||||
return exchange.getSession().doOnNext((session) -> {
|
||||
session.getAttributes()
|
||||
.put(WebSessionServerSecurityContextRepository.DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME, context);
|
||||
logger.debug(LogMessage.format("Saved SecurityContext '%s' in WebSession: '%s'", context, session));
|
||||
}).then();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+8
-2
@@ -511,6 +511,9 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
||||
}
|
||||
HttpServletRequest servletRequest = getRequest(attrs);
|
||||
HttpServletResponse servletResponse = getResponse(attrs);
|
||||
if (servletRequest == null || servletResponse == null) {
|
||||
return Mono.empty();
|
||||
}
|
||||
OAuth2AuthorizeRequest.Builder builder = OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId)
|
||||
.principal(authentication);
|
||||
builder.attributes((attributes) -> addToAttributes(attributes, servletRequest, servletResponse));
|
||||
@@ -535,6 +538,9 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
||||
}
|
||||
HttpServletRequest servletRequest = getRequest(attrs);
|
||||
HttpServletResponse servletResponse = getResponse(attrs);
|
||||
if (servletRequest == null || servletResponse == null) {
|
||||
return Mono.just(authorizedClient);
|
||||
}
|
||||
OAuth2AuthorizeRequest.Builder builder = OAuth2AuthorizeRequest.withAuthorizedClient(authorizedClient)
|
||||
.principal(authentication);
|
||||
builder.attributes((attributes) -> addToAttributes(attributes, servletRequest, servletResponse));
|
||||
@@ -546,8 +552,8 @@ public final class ServletOAuth2AuthorizedClientExchangeFilterFunction implement
|
||||
.subscribeOn(Schedulers.boundedElastic());
|
||||
}
|
||||
|
||||
private void addToAttributes(Map<String, Object> attributes, @Nullable HttpServletRequest servletRequest,
|
||||
@Nullable HttpServletResponse servletResponse) {
|
||||
private void addToAttributes(Map<String, Object> attributes, HttpServletRequest servletRequest,
|
||||
HttpServletResponse servletResponse) {
|
||||
if (servletRequest != null) {
|
||||
attributes.put(HTTP_SERVLET_REQUEST_ATTR_NAME, servletRequest);
|
||||
}
|
||||
|
||||
-47
@@ -50,9 +50,7 @@ import org.springframework.security.oauth2.jwt.ReactiveJwtDecoder;
|
||||
import org.springframework.security.oauth2.jwt.ReactiveJwtDecoderFactory;
|
||||
import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebSession;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
@@ -142,51 +140,6 @@ class RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandlerTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
// gh-19424
|
||||
@Test
|
||||
void onAuthorizationSuccessWhenDefaultServerSecurityContextRepositoryThenWebSessionIdNotChanged() {
|
||||
ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();
|
||||
Instant authTime = Instant.now();
|
||||
DefaultOidcUser principal = createOidcUser(authTime);
|
||||
OAuth2AuthenticationToken authenticationToken = new OAuth2AuthenticationToken(principal,
|
||||
principal.getAuthorities(), clientRegistration.getRegistrationId());
|
||||
OAuth2AccessToken accessToken = createAccessToken();
|
||||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(clientRegistration, principal.getName(),
|
||||
accessToken, null);
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/").build());
|
||||
Map<String, Object> attributes = Map.of(ServerWebExchange.class.getName(), exchange,
|
||||
OidcParameterNames.ID_TOKEN, "id-token-1234");
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
claims.put("iss", principal.getIssuer());
|
||||
claims.put("sub", principal.getSubject());
|
||||
claims.put("aud", principal.getAudience());
|
||||
claims.put("auth_time", authTime);
|
||||
claims.put("nonce", principal.getNonce());
|
||||
Jwt jwt = mock(Jwt.class);
|
||||
given(jwt.getTokenValue()).willReturn("id-token-1234");
|
||||
given(jwt.getIssuedAt()).willReturn(principal.getIssuedAt());
|
||||
given(jwt.getClaims()).willReturn(claims);
|
||||
ReactiveJwtDecoder jwtDecoder = mock(ReactiveJwtDecoder.class);
|
||||
given(jwtDecoder.decode(any())).willReturn(Mono.just(jwt));
|
||||
ReactiveJwtDecoderFactory<ClientRegistration> reactiveJwtDecoderFactory = mock(ReactiveJwtDecoderFactory.class);
|
||||
given(reactiveJwtDecoderFactory.createDecoder(any())).willReturn(jwtDecoder);
|
||||
ReactiveOAuth2UserService<OidcUserRequest, OidcUser> userService = mock(ReactiveOAuth2UserService.class);
|
||||
given(userService.loadUser(any())).willReturn(Mono.just(principal));
|
||||
RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler handler = new RefreshOidcUserReactiveOAuth2AuthorizationSuccessHandler();
|
||||
handler.setJwtDecoderFactory(reactiveJwtDecoderFactory);
|
||||
handler.setUserService(userService);
|
||||
String originalSessionId = exchange.getSession().map(WebSession::getId).block();
|
||||
StepVerifier.create(handler.onAuthorizationSuccess(authorizedClient, authenticationToken, attributes))
|
||||
.verifyComplete();
|
||||
StepVerifier.create(exchange.getSession())
|
||||
.assertNext((session) -> assertThat(session.getId()).isEqualTo(originalSessionId))
|
||||
.verifyComplete();
|
||||
WebSessionServerSecurityContextRepository securityContextRepository = new WebSessionServerSecurityContextRepository();
|
||||
StepVerifier.create(securityContextRepository.load(exchange).mapNotNull(SecurityContext::getAuthentication))
|
||||
.expectNext(authenticationToken)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void onAuthorizationSuccessWhenIdTokenIssuerNotSameThenException() {
|
||||
ClientRegistration clientRegistration = TestClientRegistrations.clientRegistration().build();
|
||||
|
||||
-8
@@ -35,7 +35,6 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
import reactor.util.context.Context;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -78,7 +77,6 @@ import org.springframework.web.reactive.function.client.ExchangeFunction;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.reactive.function.client.WebClientResponseException;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebSession;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
@@ -322,8 +320,6 @@ public class ServerOAuth2AuthorizedClientExchangeFilterFunctionITests {
|
||||
doReturn(Mono.just(authorizedClient)).when(this.authorizedClientRepository)
|
||||
.loadAuthorizedClient(eq(clientRegistration.getRegistrationId()), eq(this.authentication),
|
||||
eq(this.exchange));
|
||||
// Capture the original session id.
|
||||
String originalSessionId = this.exchange.getSession().map(WebSession::getId).block();
|
||||
this.webClient.get()
|
||||
.uri(this.serverUrl)
|
||||
.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction
|
||||
@@ -360,10 +356,6 @@ public class ServerOAuth2AuthorizedClientExchangeFilterFunctionITests {
|
||||
assertThat(oidcUser.getSubject()).isEqualTo("subject-1234");
|
||||
assertThat(oidcUser.getName()).isEqualTo("refreshed-username");
|
||||
});
|
||||
// Verify that session id was not changed.
|
||||
StepVerifier.create(this.exchange.getSession())
|
||||
.assertNext((session) -> assertThat(session.getId()).isEqualTo(originalSessionId))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
-63
@@ -40,10 +40,8 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistration;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.registration.TestClientRegistrations;
|
||||
@@ -168,67 +166,6 @@ public class ServletOAuth2AuthorizedClientExchangeFilterFunctionITests {
|
||||
assertThat(authorizedClientCaptor.getValue().getClientRegistration()).isSameAs(clientRegistration);
|
||||
}
|
||||
|
||||
// gh-19421
|
||||
@Test
|
||||
public void requestWhenNoServletRequestThenAuthorizeAndSendRequest() {
|
||||
RequestContextHolder.resetRequestAttributes();
|
||||
final OAuth2AuthorizedClientService delegate = new InMemoryOAuth2AuthorizedClientService(
|
||||
this.clientRegistrationRepository);
|
||||
OAuth2AuthorizedClientService authorizedClientService = spy(new OAuth2AuthorizedClientService() {
|
||||
@Override
|
||||
public <T extends OAuth2AuthorizedClient> T loadAuthorizedClient(String clientRegistrationId,
|
||||
String principal) {
|
||||
return delegate.loadAuthorizedClient(clientRegistrationId, principal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveAuthorizedClient(OAuth2AuthorizedClient authorizedClient, Authentication principal) {
|
||||
delegate.saveAuthorizedClient(authorizedClient, principal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAuthorizedClient(String clientRegistrationId, String principal) {
|
||||
delegate.removeAuthorizedClient(clientRegistrationId, principal);
|
||||
}
|
||||
});
|
||||
this.authorizedClientFilter = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
|
||||
new AuthorizedClientServiceOAuth2AuthorizedClientManager(this.clientRegistrationRepository,
|
||||
authorizedClientService));
|
||||
this.webClient = WebClient.builder().apply(this.authorizedClientFilter.oauth2Configuration()).build();
|
||||
|
||||
// @formatter:off
|
||||
String accessTokenResponse = "{\n"
|
||||
+ " \"access_token\": \"access-token-1234\",\n"
|
||||
+ " \"token_type\": \"bearer\",\n"
|
||||
+ " \"expires_in\": \"3600\",\n"
|
||||
+ " \"scope\": \"read write\"\n"
|
||||
+ "}\n";
|
||||
String clientResponse = "{\n"
|
||||
+ " \"attribute1\": \"value1\",\n"
|
||||
+ " \"attribute2\": \"value2\"\n"
|
||||
+ "}\n";
|
||||
// @formatter:on
|
||||
this.server.enqueue(jsonResponse(accessTokenResponse));
|
||||
this.server.enqueue(jsonResponse(clientResponse));
|
||||
ClientRegistration clientRegistration = TestClientRegistrations.clientCredentials()
|
||||
.tokenUri(this.serverUrl)
|
||||
.build();
|
||||
given(this.clientRegistrationRepository.findByRegistrationId(eq(clientRegistration.getRegistrationId())))
|
||||
.willReturn(clientRegistration);
|
||||
this.webClient.get()
|
||||
.uri(this.serverUrl)
|
||||
.attributes(ServletOAuth2AuthorizedClientExchangeFilterFunction
|
||||
.clientRegistrationId(clientRegistration.getRegistrationId()))
|
||||
.retrieve()
|
||||
.bodyToMono(String.class)
|
||||
.block();
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(2);
|
||||
ArgumentCaptor<OAuth2AuthorizedClient> authorizedClientCaptor = ArgumentCaptor
|
||||
.forClass(OAuth2AuthorizedClient.class);
|
||||
verify(authorizedClientService).saveAuthorizedClient(authorizedClientCaptor.capture(), eq(this.authentication));
|
||||
assertThat(authorizedClientCaptor.getValue().getClientRegistration()).isSameAs(clientRegistration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requestWhenAuthorizedButExpiredThenRefreshAndSendRequest() {
|
||||
// @formatter:off
|
||||
|
||||
-42
@@ -67,14 +67,12 @@ import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.ClientAuthorizationException;
|
||||
import org.springframework.security.oauth2.client.JwtBearerOAuth2AuthorizedClientProvider;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizationFailureHandler;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
|
||||
import org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider;
|
||||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
|
||||
import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;
|
||||
@@ -136,9 +134,6 @@ public class ServletOAuth2AuthorizedClientExchangeFilterFunctionTests {
|
||||
@Mock
|
||||
private OAuth2AuthorizedClientRepository authorizedClientRepository;
|
||||
|
||||
@Mock
|
||||
private OAuth2AuthorizedClientService authorizedClientService;
|
||||
|
||||
@Mock
|
||||
private ClientRegistrationRepository clientRegistrationRepository;
|
||||
|
||||
@@ -666,43 +661,6 @@ public class ServletOAuth2AuthorizedClientExchangeFilterFunctionTests {
|
||||
authentication, servletRequest);
|
||||
}
|
||||
|
||||
// gh-19421
|
||||
@Test
|
||||
public void filterWhenServletRequestNullAndClientRegistrationIdFromAuthenticationAndCustomPrincipalResolverThenAuthorizedClientResolved() {
|
||||
this.function = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
|
||||
new AuthorizedClientServiceOAuth2AuthorizedClientManager(this.clientRegistrationRepository,
|
||||
this.authorizedClientService));
|
||||
this.function.setDefaultOAuth2AuthorizedClient(true);
|
||||
OAuth2User user = mock(OAuth2User.class);
|
||||
List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_USER");
|
||||
OAuth2AuthenticationToken initialAuthentication = new OAuth2AuthenticationToken(user, authorities,
|
||||
"initial-registration-id");
|
||||
OAuth2AuthenticationToken authentication = new OAuth2AuthenticationToken(user, authorities,
|
||||
this.registration.getRegistrationId());
|
||||
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(this.registration, "principalName",
|
||||
this.accessToken);
|
||||
given(this.clientRegistrationRepository.findByRegistrationId(this.registration.getRegistrationId()))
|
||||
.willReturn(this.registration);
|
||||
given(this.authorizedClientService.loadAuthorizedClient(this.registration.getRegistrationId(),
|
||||
initialAuthentication.getName()))
|
||||
.willReturn(authorizedClient);
|
||||
final ClientRequest clientRequest = ClientRequest.create(HttpMethod.GET, URI.create("https://example.com"))
|
||||
.build();
|
||||
this.function.setPrincipalResolver((request) -> authentication);
|
||||
this.function.filter(clientRequest, this.exchange)
|
||||
.contextWrite(context(null, null, initialAuthentication))
|
||||
.block();
|
||||
List<ClientRequest> requests = this.exchange.getRequests();
|
||||
assertThat(requests).hasSize(1);
|
||||
ClientRequest request = requests.get(0);
|
||||
assertThat(request.headers().getFirst(HttpHeaders.AUTHORIZATION)).isEqualTo("Bearer token-0");
|
||||
assertThat(request.url().toASCIIString()).isEqualTo("https://example.com");
|
||||
assertThat(request.method()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(getBody(request)).isEmpty();
|
||||
verify(this.authorizedClientService).loadAuthorizedClient(this.registration.getRegistrationId(),
|
||||
authentication.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterWhenUnauthorizedThenInvokeFailureHandler() {
|
||||
assertHttpStatusInvokesFailureHandler(HttpStatus.UNAUTHORIZED, OAuth2ErrorCodes.INVALID_TOKEN);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
changelog:
|
||||
repository: spring-projects/spring-security
|
||||
repository: spring-projects/spring-security-commercial
|
||||
sections:
|
||||
- title: ":rewind: Breaking Changes"
|
||||
labels: ["type: breaks-passivity"]
|
||||
@@ -17,5 +17,6 @@ changelog:
|
||||
labels: ["type: task", "in: build"]
|
||||
sort: "title"
|
||||
issues:
|
||||
generate_links: false
|
||||
exclude:
|
||||
labels: ["status: duplicate"]
|
||||
|
||||
+2
-2
@@ -15,12 +15,12 @@ dependencyResolutionManagement {
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'spring-security'
|
||||
rootProject.name = 'spring-security-commercial'
|
||||
|
||||
FileTree buildFiles = fileTree(rootDir) {
|
||||
List excludes = gradle.startParameter.projectProperties.get("excludeProjects")?.split(",")
|
||||
include '**/*.gradle', '**/*.gradle.kts'
|
||||
exclude 'build', '**/gradle', 'settings.gradle', 'buildSrc', '/build.gradle', '.*', 'out'
|
||||
exclude 'build', '**/gradle', 'settings.gradle', 'commercial-settings.gradle', 'buildSrc', '/build.gradle', '.*', 'out'
|
||||
exclude '**/grails3'
|
||||
if(excludes) {
|
||||
exclude excludes
|
||||
|
||||
Reference in New Issue
Block a user