Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1a6915d3c0 | |||
| 8325728035 |
+59
-97
@@ -1,110 +1,72 @@
|
||||
version: 2
|
||||
|
||||
registries:
|
||||
spring-milestones:
|
||||
type: maven-repository
|
||||
url: https://repo.spring.io/milestone
|
||||
|
||||
updates:
|
||||
- package-ecosystem: gradle
|
||||
target-branch: 6.3.x
|
||||
directory: /
|
||||
|
||||
- package-ecosystem: "gradle"
|
||||
target-branch: "main"
|
||||
milestone: 319 # 6.2.x
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: '03:00'
|
||||
timezone: Etc/UTC
|
||||
labels:
|
||||
- 'type: dependency-upgrade'
|
||||
interval: "daily"
|
||||
time: "03:00"
|
||||
timezone: "Etc/UTC"
|
||||
labels: [ "type: dependency-upgrade" ]
|
||||
registries:
|
||||
- spring-milestones
|
||||
- "spring-milestones"
|
||||
ignore:
|
||||
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
||||
- dependency-name: org.python:jython
|
||||
- dependency-name: org.apache.directory.server:*
|
||||
- dependency-name: org.junit:junit-bom
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
- dependency-name: org.mockito:mockito-bom
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
- dependency-name: '*'
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
- version-update:semver-minor
|
||||
- package-ecosystem: gradle
|
||||
target-branch: main
|
||||
directory: /
|
||||
- dependency-name: "com.nimbusds:nimbus-jose-jwt" # nimbus-jose-jwt gets updated when oauth2-oidc-sdk is updated to ensure consistency
|
||||
- dependency-name: "org.python:jython" # jython updates break integration tests
|
||||
- dependency-name: "org.apache.directory.server:*" # ApacheDS version > 1.5.5 contains break changes
|
||||
- dependency-name: "org.junit:junit-bom"
|
||||
update-types: [ "version-update:semver-major" ]
|
||||
- dependency-name: "org.mockito:mockito-bom"
|
||||
update-types: [ "version-update:semver-major" ]
|
||||
- dependency-name: "*"
|
||||
update-types: [ "version-update:semver-major", "version-update:semver-minor" ]
|
||||
|
||||
- package-ecosystem: "gradle"
|
||||
target-branch: "6.1.x"
|
||||
milestone: 318 # 6.1.x
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: daily
|
||||
time: '03:00'
|
||||
timezone: Etc/UTC
|
||||
labels:
|
||||
- 'type: dependency-upgrade'
|
||||
registries:
|
||||
- spring-milestones
|
||||
interval: "daily"
|
||||
time: "03:00"
|
||||
timezone: "Etc/UTC"
|
||||
labels: [ "type: dependency-upgrade" ]
|
||||
ignore:
|
||||
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
||||
- dependency-name: org.python:jython
|
||||
- dependency-name: org.apache.directory.server:*
|
||||
- dependency-name: org.junit:junit-bom
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
- dependency-name: org.mockito:mockito-bom
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
- dependency-name: com.gradle.enterprise
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
- version-update:semver-minor
|
||||
- dependency-name: '*'
|
||||
update-types:
|
||||
- version-update:semver-major
|
||||
- dependency-name: "com.nimbusds:nimbus-jose-jwt" # nimbus-jose-jwt gets updated when oauth2-oidc-sdk is updated to ensure consistency
|
||||
- dependency-name: "org.python:jython" # jython updates break integration tests
|
||||
- dependency-name: "org.apache.directory.server:*" # ApacheDS version > 1.5.5 contains break changes
|
||||
- dependency-name: "org.junit:junit-bom"
|
||||
update-types: [ "version-update:semver-major" ]
|
||||
- dependency-name: "org.mockito:mockito-bom"
|
||||
update-types: [ "version-update:semver-major" ]
|
||||
- dependency-name: "*"
|
||||
update-types: [ "version-update:semver-major", "version-update:semver-minor" ]
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
target-branch: 6.3.x
|
||||
directory: /
|
||||
- package-ecosystem: "gradle"
|
||||
target-branch: "5.8.x"
|
||||
milestone: 246 # 5.8.x
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: weekly
|
||||
labels:
|
||||
- 'type: task'
|
||||
- 'in: build'
|
||||
- package-ecosystem: github-actions
|
||||
target-branch: main
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
labels:
|
||||
- 'type: task'
|
||||
- 'in: build'
|
||||
- package-ecosystem: github-actions
|
||||
target-branch: docs-build
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
labels:
|
||||
- 'type: task'
|
||||
- 'in: build'
|
||||
|
||||
- package-ecosystem: npm
|
||||
target-branch: docs-build
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
labels:
|
||||
- 'type: task'
|
||||
- 'in: build'
|
||||
|
||||
- package-ecosystem: npm
|
||||
target-branch: main
|
||||
directory: /docs
|
||||
schedule:
|
||||
interval: weekly
|
||||
labels:
|
||||
- 'type: task'
|
||||
- 'in: build'
|
||||
- package-ecosystem: npm
|
||||
target-branch: 6.3.x
|
||||
directory: /docs
|
||||
schedule:
|
||||
interval: weekly
|
||||
labels:
|
||||
- 'type: task'
|
||||
- 'in: build'
|
||||
interval: "daily"
|
||||
time: "03:00"
|
||||
timezone: "Etc/UTC"
|
||||
labels: [ "type: dependency-upgrade" ]
|
||||
ignore:
|
||||
- dependency-name: "com.nimbusds:nimbus-jose-jwt" # nimbus-jose-jwt gets updated when oauth2-oidc-sdk is updated to ensure consistency
|
||||
- dependency-name: "org.python:jython" # jython updates break integration tests
|
||||
- dependency-name: "org.apache.directory.server:*" # ApacheDS version > 1.5.5 contains break changes
|
||||
- dependency-name: "io.mockk:mockk" # mockk updates break tests
|
||||
- dependency-name: "org.opensaml:*" # org.opensaml maintains two different versions, so it must be updated manually
|
||||
- dependency-name: "org.junit:junit-bom"
|
||||
update-types: [ "version-update:semver-major" ]
|
||||
- dependency-name: "org.mockito:mockito-bom"
|
||||
update-types: [ "version-update:semver-major" ]
|
||||
- dependency-name: "*"
|
||||
update-types: [ "version-update:semver-major", "version-update:semver-minor" ]
|
||||
|
||||
@@ -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@1
|
||||
@@ -1,109 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- "dependabot/**"
|
||||
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:
|
||||
build:
|
||||
name: Build
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/build.yml@v1
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest ]
|
||||
jdk: [ 17 ]
|
||||
with:
|
||||
runs-on: ${{ matrix.os }}
|
||||
java-version: ${{ matrix.jdk }}
|
||||
distribution: temurin
|
||||
secrets: inherit
|
||||
test:
|
||||
name: Test Against Snapshots
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/test.yml@v1
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- java-version: 21-ea
|
||||
toolchain: 21
|
||||
- java-version: 17
|
||||
toolchain: 17
|
||||
with:
|
||||
java-version: ${{ matrix.java-version }}
|
||||
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=6.2.+ -PreactorVersion=2023.0.+ -PspringDataVersion=2024.0.+ --stacktrace
|
||||
secrets: inherit
|
||||
check-samples:
|
||||
name: Check Samples
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'spring-projects' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v2
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
- name: Check samples project
|
||||
env:
|
||||
LOCAL_REPOSITORY_PATH: ${{ github.workspace }}/build/publications/repos
|
||||
SAMPLES_DIR: ../spring-security-samples
|
||||
run: |
|
||||
# Extract version from gradle.properties
|
||||
version=$(cat gradle.properties | grep "version=" | awk -F'=' '{print $2}')
|
||||
# Extract samplesBranch from gradle.properties
|
||||
samples_branch=$(cat gradle.properties | grep "samplesBranch=" | awk -F'=' '{print $2}')
|
||||
./gradlew publishMavenJavaPublicationToLocalRepository
|
||||
./gradlew cloneRepository -PrepositoryName="spring-projects/spring-security-samples" -Pref="$samples_branch" -PcloneOutputDirectory="$SAMPLES_DIR"
|
||||
./gradlew --refresh-dependencies --project-dir "$SAMPLES_DIR" --init-script spring-security-ci.gradle -PlocalRepositoryPath="$LOCAL_REPOSITORY_PATH" -PspringSecurityVersion="$version" test integrationTest
|
||||
deploy-artifacts:
|
||||
name: Deploy Artifacts
|
||||
needs: [ build, test, check-samples ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@v1
|
||||
with:
|
||||
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
secrets: inherit
|
||||
deploy-docs:
|
||||
name: Deploy Docs
|
||||
needs: [ build, test, check-samples ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-docs.yml@v1
|
||||
with:
|
||||
should-deploy-docs: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
secrets: inherit
|
||||
deploy-schema:
|
||||
name: Deploy Schema
|
||||
needs: [ build, test, check-samples ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@v1
|
||||
with:
|
||||
should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
secrets: inherit
|
||||
perform-release:
|
||||
name: Perform Release
|
||||
needs: [ deploy-artifacts, deploy-docs, deploy-schema ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@v1
|
||||
with:
|
||||
should-perform-release: ${{ needs.deploy-artifacts.outputs.artifacts-deployed }}
|
||||
project-version: ${{ needs.deploy-artifacts.outputs.project-version }}
|
||||
milestone-repo-url: https://repo.spring.io/artifactory/milestone
|
||||
release-repo-url: https://repo1.maven.org/maven2
|
||||
artifact-path: org/springframework/security/spring-security-core
|
||||
slack-announcing-id: spring-security-announcing
|
||||
secrets: inherit
|
||||
send-notification:
|
||||
name: Send Notification
|
||||
needs: [ perform-release ]
|
||||
if: ${{ !success() }}
|
||||
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 }}
|
||||
@@ -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@v4
|
||||
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,33 +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
|
||||
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@v4
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Set up Gradle
|
||||
uses: gradle/gradle-build-action@v3
|
||||
- name: Upgrade Wrappers
|
||||
run: ./gradlew clean upgradeGradleWrapperAll --continue -Porg.gradle.java.installations.auto-download=false
|
||||
env:
|
||||
WRAPPER_UPGRADE_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,35 +0,0 @@
|
||||
name: Check Milestone
|
||||
on:
|
||||
milestone:
|
||||
types: [created, opened, edited]
|
||||
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'"
|
||||
- name: Validate DueOn
|
||||
if: env.DUE_ON != ''
|
||||
run: |
|
||||
export TOOL_VERSION=0.1.1
|
||||
wget "https://repo.maven.apache.org/maven2/io/spring/releasetrain/spring-release-train-tools/$TOOL_VERSION/spring-release-train-tools-$TOOL_VERSION.jar"
|
||||
java -cp "spring-release-train-tools-$TOOL_VERSION.jar" io.spring.releasetrain.CheckMilestoneDueOnMain --dueOn "$DUE_ON" --expectedDayOfWeek MONDAY --expectedMondayCount 3
|
||||
send-notification:
|
||||
name: Send Notification
|
||||
needs: [ spring-releasetrain-checks ]
|
||||
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 }}
|
||||
@@ -1,51 +0,0 @@
|
||||
name: PR Build
|
||||
|
||||
on: pull_request
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v2
|
||||
with:
|
||||
java-version: '17'
|
||||
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@v4
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v2
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Run Antora
|
||||
run: ./gradlew -PbuildSrc.skipTests=true :spring-security-docs:antora
|
||||
- name: Upload Docs
|
||||
id: upload
|
||||
uses: actions/upload-artifact@v4
|
||||
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@v1
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
@@ -1,24 +0,0 @@
|
||||
name: Release Scheduler
|
||||
on:
|
||||
schedule:
|
||||
- cron: '15 15 * * MON' # Every Monday at 3:15pm UTC
|
||||
workflow_dispatch:
|
||||
permissions: read-all
|
||||
jobs:
|
||||
dispatch_scheduled_releases:
|
||||
name: Dispatch scheduled releases
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
strategy:
|
||||
matrix:
|
||||
# List of active maintenance branches.
|
||||
branch: [ main, 6.3.x, 6.2.x, 5.8.x ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Dispatch
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
run: gh workflow run update-scheduled-release-version.yml -r ${{ matrix.branch }}
|
||||
@@ -1,35 +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:
|
||||
runs-on: ubuntu-latest
|
||||
name: Update on Supported Branches
|
||||
strategy:
|
||||
matrix:
|
||||
branch: [ '5.8.x', '6.2.x', '6.3.x', 'main' ]
|
||||
steps:
|
||||
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@e28269199d1d27975cf7f65e16d6095c555b3cd0
|
||||
name: Update
|
||||
with:
|
||||
docs-branch: ${{ matrix.branch }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
antora-file-path: 'docs/antora-playbook.yml'
|
||||
update-antora-ui-spring-docs-build:
|
||||
runs-on: ubuntu-latest
|
||||
name: Update on docs-build
|
||||
steps:
|
||||
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@e28269199d1d27975cf7f65e16d6095c555b3cd0
|
||||
name: Update
|
||||
with:
|
||||
docs-branch: 'docs-build'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,23 +0,0 @@
|
||||
name: Update Scheduled Release Version
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Manual trigger only. Triggered by release-scheduler.yml on main.
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update-scheduled-release-version:
|
||||
name: Update Scheduled Release Version
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml@v1
|
||||
secrets: inherit
|
||||
send-notification:
|
||||
name: Send Notification
|
||||
needs: [ update-scheduled-release-version ]
|
||||
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 }}
|
||||
+1
-1
@@ -105,7 +105,7 @@ Once merged, the fix will be forwarded-ported to applicable branches including `
|
||||
1. Create a local branch
|
||||
If this is for an issue, consider a branch name with the issue number, like `gh-22276`.
|
||||
[[write-tests]]
|
||||
1. Add documentation and JUnit Tests for your changes.
|
||||
1. Add JUnit Tests for your changes
|
||||
[[update-copyright]]
|
||||
1. In all files you edited, if the copyright header is of the form 2002-20xx, update the final copyright year to the current year.
|
||||
[[add-since]]
|
||||
|
||||
+5
-7
@@ -1,8 +1,8 @@
|
||||
image::https://badges.gitter.im/Join%20Chat.svg[Gitter,link=https://gitter.im/spring-projects/spring-security?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge]
|
||||
|
||||
image:https://github.com/spring-projects/spring-security/actions/workflows/continuous-integration-workflow.yml/badge.svg?branch=main["Build Status", link="https://github.com/spring-projects/spring-security/actions/workflows/continuous-integration-workflow.yml"]
|
||||
image:https://github.com/spring-projects/spring-security/workflows/CI/badge.svg?branch=main["Build Status", link="https://github.com/spring-projects/spring-security/actions?query=workflow%3ACI"]
|
||||
|
||||
image:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Develocity", link="https://ge.spring.io/scans?search.rootProjectNames=spring-security"]
|
||||
image:https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Gradle Enterprise", link="https://ge.spring.io/scans?search.rootProjectNames=spring-security"]
|
||||
|
||||
= Spring Security
|
||||
|
||||
@@ -15,16 +15,14 @@ For a detailed list of features and access to the latest release, please visit h
|
||||
Please see our https://github.com/spring-projects/.github/blob/main/CODE_OF_CONDUCT.md[code of conduct]
|
||||
|
||||
== Downloading Artifacts
|
||||
See https://docs.spring.io/spring-security/reference/getting-spring-security.html[Getting Spring Security] for how to obtain Spring Security.
|
||||
See https://docs.spring.io/spring-security/site/docs/current/reference/html5/#getting[Getting Spring Security] for how to obtain Spring Security.
|
||||
|
||||
== Documentation
|
||||
Be sure to read the https://docs.spring.io/spring-security/reference/[Spring Security Reference].
|
||||
Be sure to read the https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/[Spring Security Reference].
|
||||
Extensive JavaDoc for the Spring Security code is also available in the https://docs.spring.io/spring-security/site/docs/current/api/[Spring Security API Documentation].
|
||||
|
||||
You may also want to check out https://docs.spring.io/spring-security/reference/whats-new.html[what's new in the latest release].
|
||||
|
||||
== Quick Start
|
||||
See https://docs.spring.io/spring-security/reference/servlet/getting-started.html[Hello Spring Security] to get started with a "Hello, World" application.
|
||||
See https://docs.spring.io/spring-security/site/docs/current/reference/html5/#servlet-hello[Hello Spring Security] to get started with a "Hello, World" application.
|
||||
|
||||
== Building from Source
|
||||
Spring Security uses a https://gradle.org[Gradle]-based build system.
|
||||
|
||||
@@ -20,5 +20,4 @@ dependencies {
|
||||
testImplementation "org.springframework:spring-test"
|
||||
|
||||
testRuntimeOnly 'org.hsqldb:hsqldb'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
+1
-18
@@ -17,13 +17,10 @@
|
||||
package org.springframework.security.acls.domain;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.acls.model.SidRetrievalStrategy;
|
||||
@@ -62,8 +59,6 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
|
||||
|
||||
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
|
||||
|
||||
private RoleHierarchy roleHierarchy = new NullRoleHierarchy();
|
||||
|
||||
/**
|
||||
* Constructor. The only mandatory parameter relates to the system-wide
|
||||
* {@link GrantedAuthority} instances that can be held to always permit ACL changes.
|
||||
@@ -105,9 +100,7 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
|
||||
}
|
||||
|
||||
// Iterate this principal's authorities to determine right
|
||||
Collection<? extends GrantedAuthority> reachableGrantedAuthorities = this.roleHierarchy
|
||||
.getReachableGrantedAuthorities(authentication.getAuthorities());
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(reachableGrantedAuthorities);
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
|
||||
if (acl.getOwner() instanceof GrantedAuthoritySid
|
||||
&& authorities.contains(((GrantedAuthoritySid) acl.getOwner()).getGrantedAuthority())) {
|
||||
return;
|
||||
@@ -169,14 +162,4 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
|
||||
this.securityContextHolderStrategy = securityContextHolderStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RoleHierarchy} to use. The default is to use a
|
||||
* {@link NullRoleHierarchy}
|
||||
* @since 6.4
|
||||
*/
|
||||
public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
|
||||
Assert.notNull(roleHierarchy, "roleHierarchy cannot be null");
|
||||
this.roleHierarchy = roleHierarchy;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
public boolean isSidLoaded(List<Sid> sids) {
|
||||
// If loadedSides is null, this indicates all SIDs were loaded
|
||||
// Also return true if the caller didn't specify a SID to find
|
||||
if ((this.loadedSids == null) || (sids == null) || sids.isEmpty()) {
|
||||
if ((this.loadedSids == null) || (sids == null) || (sids.size() == 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -140,7 +140,7 @@ public class DefaultPermissionFactory implements PermissionFactory {
|
||||
|
||||
@Override
|
||||
public List<Permission> buildFromNames(List<String> names) {
|
||||
if ((names == null) || names.isEmpty()) {
|
||||
if ((names == null) || (names.size() == 0)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Permission> permissions = new ArrayList<>(names.size());
|
||||
|
||||
-11
@@ -25,7 +25,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
@@ -35,7 +34,6 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
@@ -88,15 +86,6 @@ public class AclAuthorizationStrategyImplTests {
|
||||
this.strategy.securityCheck(this.acl, AclAuthorizationStrategy.CHANGE_GENERAL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securityCheckWhenRoleReachableByHierarchyThenAuthorized() {
|
||||
given(this.acl.getOwner()).willReturn(new GrantedAuthoritySid("ROLE_AUTH_B"));
|
||||
this.strategy = new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_SYSTEM_ADMIN"));
|
||||
this.strategy.setRoleHierarchy(RoleHierarchyImpl.fromHierarchy("ROLE_AUTH > ROLE_AUTH_B"));
|
||||
assertThatNoException()
|
||||
.isThrownBy(() -> this.strategy.securityCheck(this.acl, AclAuthorizationStrategy.CHANGE_GENERAL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securityCheckWhenCustomSecurityContextHolderStrategyThenUses() {
|
||||
given(this.securityContextHolderStrategy.getContext()).willReturn(this.context);
|
||||
|
||||
@@ -27,8 +27,6 @@ dependencies {
|
||||
testImplementation "org.mockito:mockito-junit-jupiter"
|
||||
testImplementation "org.springframework:spring-test"
|
||||
testAspect sourceSets.main.output
|
||||
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
compileAspectj.ajcOptions.outxmlfile = "META-INF/aop.xml"
|
||||
|
||||
+1
-21
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -103,13 +103,6 @@ public class PostAuthorizeAspectTests {
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.prePostSecured::denyAllMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedDenyAllPostAuthorizeDeniesAccess() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.anne);
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(() -> this.secured.myObject().denyAllMethod());
|
||||
}
|
||||
|
||||
interface SecuredInterface {
|
||||
|
||||
@PostAuthorize("hasRole('X')")
|
||||
@@ -141,10 +134,6 @@ public class PostAuthorizeAspectTests {
|
||||
privateMethod();
|
||||
}
|
||||
|
||||
NestedObject myObject() {
|
||||
return new NestedObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SecuredImplSubclass extends SecuredImpl {
|
||||
@@ -168,13 +157,4 @@ public class PostAuthorizeAspectTests {
|
||||
|
||||
}
|
||||
|
||||
static class NestedObject {
|
||||
|
||||
@PostAuthorize("denyAll")
|
||||
void denyAllMethod() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -54,11 +54,6 @@ public class PostFilterAspectTests {
|
||||
assertThat(this.prePostSecured.postFilterMethod(objects)).containsExactly("apple", "aubergine");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedDenyAllPostFilterDeniesAccess() {
|
||||
assertThat(this.prePostSecured.myObject().denyAllMethod()).isEmpty();
|
||||
}
|
||||
|
||||
static class PrePostSecured {
|
||||
|
||||
@PostFilter("filterObject.startsWith('a')")
|
||||
@@ -66,19 +61,6 @@ public class PostFilterAspectTests {
|
||||
return objects;
|
||||
}
|
||||
|
||||
NestedObject myObject() {
|
||||
return new NestedObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NestedObject {
|
||||
|
||||
@PostFilter("filterObject == null")
|
||||
List<String> denyAllMethod() {
|
||||
return new ArrayList<>(List.of("deny"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-45
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
@@ -47,8 +47,6 @@ public class PreAuthorizeAspectTests {
|
||||
|
||||
private PrePostSecured prePostSecured = new PrePostSecured();
|
||||
|
||||
private MultipleInterfaces multiple = new MultipleInterfaces();
|
||||
|
||||
@BeforeEach
|
||||
public final void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
@@ -105,19 +103,6 @@ public class PreAuthorizeAspectTests {
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.prePostSecured::denyAllMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedDenyAllPreAuthorizeDeniesAccess() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.anne);
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(() -> this.secured.myObject().denyAllMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleInterfacesPreAuthorizeAllows() {
|
||||
// aspectj doesn't inherit annotations
|
||||
this.multiple.securedMethod();
|
||||
}
|
||||
|
||||
interface SecuredInterface {
|
||||
|
||||
@PreAuthorize("hasRole('X')")
|
||||
@@ -144,15 +129,11 @@ public class PreAuthorizeAspectTests {
|
||||
protected void protectedMethod() {
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('A')")
|
||||
@PreAuthorize("hasRole('X')")
|
||||
void publicCallsPrivate() {
|
||||
privateMethod();
|
||||
}
|
||||
|
||||
NestedObject myObject() {
|
||||
return new NestedObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SecuredImplSubclass extends SecuredImpl {
|
||||
@@ -176,28 +157,4 @@ public class PreAuthorizeAspectTests {
|
||||
|
||||
}
|
||||
|
||||
static class NestedObject {
|
||||
|
||||
@PreAuthorize("denyAll")
|
||||
void denyAllMethod() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface AnotherSecuredInterface {
|
||||
|
||||
@PreAuthorize("hasRole('Y')")
|
||||
void securedMethod();
|
||||
|
||||
}
|
||||
|
||||
static class MultipleInterfaces implements SecuredInterface, AnotherSecuredInterface {
|
||||
|
||||
@Override
|
||||
public void securedMethod() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-19
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -54,11 +54,6 @@ public class PreFilterAspectTests {
|
||||
assertThat(this.prePostSecured.preFilterMethod(objects)).containsExactly("apple", "aubergine");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedDenyAllPreFilterDeniesAccess() {
|
||||
assertThat(this.prePostSecured.myObject().denyAllMethod(new ArrayList<>(List.of("deny")))).isEmpty();
|
||||
}
|
||||
|
||||
static class PrePostSecured {
|
||||
|
||||
@PreFilter("filterObject.startsWith('a')")
|
||||
@@ -66,19 +61,6 @@ public class PreFilterAspectTests {
|
||||
return objects;
|
||||
}
|
||||
|
||||
NestedObject myObject() {
|
||||
return new NestedObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NestedObject {
|
||||
|
||||
@PreFilter("filterObject == null")
|
||||
List<String> denyAllMethod(List<String> list) {
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
|
||||
+8
-6
@@ -20,6 +20,7 @@ plugins {
|
||||
|
||||
apply plugin: 'io.spring.nohttp'
|
||||
apply plugin: 'locks'
|
||||
apply plugin: 's101'
|
||||
apply plugin: 'io.spring.convention.root'
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
apply plugin: 'org.springframework.security.versions.verify-dependencies-versions'
|
||||
@@ -105,14 +106,10 @@ develocity {
|
||||
}
|
||||
|
||||
nohttp {
|
||||
source.exclude "buildSrc/build/**", "javascript/.gradle/**", "javascript/package-lock.json", "javascript/node_modules/**", "javascript/build/**", "javascript/dist/**"
|
||||
source.exclude "buildSrc/build/**"
|
||||
source.builtBy(project(':spring-security-config').tasks.withType(RncToXsd))
|
||||
}
|
||||
|
||||
tasks.named('checkstyleNohttp') {
|
||||
maxHeapSize = '1g'
|
||||
}
|
||||
|
||||
tasks.register('cloneRepository', IncludeRepoTask) {
|
||||
repository = project.getProperties().get("repositoryName")
|
||||
ref = project.getProperties().get("ref")
|
||||
@@ -120,11 +117,16 @@ tasks.register('cloneRepository', IncludeRepoTask) {
|
||||
outputDirectory = project.hasProperty("cloneOutputDirectory") ? project.file("$cloneOutputDirectory") : defaultDirectory
|
||||
}
|
||||
|
||||
s101 {
|
||||
repository = 'https://structure101.com/binaries/latest'
|
||||
configurationDirectory = project.file("etc/s101")
|
||||
}
|
||||
|
||||
wrapperUpgrade {
|
||||
gradle {
|
||||
'spring-security' {
|
||||
repo = 'spring-projects/spring-security'
|
||||
baseBranch = '6.3.x' // runs only on 6.3.x and the update is merged forward to main
|
||||
baseBranch = '6.1.x' // runs only on 6.1.x and the update is merged forward to main
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +95,6 @@ dependencies {
|
||||
testImplementation 'org.mockito:mockito-core'
|
||||
testImplementation 'org.mockito:mockito-junit-jupiter'
|
||||
testImplementation libs.com.squareup.okhttp3.mockwebserver
|
||||
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
@@ -7,6 +7,8 @@ import org.gradle.api.Project;
|
||||
import org.gradle.api.plugins.JavaPlatformPlugin;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.publish.PublishingExtension;
|
||||
import org.gradle.api.publish.VariantVersionMappingStrategy;
|
||||
import org.gradle.api.publish.VersionMappingStrategy;
|
||||
import org.gradle.api.publish.maven.MavenPublication;
|
||||
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
|
||||
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -23,6 +23,7 @@ import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency;
|
||||
import org.gradle.api.artifacts.VersionCatalog;
|
||||
import org.gradle.api.artifacts.VersionCatalogsExtension;
|
||||
import org.gradle.api.file.RegularFile;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.provider.Property;
|
||||
@@ -35,6 +36,7 @@ import org.gradle.api.tasks.TaskExecutionException;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
import org.gradle.api.tasks.VerificationException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -50,7 +50,7 @@ public class S101Plugin implements Plugin<Project> {
|
||||
|
||||
private void configure(JavaExec exec) {
|
||||
exec.setDescription("Runs Structure101 headless analysis, installing and configuring if necessary");
|
||||
exec.dependsOn("assemble");
|
||||
exec.dependsOn("check");
|
||||
Project project = exec.getProject();
|
||||
S101PluginExtension extension = project.getExtensions().getByType(S101PluginExtension.class);
|
||||
exec
|
||||
|
||||
@@ -6,7 +6,5 @@ dependencies {
|
||||
implementation 'org.springframework:spring-core'
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-engine"
|
||||
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,4 @@ dependencies {
|
||||
optional 'ch.qos.logback:logback-classic'
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-engine"
|
||||
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
@@ -22,6 +22,4 @@ dependencies {
|
||||
testImplementation "org.mockito:mockito-junit-jupiter"
|
||||
testImplementation "org.springframework:spring-test"
|
||||
testImplementation 'org.skyscreamer:jsonassert'
|
||||
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
+1
-13
@@ -56,7 +56,6 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Scott Battaglia
|
||||
* @author Kim Youngwoong
|
||||
*/
|
||||
public class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
|
||||
|
||||
@@ -64,7 +63,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
||||
|
||||
private AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService;
|
||||
|
||||
private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
|
||||
private final UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
|
||||
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
|
||||
@@ -188,17 +187,6 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
|
||||
this.authenticationUserDetailsService = authenticationUserDetailsService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the UserDetailsChecker to be used for checking the status of retrieved user
|
||||
* details. This allows customization of the UserDetailsChecker implementation.
|
||||
* @param userDetailsChecker the UserDetailsChecker to be set
|
||||
* @since 6.4
|
||||
*/
|
||||
public void setUserDetailsChecker(final UserDetailsChecker userDetailsChecker) {
|
||||
Assert.notNull(userDetailsChecker, "userDetailsChecker cannot be null");
|
||||
this.userDetailsChecker = userDetailsChecker;
|
||||
}
|
||||
|
||||
public void setServiceProperties(final ServiceProperties serviceProperties) {
|
||||
this.serviceProperties = serviceProperties;
|
||||
}
|
||||
|
||||
+9
-2
@@ -115,8 +115,15 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken implemen
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
if (obj instanceof CasAuthenticationToken test) {
|
||||
return this.assertion.equals(test.getAssertion()) && this.getKeyHash() == test.getKeyHash();
|
||||
if (obj instanceof CasAuthenticationToken) {
|
||||
CasAuthenticationToken test = (CasAuthenticationToken) obj;
|
||||
if (!this.assertion.equals(test.getAssertion())) {
|
||||
return false;
|
||||
}
|
||||
if (this.getKeyHash() != test.getKeyHash()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ import org.springframework.security.jackson2.SecurityJackson2Modules;
|
||||
* @since 4.2
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CasJackson2Module extends SimpleModule {
|
||||
|
||||
public CasJackson2Module() {
|
||||
|
||||
+3
-15
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -28,7 +28,6 @@ import org.springframework.security.cas.ServiceProperties;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.DefaultRedirectStrategy;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -62,8 +61,6 @@ public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, In
|
||||
*/
|
||||
private boolean encodeServiceUrlWithSessionId = true;
|
||||
|
||||
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.hasLength(this.loginUrl, "loginUrl must be specified");
|
||||
@@ -77,7 +74,8 @@ public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, In
|
||||
String urlEncodedService = createServiceUrl(servletRequest, response);
|
||||
String redirectUrl = createRedirectUrl(urlEncodedService);
|
||||
preCommence(servletRequest, response);
|
||||
this.redirectStrategy.sendRedirect(servletRequest, response, redirectUrl);
|
||||
new DefaultRedirectStrategy().sendRedirect(servletRequest, response, redirectUrl);
|
||||
// response.sendRedirect(redirectUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,14 +149,4 @@ public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, In
|
||||
return this.encodeServiceUrlWithSessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RedirectStrategy} to use
|
||||
* @param redirectStrategy the {@link RedirectStrategy} to use
|
||||
* @since 6.3
|
||||
*/
|
||||
public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
|
||||
Assert.notNull(redirectStrategy, "redirectStrategy cannot be null");
|
||||
this.redirectStrategy = redirectStrategy;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
-61
@@ -22,7 +22,6 @@ import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.apereo.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||
import org.apereo.cas.client.util.WebUtils;
|
||||
import org.apereo.cas.client.validation.TicketValidator;
|
||||
@@ -41,20 +40,14 @@ import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.web.DefaultRedirectStrategy;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
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.HttpSessionSecurityContextRepository;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Processes a CAS service ticket, obtains proxy granting tickets, and processes proxy
|
||||
@@ -62,14 +55,14 @@ import org.springframework.util.StringUtils;
|
||||
* <h2>Service Tickets</h2>
|
||||
* <p>
|
||||
* A service ticket consists of an opaque ticket string. It arrives at this filter by the
|
||||
* user's browser successfully authenticating using CAS, and then receiving an HTTP
|
||||
* user's browser successfully authenticating using CAS, and then receiving a HTTP
|
||||
* redirect to a <code>service</code>. The opaque ticket string is presented in the
|
||||
* <code>ticket</code> request parameter.
|
||||
* <p>
|
||||
* This filter monitors the <code>service</code> URL so that it can receive the service
|
||||
* ticket and process it. By default, this filter processes the URL <tt>/login/cas</tt>.
|
||||
* When processing this URL, the value of {@link ServiceProperties#getService()} is used
|
||||
* as the <tt>service</tt> when validating the <code>ticket</code>. This means that it is
|
||||
* This filter monitors the <code>service</code> URL so it can receive the service ticket
|
||||
* and process it. By default this filter processes the URL <tt>/login/cas</tt>. When
|
||||
* processing this URL, the value of {@link ServiceProperties#getService()} is used as the
|
||||
* <tt>service</tt> when validating the <code>ticket</code>. This means that it is
|
||||
* important that {@link ServiceProperties#getService()} specifies the same value as the
|
||||
* <tt>filterProcessesUrl</tt>.
|
||||
* <p>
|
||||
@@ -80,22 +73,22 @@ import org.springframework.util.StringUtils;
|
||||
* <h2>Obtaining Proxy Granting Tickets</h2>
|
||||
* <p>
|
||||
* If specified, the filter can also monitor the <code>proxyReceptorUrl</code>. The filter
|
||||
* will respond to the requests matching this url so that the CAS Server can provide a PGT
|
||||
* to the filter. Note that in addition to the <code>proxyReceptorUrl</code> a non-null
|
||||
* will respond to requests matching this url so that the CAS Server can provide a PGT to
|
||||
* the filter. Note that in addition to the <code>proxyReceptorUrl</code> a non-null
|
||||
* <code>proxyGrantingTicketStorage</code> must be provided in order for the filter to
|
||||
* respond to proxy receptor requests. By configuring a shared
|
||||
* {@link ProxyGrantingTicketStorage} between the {@link TicketValidator} and the
|
||||
* <code>CasAuthenticationFilter</code>, one can have the
|
||||
* <code>CasAuthenticationFilter</code> handling the proxying requirements for CAS.
|
||||
* CasAuthenticationFilter one can have the CasAuthenticationFilter handle the proxying
|
||||
* requirements for CAS.
|
||||
* <h2>Proxy Tickets</h2>
|
||||
* <p>
|
||||
* The filter can process tickets present on any url. This is useful when one wants to
|
||||
* process proxy tickets. In order for proxy tickets to get processed,
|
||||
* The filter can process tickets present on any url. This is useful when wanting to
|
||||
* process proxy tickets. In order for proxy tickets to get processed
|
||||
* {@link ServiceProperties#isAuthenticateAllArtifacts()} must return <code>true</code>.
|
||||
* Additionally, if the request is already authenticated, authentication will <b>not</b>
|
||||
* occur. Last, {@link AuthenticationDetailsSource#buildDetails(Object)} must return a
|
||||
* {@link ServiceAuthenticationDetails}. This can be accomplished using the
|
||||
* {@link ServiceAuthenticationDetailsSource}. In this case,
|
||||
* {@link ServiceAuthenticationDetailsSource}. In this case
|
||||
* {@link ServiceAuthenticationDetails#getServiceUrl()} will be used for the service url.
|
||||
* <p>
|
||||
* Processing the proxy ticket involves creating a
|
||||
@@ -207,10 +200,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
.getContextHolderStrategy();
|
||||
|
||||
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
|
||||
|
||||
private RequestCache requestCache = new HttpSessionRequestCache();
|
||||
|
||||
private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
||||
|
||||
public CasAuthenticationFilter() {
|
||||
@@ -251,22 +240,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
||||
return null;
|
||||
}
|
||||
String serviceTicket = obtainArtifact(request);
|
||||
if (!StringUtils.hasText(serviceTicket)) {
|
||||
HttpSession session = request.getSession(false);
|
||||
if (session != null && session
|
||||
.getAttribute(CasGatewayAuthenticationRedirectFilter.CAS_GATEWAY_AUTHENTICATION_ATTR) != null) {
|
||||
this.logger.debug("Failed authentication response from CAS gateway request");
|
||||
session.removeAttribute(CasGatewayAuthenticationRedirectFilter.CAS_GATEWAY_AUTHENTICATION_ATTR);
|
||||
SavedRequest savedRequest = this.requestCache.getRequest(request, response);
|
||||
if (savedRequest != null) {
|
||||
String redirectUrl = savedRequest.getRedirectUrl();
|
||||
this.logger.debug(LogMessage.format("Redirecting to: %s", redirectUrl));
|
||||
this.requestCache.removeRequest(request, response);
|
||||
this.redirectStrategy.sendRedirect(request, response, redirectUrl);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (serviceTicket == null) {
|
||||
this.logger.debug("Failed to obtain an artifact (cas ticket)");
|
||||
serviceTicket = "";
|
||||
}
|
||||
@@ -344,28 +318,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
|
||||
this.securityContextHolderStrategy = securityContextHolderStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link RedirectStrategy} used to redirect to the saved request if there is
|
||||
* one saved. Defaults to {@link DefaultRedirectStrategy}.
|
||||
* @param redirectStrategy the redirect strategy to use
|
||||
* @since 6.3
|
||||
*/
|
||||
public final void setRedirectStrategy(RedirectStrategy redirectStrategy) {
|
||||
Assert.notNull(redirectStrategy, "redirectStrategy cannot be null");
|
||||
this.redirectStrategy = redirectStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link RequestCache} used to retrieve the saved request in failed gateway
|
||||
* authentication scenarios.
|
||||
* @param requestCache the request cache to use
|
||||
* @since 6.3
|
||||
*/
|
||||
public final void setRequestCache(RequestCache requestCache) {
|
||||
Assert.notNull(requestCache, "requestCache cannot be null");
|
||||
this.requestCache = requestCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the request is elgible to process a service ticket. This method exists
|
||||
* for readability.
|
||||
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.cas.web;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.apereo.cas.client.util.CommonUtils;
|
||||
import org.apereo.cas.client.util.WebUtils;
|
||||
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
import org.springframework.security.web.DefaultRedirectStrategy;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
/**
|
||||
* Redirects the request to the CAS server appending {@code gateway=true} to the URL. Upon
|
||||
* redirection, the {@link ServiceProperties#isSendRenew()} is ignored and considered as
|
||||
* {@code false} to align with the specification says that the {@code sendRenew} parameter
|
||||
* is not compatible with the {@code gateway} parameter. See the <a href=
|
||||
* "https://apereo.github.io/cas/6.6.x/protocol/CAS-Protocol-V2-Specification.html#:~:text=This%20parameter%20is%20not%20compatible%20with%20the%20%E2%80%9Crenew%E2%80%9D%20parameter.%20Behavior%20is%20undefined%20if%20both%20are%20set.">CAS
|
||||
* Protocol Specification</a> for more details. To allow other filters to know if the
|
||||
* request is a gateway request, this filter creates a session and add an attribute with
|
||||
* name {@link #CAS_GATEWAY_AUTHENTICATION_ATTR} which can be checked by other filters if
|
||||
* needed. It is recommended that this filter is placed after
|
||||
* {@link CasAuthenticationFilter} if it is defined.
|
||||
*
|
||||
* @author Michael Remond
|
||||
* @author Jerome LELEU
|
||||
* @author Marcus da Coregio
|
||||
* @since 6.3
|
||||
*/
|
||||
public final class CasGatewayAuthenticationRedirectFilter extends GenericFilterBean {
|
||||
|
||||
public static final String CAS_GATEWAY_AUTHENTICATION_ATTR = "CAS_GATEWAY_AUTHENTICATION";
|
||||
|
||||
private final String casLoginUrl;
|
||||
|
||||
private final ServiceProperties serviceProperties;
|
||||
|
||||
private RequestMatcher requestMatcher;
|
||||
|
||||
private RequestCache requestCache = new HttpSessionRequestCache();
|
||||
|
||||
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
|
||||
|
||||
/**
|
||||
* Constructs a new instance of this class
|
||||
* @param serviceProperties the {@link ServiceProperties}
|
||||
*/
|
||||
public CasGatewayAuthenticationRedirectFilter(String casLoginUrl, ServiceProperties serviceProperties) {
|
||||
Assert.hasText(casLoginUrl, "casLoginUrl cannot be null or empty");
|
||||
Assert.notNull(serviceProperties, "serviceProperties cannot be null");
|
||||
this.casLoginUrl = casLoginUrl;
|
||||
this.serviceProperties = serviceProperties;
|
||||
this.requestMatcher = new CasGatewayResolverRequestMatcher(this.serviceProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
|
||||
HttpServletRequest request = (HttpServletRequest) req;
|
||||
HttpServletResponse response = (HttpServletResponse) res;
|
||||
|
||||
if (!this.requestMatcher.matches(request)) {
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
this.requestCache.saveRequest(request, response);
|
||||
HttpSession session = request.getSession(true);
|
||||
session.setAttribute(CAS_GATEWAY_AUTHENTICATION_ATTR, true);
|
||||
String urlEncodedService = WebUtils.constructServiceUrl(request, response, this.serviceProperties.getService(),
|
||||
null, this.serviceProperties.getServiceParameter(), this.serviceProperties.getArtifactParameter(),
|
||||
true);
|
||||
String redirectUrl = CommonUtils.constructRedirectUrl(this.casLoginUrl,
|
||||
this.serviceProperties.getServiceParameter(), urlEncodedService, false, true);
|
||||
this.redirectStrategy.sendRedirect(request, response, redirectUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RequestMatcher} used to trigger this filter. Defaults to
|
||||
* {@link CasGatewayResolverRequestMatcher}.
|
||||
* @param requestMatcher the {@link RequestMatcher} to use
|
||||
*/
|
||||
public void setRequestMatcher(RequestMatcher requestMatcher) {
|
||||
Assert.notNull(requestMatcher, "requestMatcher cannot be null");
|
||||
this.requestMatcher = requestMatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RequestCache} used to store the current request to be replayed
|
||||
* after redirect from the CAS server. Defaults to {@link HttpSessionRequestCache}.
|
||||
* @param requestCache the {@link RequestCache} to use
|
||||
*/
|
||||
public void setRequestCache(RequestCache requestCache) {
|
||||
Assert.notNull(requestCache, "requestCache cannot be null");
|
||||
this.requestCache = requestCache;
|
||||
}
|
||||
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.cas.web;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.apereo.cas.client.authentication.DefaultGatewayResolverImpl;
|
||||
import org.apereo.cas.client.authentication.GatewayResolver;
|
||||
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link RequestMatcher} implementation that delegates the check to an instance of
|
||||
* {@link GatewayResolver}. The request is marked as "gatewayed" using the configured
|
||||
* {@link GatewayResolver} to avoid infinite loop.
|
||||
*
|
||||
* @author Michael Remond
|
||||
* @author Marcus da Coregio
|
||||
* @since 6.3
|
||||
*/
|
||||
public final class CasGatewayResolverRequestMatcher implements RequestMatcher {
|
||||
|
||||
private final ServiceProperties serviceProperties;
|
||||
|
||||
private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();
|
||||
|
||||
public CasGatewayResolverRequestMatcher(ServiceProperties serviceProperties) {
|
||||
Assert.notNull(serviceProperties, "serviceProperties cannot be null");
|
||||
this.serviceProperties = serviceProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(HttpServletRequest request) {
|
||||
boolean wasGatewayed = this.gatewayStorage.hasGatewayedAlready(request, this.serviceProperties.getService());
|
||||
if (!wasGatewayed) {
|
||||
this.gatewayStorage.storeGatewayInformation(request, this.serviceProperties.getService());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link GatewayResolver} to check if the request was already gatewayed.
|
||||
* Defaults to {@link DefaultGatewayResolverImpl}
|
||||
* @param gatewayStorage the {@link GatewayResolver} to use. Cannot be null.
|
||||
*/
|
||||
public void setGatewayStorage(GatewayResolver gatewayStorage) {
|
||||
Assert.notNull(gatewayStorage, "gatewayStorage cannot be null");
|
||||
this.gatewayStorage = gatewayStorage;
|
||||
}
|
||||
|
||||
}
|
||||
-27
@@ -18,7 +18,6 @@ package org.springframework.security.cas.authentication;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apereo.cas.client.validation.Assertion;
|
||||
import org.apereo.cas.client.validation.AssertionImpl;
|
||||
@@ -32,13 +31,11 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsChecker;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.web.authentication.WebAuthenticationDetails;
|
||||
|
||||
@@ -58,7 +55,6 @@ import static org.mockito.Mockito.verify;
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Scott Battaglia
|
||||
* @author Kim Youngwoong
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class CasAuthenticationProviderTests {
|
||||
@@ -324,29 +320,6 @@ public class CasAuthenticationProviderTests {
|
||||
assertThat(cap.supports(CasAuthenticationToken.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetUserDetailsChecker() throws AuthenticationException {
|
||||
CasAuthenticationProvider cap = new CasAuthenticationProvider();
|
||||
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
|
||||
cap.setKey("qwerty");
|
||||
cap.setTicketValidator(new MockTicketValidator(true));
|
||||
cap.setServiceProperties(makeServiceProperties());
|
||||
cap.afterPropertiesSet();
|
||||
CasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateful("ST-123");
|
||||
|
||||
AtomicInteger checkCount = new AtomicInteger(0);
|
||||
UserDetailsChecker userDetailsChecker = new UserDetailsChecker() {
|
||||
@Override
|
||||
public void check(UserDetails user) {
|
||||
checkCount.incrementAndGet();
|
||||
}
|
||||
};
|
||||
cap.setUserDetailsChecker(userDetailsChecker);
|
||||
cap.authenticate(token);
|
||||
|
||||
assertThat(checkCount.get()).isEqualTo(1);
|
||||
}
|
||||
|
||||
private class MockAuthoritiesPopulator implements AuthenticationUserDetailsService {
|
||||
|
||||
@Override
|
||||
|
||||
-27
@@ -16,22 +16,16 @@
|
||||
|
||||
package org.springframework.security.cas.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
import org.springframework.security.web.RedirectStrategy;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests {@link CasAuthenticationEntryPoint}.
|
||||
@@ -101,25 +95,4 @@ public class CasAuthenticationEntryPointTests {
|
||||
.isEqualTo(response.getRedirectedUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
void setRedirectStrategyThenUses() throws IOException {
|
||||
CasAuthenticationEntryPoint ep = new CasAuthenticationEntryPoint();
|
||||
ServiceProperties sp = new ServiceProperties();
|
||||
|
||||
sp.setService("https://mycompany.com/login/cas");
|
||||
ep.setServiceProperties(sp);
|
||||
ep.setLoginUrl("https://cas/login");
|
||||
|
||||
RedirectStrategy redirectStrategy = mock();
|
||||
|
||||
ep.setRedirectStrategy(redirectStrategy);
|
||||
MockHttpServletRequest req = new MockHttpServletRequest();
|
||||
MockHttpServletResponse res = new MockHttpServletResponse();
|
||||
|
||||
ep.commence(req, res, new BadCredentialsException("bad credentials"));
|
||||
|
||||
verify(redirectStrategy).sendRedirect(eq(req), eq(res),
|
||||
eq("https://cas/login?service=https%3A%2F%2Fmycompany.com%2Flogin%2Fcas"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-21
@@ -20,7 +20,6 @@ import java.io.IOException;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import org.apereo.cas.client.proxy.ProxyGrantingTicketStorage;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -42,7 +41,6 @@ import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.context.SecurityContextRepository;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -226,25 +224,6 @@ public class CasAuthenticationFilterTests {
|
||||
verify(securityContextRepository).saveContext(any(SecurityContext.class), eq(request), eq(response));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void attemptAuthenticationWhenNoServiceTicketAndIsGatewayRequestThenRedirectToSavedRequestAndClearAttribute()
|
||||
throws Exception {
|
||||
CasAuthenticationFilter filter = new CasAuthenticationFilter();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
HttpSession session = request.getSession(true);
|
||||
session.setAttribute(CasGatewayAuthenticationRedirectFilter.CAS_GATEWAY_AUTHENTICATION_ATTR, true);
|
||||
|
||||
new HttpSessionRequestCache().saveRequest(request, response);
|
||||
|
||||
Authentication authn = filter.attemptAuthentication(request, response);
|
||||
assertThat(authn).isNull();
|
||||
assertThat(response.getStatus()).isEqualTo(302);
|
||||
assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost?continue");
|
||||
assertThat(session.getAttribute(CasGatewayAuthenticationRedirectFilter.CAS_GATEWAY_AUTHENTICATION_ATTR))
|
||||
.isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void successfulAuthenticationWhenSecurityContextRepositorySetThenUses() throws ServletException, IOException {
|
||||
SecurityContextRepository securityContextRepository = mock(SecurityContextRepository.class);
|
||||
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.cas.web;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link CasGatewayAuthenticationRedirectFilter}.
|
||||
*
|
||||
* @author Jerome LELEU
|
||||
* @author Marcus da Coregio
|
||||
*/
|
||||
public class CasGatewayAuthenticationRedirectFilterTests {
|
||||
|
||||
private static final String CAS_LOGIN_URL = "http://mycasserver/login";
|
||||
|
||||
CasGatewayAuthenticationRedirectFilter filter = new CasGatewayAuthenticationRedirectFilter(CAS_LOGIN_URL,
|
||||
serviceProperties());
|
||||
|
||||
@Test
|
||||
void doFilterWhenMatchesThenSavesRequestAndSavesAttributeAndSendRedirect() throws IOException, ServletException {
|
||||
RequestCache requestCache = mock();
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
this.filter.setRequestMatcher((req) -> true);
|
||||
this.filter.setRequestCache(requestCache);
|
||||
this.filter.doFilter(request, response, new MockFilterChain());
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());
|
||||
assertThat(response.getHeader("Location"))
|
||||
.isEqualTo("http://mycasserver/login?service=http%3A%2F%2Flocalhost%2Flogin%2Fcas&gateway=true");
|
||||
verify(requestCache).saveRequest(request, response);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterWhenNotMatchThenContinueFilter() throws ServletException, IOException {
|
||||
this.filter.setRequestMatcher((req) -> false);
|
||||
FilterChain chain = mock();
|
||||
MockHttpServletResponse response = mock();
|
||||
this.filter.doFilter(new MockHttpServletRequest(), response, chain);
|
||||
verify(chain).doFilter(any(), any());
|
||||
verifyNoInteractions(response);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterWhenSendRenewTrueThenIgnores() throws ServletException, IOException {
|
||||
ServiceProperties serviceProperties = serviceProperties();
|
||||
serviceProperties.setSendRenew(true);
|
||||
this.filter = new CasGatewayAuthenticationRedirectFilter(CAS_LOGIN_URL, serviceProperties);
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
this.filter.setRequestMatcher((req) -> true);
|
||||
this.filter.doFilter(request, response, new MockFilterChain());
|
||||
assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value());
|
||||
assertThat(response.getHeader("Location"))
|
||||
.isEqualTo("http://mycasserver/login?service=http%3A%2F%2Flocalhost%2Flogin%2Fcas&gateway=true");
|
||||
}
|
||||
|
||||
private static ServiceProperties serviceProperties() {
|
||||
ServiceProperties serviceProperties = new ServiceProperties();
|
||||
serviceProperties.setService("http://localhost/login/cas");
|
||||
return serviceProperties;
|
||||
}
|
||||
|
||||
}
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.cas.web;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests {@link CasGatewayResolverRequestMatcher}.
|
||||
*
|
||||
* @author Marcus da Coregio
|
||||
*/
|
||||
class CasGatewayResolverRequestMatcherTests {
|
||||
|
||||
CasGatewayResolverRequestMatcher matcher = new CasGatewayResolverRequestMatcher(new ServiceProperties());
|
||||
|
||||
@Test
|
||||
void constructorWhenServicePropertiesNullThenException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new CasGatewayResolverRequestMatcher(null))
|
||||
.withMessage("serviceProperties cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesWhenAlreadyGatewayedThenReturnsFalse() {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.getSession().setAttribute("_const_cas_gateway_", "yes");
|
||||
boolean matches = this.matcher.matches(request);
|
||||
assertThat(matches).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesWhenNotGatewayedThenReturnsTrue() {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
boolean matches = this.matcher.matches(request);
|
||||
assertThat(matches).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesWhenNoSessionThenReturnsTrue() {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setSession(null);
|
||||
boolean matches = this.matcher.matches(request);
|
||||
assertThat(matches).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesWhenNotGatewayedAndCheckedAgainThenSavesAsGatewayedAndReturnsFalse() {
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
boolean matches = this.matcher.matches(request);
|
||||
boolean secondMatch = this.matcher.matches(request);
|
||||
assertThat(matches).isTrue();
|
||||
assertThat(secondMatch).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,12 +6,6 @@ apply plugin: 'io.spring.convention.spring-module'
|
||||
apply plugin: 'trang'
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
configurations {
|
||||
opensaml5 {
|
||||
extendsFrom(optional, tests)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
management platform(project(":spring-security-dependencies"))
|
||||
// NB: Don't add other compile time dependencies to the config module as this breaks tooling
|
||||
@@ -21,11 +15,9 @@ dependencies {
|
||||
api 'org.springframework:spring-context'
|
||||
api 'org.springframework:spring-core'
|
||||
|
||||
optional project(':spring-security-data')
|
||||
optional project(':spring-security-ldap')
|
||||
optional project(':spring-security-messaging')
|
||||
optional project(path: ':spring-security-saml2-service-provider')
|
||||
opensaml5 project(path: ':spring-security-saml2-service-provider', configuration: 'opensamlFiveMain')
|
||||
optional project(':spring-security-saml2-service-provider')
|
||||
optional project(':spring-security-oauth2-client')
|
||||
optional project(':spring-security-oauth2-jose')
|
||||
optional project(':spring-security-oauth2-resource-server')
|
||||
@@ -43,7 +35,6 @@ dependencies {
|
||||
optional 'org.jetbrains.kotlin:kotlin-reflect'
|
||||
optional 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
||||
optional 'jakarta.annotation:jakarta.annotation-api'
|
||||
optional libs.webauthn4j.core
|
||||
|
||||
provided 'jakarta.servlet:jakarta.servlet-api'
|
||||
|
||||
@@ -74,7 +65,7 @@ dependencies {
|
||||
testImplementation 'jakarta.websocket:jakarta.websocket-api'
|
||||
testImplementation 'jakarta.websocket:jakarta.websocket-client-api'
|
||||
testImplementation 'ldapsdk:ldapsdk:4.1'
|
||||
testImplementation('org.htmlunit:htmlunit') {
|
||||
testImplementation('net.sourceforge.htmlunit:htmlunit') {
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
exclude group: 'xml-apis', module: 'xml-apis'
|
||||
}
|
||||
@@ -89,7 +80,7 @@ dependencies {
|
||||
testImplementation "org.hibernate.orm:hibernate-core"
|
||||
testImplementation 'org.hsqldb:hsqldb'
|
||||
testImplementation 'org.mockito:mockito-core'
|
||||
testImplementation('org.seleniumhq.selenium:htmlunit3-driver') {
|
||||
testImplementation('org.seleniumhq.selenium:htmlunit-driver') {
|
||||
exclude group: 'commons-logging', module: 'commons-logging'
|
||||
exclude group: 'xml-apis', module: 'xml-apis'
|
||||
}
|
||||
@@ -122,11 +113,8 @@ dependencies {
|
||||
exclude group: "org.slf4j", module: "jcl-over-slf4j"
|
||||
}
|
||||
testImplementation libs.org.instancio.instancio.junit
|
||||
testImplementation libs.org.eclipse.jetty.jetty.server
|
||||
testImplementation libs.org.eclipse.jetty.jetty.servlet
|
||||
|
||||
testRuntimeOnly 'org.hsqldb:hsqldb'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
def rncToXsd = tasks.named('rncToXsd', RncToXsd)
|
||||
@@ -172,26 +160,3 @@ configure(project.tasks.withType(Test)) {
|
||||
systemProperties['springSecurityVersion'] = version
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
onOutput { descriptor, event ->
|
||||
if (!project.hasProperty('serialization')) {
|
||||
return
|
||||
}
|
||||
if (descriptor.name=='listClassesMissingSerialVersion()') {
|
||||
logger.lifecycle(event.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("opensaml5Test", Test) {
|
||||
filter {
|
||||
includeTestsMatching "org.springframework.security.config.annotation.web.configurers.saml2.*"
|
||||
}
|
||||
useJUnitPlatform()
|
||||
classpath = sourceSets.main.output + sourceSets.test.output + configurations.opensaml5
|
||||
}
|
||||
|
||||
tasks.named("check") {
|
||||
dependsOn opensaml5Test
|
||||
}
|
||||
|
||||
-357
@@ -1,357 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.configurers;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
import org.assertj.core.api.AbstractStringAssert;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.WebDriverException;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.chrome.ChromeDriverService;
|
||||
import org.openqa.selenium.chrome.ChromeOptions;
|
||||
import org.openqa.selenium.chromium.HasCdp;
|
||||
import org.openqa.selenium.devtools.HasDevTools;
|
||||
import org.openqa.selenium.remote.Augmenter;
|
||||
import org.openqa.selenium.remote.RemoteWebDriver;
|
||||
import org.openqa.selenium.support.ui.FluentWait;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.mock.env.MockPropertySource;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
|
||||
import org.springframework.web.filter.DelegatingFilterProxy;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Webdriver-based tests for the WebAuthnConfigurer. This uses a full browser because
|
||||
* these features require Javascript and browser APIs to be available.
|
||||
*
|
||||
* @author Daniel Garnier-Moiroux
|
||||
*/
|
||||
@org.junit.jupiter.api.Disabled
|
||||
class WebAuthnWebDriverTests {
|
||||
|
||||
private String baseUrl;
|
||||
|
||||
private static ChromeDriverService driverService;
|
||||
|
||||
private Server server;
|
||||
|
||||
private RemoteWebDriver driver;
|
||||
|
||||
private static final String USERNAME = "user";
|
||||
|
||||
private static final String PASSWORD = "password";
|
||||
|
||||
@BeforeAll
|
||||
static void startChromeDriverService() throws Exception {
|
||||
driverService = new ChromeDriverService.Builder().usingAnyFreePort().build();
|
||||
driverService.start();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void stopChromeDriverService() {
|
||||
driverService.stop();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void startServer() throws Exception {
|
||||
// Create the server on port 8080
|
||||
this.server = new Server(0);
|
||||
|
||||
// Set up the ServletContextHandler
|
||||
ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
||||
contextHandler.setContextPath("/");
|
||||
this.server.setHandler(contextHandler);
|
||||
this.server.start();
|
||||
int serverPort = ((ServerConnector) this.server.getConnectors()[0]).getLocalPort();
|
||||
this.baseUrl = "http://localhost:" + serverPort;
|
||||
|
||||
// Set up Spring application context
|
||||
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
|
||||
applicationContext.register(WebAuthnConfiguration.class);
|
||||
applicationContext.setServletContext(contextHandler.getServletContext());
|
||||
|
||||
// Add the server port
|
||||
MockPropertySource propertySource = new MockPropertySource().withProperty("server.port", serverPort);
|
||||
applicationContext.getEnvironment().getPropertySources().addFirst(propertySource);
|
||||
|
||||
// Register the filter chain
|
||||
DelegatingFilterProxy filterProxy = new DelegatingFilterProxy("securityFilterChain", applicationContext);
|
||||
FilterHolder filterHolder = new FilterHolder(filterProxy);
|
||||
contextHandler.addFilter(filterHolder, "/*", null);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void stopServer() throws Exception {
|
||||
this.server.stop();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setupDriver() {
|
||||
ChromeOptions options = new ChromeOptions();
|
||||
options.addArguments("--headless=new");
|
||||
RemoteWebDriver baseDriver = new RemoteWebDriver(driverService.getUrl(), options);
|
||||
// Enable dev tools
|
||||
this.driver = (RemoteWebDriver) new Augmenter().augment(baseDriver);
|
||||
this.driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(1));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void cleanupDriver() {
|
||||
this.driver.quit();
|
||||
}
|
||||
|
||||
@Test
|
||||
void loginWhenNoValidAuthenticatorCredentialsThenRejects() {
|
||||
createVirtualAuthenticator(true);
|
||||
this.driver.get(this.baseUrl);
|
||||
this.driver.findElement(signinWithPasskeyButton()).click();
|
||||
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/login?error"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWhenNoLabelThenRejects() {
|
||||
login();
|
||||
|
||||
this.driver.get(this.baseUrl + "/webauthn/register");
|
||||
|
||||
this.driver.findElement(registerPasskeyButton()).click();
|
||||
assertHasAlertStartingWith("error", "Error: Passkey Label is required");
|
||||
}
|
||||
|
||||
@Test
|
||||
void registerWhenAuthenticatorNoUserVerificationThenRejects() {
|
||||
createVirtualAuthenticator(false);
|
||||
login();
|
||||
this.driver.get(this.baseUrl + "/webauthn/register");
|
||||
this.driver.findElement(passkeyLabel()).sendKeys("Virtual authenticator");
|
||||
this.driver.findElement(registerPasskeyButton()).click();
|
||||
|
||||
await(() -> assertHasAlertStartingWith("error",
|
||||
"Registration failed. Call to navigator.credentials.create failed:"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test in 4 steps to verify the end-to-end flow of registering an authenticator and
|
||||
* using it to register.
|
||||
* <ul>
|
||||
* <li>Step 1: Log in with username / password</li>
|
||||
* <li>Step 2: Register a credential from the virtual authenticator</li>
|
||||
* <li>Step 3: Log out</li>
|
||||
* <li>Step 4: Log in with the authenticator</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Test
|
||||
void loginWhenAuthenticatorRegisteredThenSuccess() {
|
||||
// Setup
|
||||
createVirtualAuthenticator(true);
|
||||
|
||||
// Step 1: log in with username / password
|
||||
login();
|
||||
|
||||
// Step 2: register a credential from the virtual authenticator
|
||||
this.driver.get(this.baseUrl + "/webauthn/register");
|
||||
this.driver.findElement(passkeyLabel()).sendKeys("Virtual authenticator");
|
||||
this.driver.findElement(registerPasskeyButton()).click();
|
||||
|
||||
// Ensure the page location has changed before performing further assertions.
|
||||
// This is required because the location change is asynchronously performed in
|
||||
// javascript, and performing assertions based on this.driver.findElement(...)
|
||||
// may result in a StaleElementReferenceException.
|
||||
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/webauthn/register?success"));
|
||||
await(() -> assertHasAlertStartingWith("success", "Success!"));
|
||||
|
||||
List<WebElement> passkeyRows = this.driver.findElements(passkeyTableRows());
|
||||
assertThat(passkeyRows).hasSize(1)
|
||||
.first()
|
||||
.extracting((row) -> row.findElement(firstCell()))
|
||||
.extracting(WebElement::getText)
|
||||
.isEqualTo("Virtual authenticator");
|
||||
|
||||
// Step 3: log out
|
||||
logout();
|
||||
|
||||
// Step 4: log in with the virtual authenticator
|
||||
this.driver.get(this.baseUrl + "/webauthn/register");
|
||||
this.driver.findElement(signinWithPasskeyButton()).click();
|
||||
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/webauthn/register?continue"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a virtual authenticator.
|
||||
* <p>
|
||||
* Note that Selenium docs for {@link HasCdp} strongly encourage to use
|
||||
* {@link HasDevTools} instead. However, devtools require more dependencies and
|
||||
* boilerplate, notably to sync the Devtools-CDP version with the current browser
|
||||
* version, whereas CDP runs out of the box.
|
||||
* <p>
|
||||
* @param userIsVerified whether the authenticator simulates user verification.
|
||||
* Setting it to false will make the ceremonies fail.
|
||||
* @see <a href=
|
||||
* "https://chromedevtools.github.io/devtools-protocol/tot/WebAuthn/">https://chromedevtools.github.io/devtools-protocol/tot/WebAuthn/</a>
|
||||
*/
|
||||
private void createVirtualAuthenticator(boolean userIsVerified) {
|
||||
HasCdp cdpDriver = (HasCdp) this.driver;
|
||||
cdpDriver.executeCdpCommand("WebAuthn.enable", Map.of("enableUI", false));
|
||||
// this.driver.addVirtualAuthenticator(createVirtualAuthenticatorOptions());
|
||||
//@formatter:off
|
||||
cdpDriver.executeCdpCommand("WebAuthn.addVirtualAuthenticator",
|
||||
Map.of(
|
||||
"options",
|
||||
Map.of(
|
||||
"protocol", "ctap2",
|
||||
"transport", "usb",
|
||||
"hasUserVerification", true,
|
||||
"hasResidentKey", true,
|
||||
"isUserVerified", userIsVerified,
|
||||
"automaticPresenceSimulation", true
|
||||
)
|
||||
));
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
private void login() {
|
||||
this.driver.get(this.baseUrl);
|
||||
this.driver.findElement(usernameField()).sendKeys(USERNAME);
|
||||
this.driver.findElement(passwordField()).sendKeys(PASSWORD);
|
||||
this.driver.findElement(signinWithUsernamePasswordButton()).click();
|
||||
}
|
||||
|
||||
private void logout() {
|
||||
this.driver.get(this.baseUrl + "/logout");
|
||||
this.driver.findElement(logoutButton()).click();
|
||||
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/login?logout"));
|
||||
}
|
||||
|
||||
private AbstractStringAssert<?> assertHasAlertStartingWith(String alertType, String alertMessage) {
|
||||
WebElement alert = this.driver.findElement(new By.ById(alertType));
|
||||
assertThat(alert.isDisplayed())
|
||||
.withFailMessage(
|
||||
() -> alertType + " alert was not displayed. Full page source:\n\n" + this.driver.getPageSource())
|
||||
.isTrue();
|
||||
|
||||
return assertThat(alert.getText()).startsWith(alertMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Await until the assertion passes. If the assertion fails, it will display the
|
||||
* assertion error in stdout. WebDriver-related exceptions are ignored, so that
|
||||
* {@code assertion}s can interact with the page and be retried on error, e.g.
|
||||
* {@code assertThat(this.driver.findElement(By.Id("some-id")).isNotNull()}.
|
||||
*/
|
||||
private void await(Supplier<AbstractAssert<?, ?>> assertion) {
|
||||
new FluentWait<>(this.driver).withTimeout(Duration.ofSeconds(2))
|
||||
.pollingEvery(Duration.ofMillis(100))
|
||||
.ignoring(AssertionError.class, WebDriverException.class)
|
||||
.until((d) -> {
|
||||
assertion.get();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private static By.ById passkeyLabel() {
|
||||
return new By.ById("label");
|
||||
}
|
||||
|
||||
private static By.ById registerPasskeyButton() {
|
||||
return new By.ById("register");
|
||||
}
|
||||
|
||||
private static By.ByCssSelector passkeyTableRows() {
|
||||
return new By.ByCssSelector("table > tbody > tr");
|
||||
}
|
||||
|
||||
private static By.ByCssSelector firstCell() {
|
||||
return new By.ByCssSelector("td:first-child");
|
||||
}
|
||||
|
||||
private static By.ById passwordField() {
|
||||
return new By.ById(PASSWORD);
|
||||
}
|
||||
|
||||
private static By.ById usernameField() {
|
||||
return new By.ById("username");
|
||||
}
|
||||
|
||||
private static By.ByCssSelector signinWithUsernamePasswordButton() {
|
||||
return new By.ByCssSelector("form > button[type=\"submit\"]");
|
||||
}
|
||||
|
||||
private static By.ById signinWithPasskeyButton() {
|
||||
return new By.ById("passkey-signin");
|
||||
}
|
||||
|
||||
private static By.ByCssSelector logoutButton() {
|
||||
return new By.ByCssSelector("button");
|
||||
}
|
||||
|
||||
/**
|
||||
* The configuration for WebAuthN tests. It accesses the Server's current port, so we
|
||||
* can configurer WebAuthnConfigurer#allowedOrigin
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebMvc
|
||||
@EnableWebSecurity
|
||||
static class WebAuthnConfiguration {
|
||||
|
||||
@Bean
|
||||
UserDetailsService userDetailsService() {
|
||||
return new InMemoryUserDetailsManager(
|
||||
User.withDefaultPasswordEncoder().username(USERNAME).password(PASSWORD).build());
|
||||
}
|
||||
|
||||
@Bean
|
||||
FilterChainProxy securityFilterChain(HttpSecurity http, Environment environment) throws Exception {
|
||||
SecurityFilterChain securityFilterChain = http
|
||||
.authorizeHttpRequests((auth) -> auth.anyRequest().authenticated())
|
||||
.formLogin(Customizer.withDefaults())
|
||||
.webAuthn((passkeys) -> passkeys.rpId("localhost")
|
||||
.rpName("Spring Security WebAuthN tests")
|
||||
.allowedOrigins("http://localhost:" + environment.getProperty("server.port")))
|
||||
.build();
|
||||
return new FilterChainProxy(securityFilterChain);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-200
@@ -1,200 +0,0 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.rsocket;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationHandler;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.rsocket.core.RSocketServer;
|
||||
import io.rsocket.frame.decoder.PayloadDecoder;
|
||||
import io.rsocket.metadata.WellKnownMimeType;
|
||||
import io.rsocket.transport.netty.server.CloseableChannel;
|
||||
import io.rsocket.transport.netty.server.TcpServerTransport;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
import org.springframework.messaging.rsocket.RSocketRequester;
|
||||
import org.springframework.messaging.rsocket.RSocketStrategies;
|
||||
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
|
||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;
|
||||
import org.springframework.security.rsocket.metadata.SimpleAuthenticationEncoder;
|
||||
import org.springframework.security.rsocket.metadata.UsernamePasswordMetadata;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.util.MimeTypeUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@ExtendWith(SpringExtension.class)
|
||||
public class HelloRSocketObservationITests {
|
||||
|
||||
@Autowired
|
||||
RSocketMessageHandler handler;
|
||||
|
||||
@Autowired
|
||||
SecuritySocketAcceptorInterceptor interceptor;
|
||||
|
||||
@Autowired
|
||||
ServerController controller;
|
||||
|
||||
@Autowired
|
||||
ObservationHandler<Observation.Context> observationHandler;
|
||||
|
||||
private CloseableChannel server;
|
||||
|
||||
private RSocketRequester requester;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
// @formatter:off
|
||||
this.server = RSocketServer.create()
|
||||
.payloadDecoder(PayloadDecoder.ZERO_COPY)
|
||||
.interceptors((registry) ->
|
||||
registry.forSocketAcceptor(this.interceptor)
|
||||
)
|
||||
.acceptor(this.handler.responder())
|
||||
.bind(TcpServerTransport.create("localhost", 0))
|
||||
.block();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dispose() {
|
||||
this.requester.rsocket().dispose();
|
||||
this.server.dispose();
|
||||
this.controller.payloads.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenUsingObservationRegistryThenObservesRequest() {
|
||||
UsernamePasswordMetadata credentials = new UsernamePasswordMetadata("rob", "password");
|
||||
// @formatter:off
|
||||
this.requester = RSocketRequester.builder()
|
||||
.setupMetadata(credentials, MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString()))
|
||||
.rsocketStrategies(this.handler.getRSocketStrategies())
|
||||
.connectTcp("localhost", this.server.address().getPort())
|
||||
.block();
|
||||
// @formatter:on
|
||||
String data = "rob";
|
||||
// @formatter:off
|
||||
this.requester.route("secure.retrieve-mono")
|
||||
.metadata(credentials, MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString()))
|
||||
.data(data)
|
||||
.retrieveMono(String.class)
|
||||
.block();
|
||||
// @formatter:on
|
||||
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
|
||||
verify(this.observationHandler, times(2)).onStart(captor.capture());
|
||||
Iterator<Observation.Context> contexts = captor.getAllValues().iterator();
|
||||
// once for setup
|
||||
assertThat(contexts.next().getName()).isEqualTo("spring.security.authentications");
|
||||
// once for request
|
||||
assertThat(contexts.next().getName()).isEqualTo("spring.security.authentications");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableRSocketSecurity
|
||||
static class Config {
|
||||
|
||||
private ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
|
||||
|
||||
@Bean
|
||||
ServerController controller() {
|
||||
return new ServerController();
|
||||
}
|
||||
|
||||
@Bean
|
||||
RSocketMessageHandler messageHandler() {
|
||||
RSocketMessageHandler handler = new RSocketMessageHandler();
|
||||
handler.setRSocketStrategies(rsocketStrategies());
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
RSocketStrategies rsocketStrategies() {
|
||||
return RSocketStrategies.builder().encoder(new SimpleAuthenticationEncoder()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
MapReactiveUserDetailsService uds() {
|
||||
// @formatter:off
|
||||
UserDetails rob = User.withDefaultPasswordEncoder()
|
||||
.username("rob")
|
||||
.password("password")
|
||||
.roles("USER", "ADMIN")
|
||||
.build();
|
||||
// @formatter:on
|
||||
return new MapReactiveUserDetailsService(rob);
|
||||
}
|
||||
|
||||
@Bean
|
||||
ObservationHandler<Observation.Context> observationHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ObservationRegistry observationRegistry() {
|
||||
given(this.handler.supportsContext(any())).willReturn(true);
|
||||
ObservationRegistry registry = ObservationRegistry.create();
|
||||
registry.observationConfig().observationHandler(this.handler);
|
||||
return registry;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Controller
|
||||
static class ServerController {
|
||||
|
||||
private List<String> payloads = new ArrayList<>();
|
||||
|
||||
@MessageMapping("**")
|
||||
String retrieveMono(String payload) {
|
||||
add(payload);
|
||||
return "Hi " + payload;
|
||||
}
|
||||
|
||||
private void add(String p) {
|
||||
this.payloads.add(p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-169
@@ -1,169 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.rsocket;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.rsocket.core.RSocketServer;
|
||||
import io.rsocket.exceptions.RejectedSetupException;
|
||||
import io.rsocket.frame.decoder.PayloadDecoder;
|
||||
import io.rsocket.transport.netty.server.CloseableChannel;
|
||||
import io.rsocket.transport.netty.server.TcpServerTransport;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
import org.springframework.messaging.rsocket.RSocketRequester;
|
||||
import org.springframework.messaging.rsocket.RSocketStrategies;
|
||||
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;
|
||||
import org.springframework.security.rsocket.metadata.BasicAuthenticationEncoder;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
*/
|
||||
@ContextConfiguration
|
||||
@ExtendWith(SpringExtension.class)
|
||||
public class HelloRSocketWithWebFluxITests {
|
||||
|
||||
@Autowired
|
||||
RSocketMessageHandler handler;
|
||||
|
||||
@Autowired
|
||||
SecuritySocketAcceptorInterceptor interceptor;
|
||||
|
||||
@Autowired
|
||||
ServerController controller;
|
||||
|
||||
private CloseableChannel server;
|
||||
|
||||
private RSocketRequester requester;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
// @formatter:off
|
||||
this.server = RSocketServer.create()
|
||||
.payloadDecoder(PayloadDecoder.ZERO_COPY)
|
||||
.interceptors((registry) ->
|
||||
registry.forSocketAcceptor(this.interceptor)
|
||||
)
|
||||
.acceptor(this.handler.responder())
|
||||
.bind(TcpServerTransport.create("localhost", 0))
|
||||
.block();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void dispose() {
|
||||
this.requester.rsocket().dispose();
|
||||
this.server.dispose();
|
||||
this.controller.payloads.clear();
|
||||
}
|
||||
|
||||
// gh-16161
|
||||
@Test
|
||||
public void retrieveMonoWhenSecureThenDenied() {
|
||||
// @formatter:off
|
||||
this.requester = RSocketRequester.builder()
|
||||
.rsocketStrategies(this.handler.getRSocketStrategies())
|
||||
.connectTcp("localhost", this.server.address().getPort())
|
||||
.block();
|
||||
// @formatter:on
|
||||
String data = "rob";
|
||||
// @formatter:off
|
||||
assertThatExceptionOfType(Exception.class).isThrownBy(
|
||||
() -> this.requester.route("secure.retrieve-mono")
|
||||
.data(data)
|
||||
.retrieveMono(String.class)
|
||||
.block()
|
||||
)
|
||||
.matches((ex) -> ex instanceof RejectedSetupException
|
||||
|| ex.getClass().toString().contains("ReactiveException"));
|
||||
// @formatter:on
|
||||
assertThat(this.controller.payloads).isEmpty();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableRSocketSecurity
|
||||
@EnableWebFluxSecurity
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
ServerController controller() {
|
||||
return new ServerController();
|
||||
}
|
||||
|
||||
@Bean
|
||||
RSocketMessageHandler messageHandler() {
|
||||
RSocketMessageHandler handler = new RSocketMessageHandler();
|
||||
handler.setRSocketStrategies(rsocketStrategies());
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
RSocketStrategies rsocketStrategies() {
|
||||
return RSocketStrategies.builder().encoder(new BasicAuthenticationEncoder()).build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
MapReactiveUserDetailsService uds() {
|
||||
// @formatter:off
|
||||
UserDetails rob = User.withDefaultPasswordEncoder()
|
||||
.username("rob")
|
||||
.password("password")
|
||||
.roles("USER", "ADMIN")
|
||||
.build();
|
||||
// @formatter:on
|
||||
return new MapReactiveUserDetailsService(rob);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Controller
|
||||
static class ServerController {
|
||||
|
||||
private List<String> payloads = new ArrayList<>();
|
||||
|
||||
@MessageMapping("**")
|
||||
String retrieveMono(String payload) {
|
||||
add(payload);
|
||||
return "Hi " + payload;
|
||||
}
|
||||
|
||||
private void add(String p) {
|
||||
this.payloads.add(p);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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;
|
||||
|
||||
import org.springframework.beans.factory.Aware;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* Allows initialization of Objects. Typically this is used to call the {@link Aware}
|
||||
* methods, {@link InitializingBean#afterPropertiesSet()}, and ensure that
|
||||
* {@link DisposableBean#destroy()} has been invoked.
|
||||
*
|
||||
* @param <T> the bound of the types of Objects this {@link ObjectPostProcessor} supports.
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
*/
|
||||
public interface ObjectPostProcessor<T> {
|
||||
|
||||
static <S> ObjectPostProcessor<S> identity() {
|
||||
return new ObjectPostProcessor<>() {
|
||||
@Override
|
||||
public <O extends S> O postProcess(O object) {
|
||||
return object;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the object possibly returning a modified instance that should be used
|
||||
* instead.
|
||||
* @param object the object to initialize
|
||||
* @return the initialized version of the object
|
||||
*/
|
||||
<O extends T> O postProcess(O object);
|
||||
|
||||
}
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2009-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.
|
||||
@@ -96,7 +96,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
|
||||
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 6.4. Please update your schema declarations to the 6.4 schema.",
|
||||
+ "with Spring Security 6.2. Please update your schema declarations to the 6.2 schema.",
|
||||
element);
|
||||
}
|
||||
String name = pc.getDelegate().getLocalName(element);
|
||||
@@ -221,7 +221,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-6\\.4.*.xsd.*")
|
||||
return schemaLocation.matches("(?m).*spring-security-6\\.2.*.xsd.*")
|
||||
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|
||||
|| !schemaLocation.matches("(?m).*spring-security.*");
|
||||
}
|
||||
|
||||
+1
-23
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -28,7 +28,6 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.builders.WebSecurity;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.filter.DelegatingFilterProxy;
|
||||
@@ -79,15 +78,6 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
|
||||
this(objectPostProcessor, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated(since = "6.4", forRemoval = true)
|
||||
protected AbstractConfiguredSecurityBuilder(
|
||||
org.springframework.security.config.annotation.ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
this(objectPostProcessor, false);
|
||||
}
|
||||
|
||||
/***
|
||||
* Creates a new instance with the provided {@link ObjectPostProcessor}. This post
|
||||
* processor must support Object since there are many types of objects that may be
|
||||
@@ -103,18 +93,6 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
|
||||
this.allowConfigurersOfSameType = allowConfigurersOfSameType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated(since = "6.4", forRemoval = true)
|
||||
protected AbstractConfiguredSecurityBuilder(
|
||||
org.springframework.security.config.annotation.ObjectPostProcessor<Object> objectPostProcessor,
|
||||
boolean allowConfigurersOfSameType) {
|
||||
Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
|
||||
this.objectPostProcessor = objectPostProcessor;
|
||||
this.allowConfigurersOfSameType = allowConfigurersOfSameType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link #build()} and {@link #getObject()} but checks the state to
|
||||
* determine if {@link #build()} needs to be called first.
|
||||
|
||||
+1
-4
@@ -28,11 +28,8 @@ import org.springframework.beans.factory.InitializingBean;
|
||||
* @param <T> the bound of the types of Objects this {@link ObjectPostProcessor} supports.
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @deprecated please use {@link org.springframework.security.config.ObjectPostProcessor}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public interface ObjectPostProcessor<T> extends org.springframework.security.config.ObjectPostProcessor<T> {
|
||||
public interface ObjectPostProcessor<T> {
|
||||
|
||||
/**
|
||||
* Initialize the object possibly returning a modified instance that should be used
|
||||
|
||||
+1
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,7 +21,6 @@ import java.util.List;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -92,15 +91,6 @@ public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
|
||||
this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated(since = "6.4", forRemoval = true)
|
||||
public void addObjectPostProcessor(
|
||||
org.springframework.security.config.annotation.ObjectPostProcessor<?> objectPostProcessor) {
|
||||
this.objectPostProcessor.addObjectPostProcessor(objectPostProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link SecurityBuilder} to be used. This is automatically set when using
|
||||
* {@link AbstractConfiguredSecurityBuilder#apply(SecurityConfigurerAdapter)}
|
||||
|
||||
+3
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,8 +26,8 @@ import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.ProviderManager;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
|
||||
@@ -67,23 +67,12 @@ public class AuthenticationManagerBuilder
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param objectPostProcessor the
|
||||
* {@link org.springframework.security.config.annotation.ObjectPostProcessor} instance
|
||||
* to use.
|
||||
* @param objectPostProcessor the {@link ObjectPostProcessor} instance to use.
|
||||
*/
|
||||
public AuthenticationManagerBuilder(ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
super(objectPostProcessor, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated(since = "6.4", forRemoval = true)
|
||||
public AuthenticationManagerBuilder(
|
||||
org.springframework.security.config.annotation.ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
super(objectPostProcessor, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows providing a parent {@link AuthenticationManager} that will be tried if this
|
||||
* {@link AuthenticationManager} was unable to attempt to authenticate the provided
|
||||
|
||||
+18
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -28,6 +28,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.aop.framework.ProxyFactoryBean;
|
||||
import org.springframework.aop.target.LazyInitTargetSource;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
@@ -39,7 +40,7 @@ import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
|
||||
import org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;
|
||||
@@ -56,7 +57,6 @@ import org.springframework.util.Assert;
|
||||
* Exports the authentication {@link Configuration}
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Ngoc Nhan
|
||||
* @since 3.2
|
||||
*
|
||||
*/
|
||||
@@ -197,6 +197,15 @@ public class AuthenticationConfiguration {
|
||||
return lazyBean(AuthenticationManager.class);
|
||||
}
|
||||
|
||||
private static <T> T getBeanOrNull(ApplicationContext applicationContext, Class<T> type) {
|
||||
try {
|
||||
return applicationContext.getBean(type);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException notFound) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class EnableGlobalAuthenticationAutowiredConfigurer extends GlobalAuthenticationConfigurerAdapter {
|
||||
|
||||
private final ApplicationContext context;
|
||||
@@ -321,9 +330,12 @@ public class AuthenticationConfiguration {
|
||||
if (this.passwordEncoder != null) {
|
||||
return this.passwordEncoder;
|
||||
}
|
||||
this.passwordEncoder = this.applicationContext.getBeanProvider(PasswordEncoder.class)
|
||||
.getIfUnique(PasswordEncoderFactories::createDelegatingPasswordEncoder);
|
||||
return this.passwordEncoder;
|
||||
PasswordEncoder passwordEncoder = getBeanOrNull(this.applicationContext, PasswordEncoder.class);
|
||||
if (passwordEncoder == null) {
|
||||
passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
}
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
return passwordEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+16
-24
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -16,14 +16,8 @@
|
||||
|
||||
package org.springframework.security.config.annotation.authentication.configuration;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
|
||||
@@ -55,31 +49,29 @@ class InitializeAuthenticationProviderBeanManagerConfigurer extends GlobalAuthen
|
||||
|
||||
class InitializeAuthenticationProviderManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Override
|
||||
public void configure(AuthenticationManagerBuilder auth) {
|
||||
if (auth.isConfigured()) {
|
||||
return;
|
||||
}
|
||||
String[] beanNames = InitializeAuthenticationProviderBeanManagerConfigurer.this.context
|
||||
.getBeanNamesForType(AuthenticationProvider.class);
|
||||
if (beanNames.length == 0) {
|
||||
AuthenticationProvider authenticationProvider = getBeanOrNull(AuthenticationProvider.class);
|
||||
if (authenticationProvider == null) {
|
||||
return;
|
||||
}
|
||||
else if (beanNames.length > 1) {
|
||||
this.logger.info(LogMessage.format("Found %s AuthenticationProvider beans, with names %s. "
|
||||
+ "Global Authentication Manager will not be configured with AuthenticationProviders. "
|
||||
+ "Consider publishing a single AuthenticationProvider bean, or wiring your Providers directly "
|
||||
+ "using the DSL.", beanNames.length, Arrays.toString(beanNames)));
|
||||
return;
|
||||
}
|
||||
AuthenticationProvider authenticationProvider = InitializeAuthenticationProviderBeanManagerConfigurer.this.context
|
||||
.getBean(beanNames[0], AuthenticationProvider.class);
|
||||
auth.authenticationProvider(authenticationProvider);
|
||||
this.logger.info(LogMessage.format(
|
||||
"Global AuthenticationManager configured with AuthenticationProvider bean with name %s",
|
||||
beanNames[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a bean of the requested class if there's just a single registered
|
||||
* component, null otherwise.
|
||||
*/
|
||||
private <T> T getBeanOrNull(Class<T> type) {
|
||||
String[] beanNames = InitializeAuthenticationProviderBeanManagerConfigurer.this.context
|
||||
.getBeanNamesForType(type);
|
||||
if (beanNames.length != 1) {
|
||||
return null;
|
||||
}
|
||||
return InitializeAuthenticationProviderBeanManagerConfigurer.this.context.getBean(beanNames[0], type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-46
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -16,17 +16,10 @@
|
||||
|
||||
package org.springframework.security.config.annotation.authentication.configuration;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.authentication.password.CompromisedPasswordChecker;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
@@ -38,7 +31,6 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
* {@link PasswordEncoder} is defined will wire this up too.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Ngoc Nhan
|
||||
* @since 4.1
|
||||
*/
|
||||
@Order(InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER)
|
||||
@@ -62,57 +54,27 @@ class InitializeUserDetailsBeanManagerConfigurer extends GlobalAuthenticationCon
|
||||
|
||||
class InitializeUserDetailsManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Override
|
||||
public void configure(AuthenticationManagerBuilder auth) throws Exception {
|
||||
String[] beanNames = InitializeUserDetailsBeanManagerConfigurer.this.context
|
||||
.getBeanNamesForType(UserDetailsService.class);
|
||||
if (auth.isConfigured()) {
|
||||
if (beanNames.length > 0) {
|
||||
this.logger.warn("Global AuthenticationManager configured with an AuthenticationProvider bean. "
|
||||
+ "UserDetailsService beans will not be used by Spring Security for automatically configuring username/password login. "
|
||||
+ "Consider removing the AuthenticationProvider bean. "
|
||||
+ "Alternatively, consider using the UserDetailsService in a manually instantiated DaoAuthenticationProvider. "
|
||||
+ "If the current configuration is intentional, to turn off this warning, "
|
||||
+ "increase the logging level of 'org.springframework.security.config.annotation.authentication.configuration.InitializeUserDetailsBeanManagerConfigurer' to ERROR");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (beanNames.length == 0) {
|
||||
UserDetailsService userDetailsService = getBeanOrNull(UserDetailsService.class);
|
||||
if (userDetailsService == null) {
|
||||
return;
|
||||
}
|
||||
else if (beanNames.length > 1) {
|
||||
this.logger.warn(LogMessage.format("Found %s UserDetailsService beans, with names %s. "
|
||||
+ "Global Authentication Manager will not use a UserDetailsService for username/password login. "
|
||||
+ "Consider publishing a single UserDetailsService bean.", beanNames.length,
|
||||
Arrays.toString(beanNames)));
|
||||
return;
|
||||
}
|
||||
UserDetailsService userDetailsService = InitializeUserDetailsBeanManagerConfigurer.this.context
|
||||
.getBean(beanNames[0], UserDetailsService.class);
|
||||
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
|
||||
UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
|
||||
CompromisedPasswordChecker passwordChecker = getBeanOrNull(CompromisedPasswordChecker.class);
|
||||
DaoAuthenticationProvider provider;
|
||||
if (passwordEncoder != null) {
|
||||
provider = new DaoAuthenticationProvider(passwordEncoder);
|
||||
}
|
||||
else {
|
||||
provider = new DaoAuthenticationProvider();
|
||||
}
|
||||
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
|
||||
provider.setUserDetailsService(userDetailsService);
|
||||
if (passwordEncoder != null) {
|
||||
provider.setPasswordEncoder(passwordEncoder);
|
||||
}
|
||||
if (passwordManager != null) {
|
||||
provider.setUserDetailsPasswordService(passwordManager);
|
||||
}
|
||||
if (passwordChecker != null) {
|
||||
provider.setCompromisedPasswordChecker(passwordChecker);
|
||||
}
|
||||
provider.afterPropertiesSet();
|
||||
auth.authenticationProvider(provider);
|
||||
this.logger.info(LogMessage.format(
|
||||
"Global AuthenticationManager configured with UserDetailsService bean with name %s", beanNames[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,7 +82,11 @@ class InitializeUserDetailsBeanManagerConfigurer extends GlobalAuthenticationCon
|
||||
* component, null otherwise.
|
||||
*/
|
||||
private <T> T getBeanOrNull(Class<T> type) {
|
||||
return InitializeUserDetailsBeanManagerConfigurer.this.context.getBeanProvider(type).getIfUnique();
|
||||
String[] beanNames = InitializeUserDetailsBeanManagerConfigurer.this.context.getBeanNamesForType(type);
|
||||
if (beanNames.length != 1) {
|
||||
return null;
|
||||
}
|
||||
return InitializeUserDetailsBeanManagerConfigurer.this.context.getBean(beanNames[0], type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -22,7 +22,7 @@ import java.net.ServerSocket;
|
||||
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer;
|
||||
@@ -140,16 +140,6 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated(since = "6.4", forRemoval = true)
|
||||
public LdapAuthenticationProviderConfigurer<B> withObjectPostProcessor(
|
||||
org.springframework.security.config.annotation.ObjectPostProcessor<?> objectPostProcessor) {
|
||||
addObjectPostProcessor(objectPostProcessor);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link LdapAuthoritiesPopulator} and defaults to
|
||||
* {@link DefaultLdapAuthoritiesPopulator}
|
||||
|
||||
+2
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -17,7 +17,7 @@
|
||||
package org.springframework.security.config.annotation.authentication.configurers.userdetails;
|
||||
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.authentication.ProviderManagerBuilder;
|
||||
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
|
||||
@@ -63,17 +63,6 @@ public abstract class AbstractDaoAuthenticationConfigurer<B extends ProviderMana
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated(since = "6.4", forRemoval = true)
|
||||
@SuppressWarnings("unchecked")
|
||||
public C withObjectPostProcessor(
|
||||
org.springframework.security.config.annotation.ObjectPostProcessor<?> objectPostProcessor) {
|
||||
addObjectPostProcessor(objectPostProcessor);
|
||||
return (C) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows specifying the {@link PasswordEncoder} to use with the
|
||||
* {@link DaoAuthenticationProvider}. The default is to use plain text.
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||
import org.springframework.core.NativeDetector;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
|
||||
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
|
||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.security.aot.hint.AuthorizeReturnObjectCoreHintsRegistrar;
|
||||
import org.springframework.security.aot.hint.SecurityHintsRegistrar;
|
||||
import org.springframework.security.authorization.AuthorizationProxyFactory;
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisor;
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
|
||||
import org.springframework.security.authorization.method.AuthorizeReturnObjectMethodInterceptor;
|
||||
import org.springframework.security.config.Customizer;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
final class AuthorizationProxyConfiguration implements AopInfrastructureBean {
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static AuthorizationAdvisorProxyFactory authorizationProxyFactory(
|
||||
ObjectProvider<Customizer<AuthorizationAdvisorProxyFactory>> customizers) {
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory(new ArrayList<>());
|
||||
customizers.forEach((c) -> c.customize(factory));
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static MethodInterceptor authorizeReturnObjectMethodInterceptor(ObjectProvider<AuthorizationAdvisor> provider,
|
||||
AuthorizationAdvisorProxyFactory authorizationProxyFactory) {
|
||||
provider.forEach(authorizationProxyFactory::addAdvisor);
|
||||
AuthorizeReturnObjectMethodInterceptor interceptor = new AuthorizeReturnObjectMethodInterceptor(
|
||||
authorizationProxyFactory);
|
||||
authorizationProxyFactory.addAdvisor(interceptor);
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static SecurityHintsRegistrar authorizeReturnObjectHintsRegistrar(AuthorizationProxyFactory proxyFactory) {
|
||||
return new AuthorizeReturnObjectCoreHintsRegistrar(proxyFactory);
|
||||
}
|
||||
|
||||
}
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.security.aot.hint.SecurityHintsRegistrar;
|
||||
import org.springframework.security.authorization.AuthorizationProxyFactory;
|
||||
import org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
final class AuthorizationProxyDataConfiguration implements AopInfrastructureBean {
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static SecurityHintsRegistrar authorizeReturnObjectDataHintsRegistrar(AuthorizationProxyFactory proxyFactory) {
|
||||
return new AuthorizeReturnObjectDataHintsRegistrar(proxyFactory);
|
||||
}
|
||||
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisor;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
|
||||
final class DeferringMethodInterceptor<M extends AuthorizationAdvisor> implements AuthorizationAdvisor {
|
||||
|
||||
private final Pointcut pointcut;
|
||||
|
||||
private final Supplier<M> delegate;
|
||||
|
||||
DeferringMethodInterceptor(Pointcut pointcut, Supplier<M> delegate) {
|
||||
this.pointcut = pointcut;
|
||||
this.delegate = SingletonSupplier.of(delegate);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
|
||||
return this.delegate.get().invoke(invocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pointcut getPointcut() {
|
||||
return this.pointcut;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Advice getAdvice() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.delegate.get().getOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPerInstance() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-23
@@ -19,26 +19,18 @@ package org.springframework.security.config.annotation.method.configuration;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationResult;
|
||||
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
|
||||
final class DeferringObservationAuthorizationManager<T>
|
||||
implements AuthorizationManager<T>, MethodAuthorizationDeniedHandler {
|
||||
final class DeferringObservationAuthorizationManager<T> implements AuthorizationManager<T> {
|
||||
|
||||
private final Supplier<AuthorizationManager<T>> delegate;
|
||||
|
||||
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
|
||||
|
||||
DeferringObservationAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
|
||||
AuthorizationManager<T> delegate) {
|
||||
this.delegate = SingletonSupplier.of(() -> {
|
||||
@@ -48,9 +40,6 @@ final class DeferringObservationAuthorizationManager<T>
|
||||
}
|
||||
return new ObservationAuthorizationManager<>(registry, delegate);
|
||||
});
|
||||
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
|
||||
this.handler = h;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,15 +47,4 @@ final class DeferringObservationAuthorizationManager<T>
|
||||
return this.delegate.get().check(authentication, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||
AuthorizationResult authorizationResult) {
|
||||
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-23
@@ -19,27 +19,19 @@ package org.springframework.security.config.annotation.method.configuration;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationResult;
|
||||
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
|
||||
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedHandler;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
|
||||
final class DeferringObservationReactiveAuthorizationManager<T>
|
||||
implements ReactiveAuthorizationManager<T>, MethodAuthorizationDeniedHandler {
|
||||
final class DeferringObservationReactiveAuthorizationManager<T> implements ReactiveAuthorizationManager<T> {
|
||||
|
||||
private final Supplier<ReactiveAuthorizationManager<T>> delegate;
|
||||
|
||||
private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();
|
||||
|
||||
DeferringObservationReactiveAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
|
||||
ReactiveAuthorizationManager<T> delegate) {
|
||||
this.delegate = SingletonSupplier.of(() -> {
|
||||
@@ -49,9 +41,6 @@ final class DeferringObservationReactiveAuthorizationManager<T>
|
||||
}
|
||||
return new ObservationReactiveAuthorizationManager<>(registry, delegate);
|
||||
});
|
||||
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
|
||||
this.handler = h;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,15 +48,4 @@ final class DeferringObservationReactiveAuthorizationManager<T>
|
||||
return this.delegate.get().check(authentication, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||
AuthorizationResult authorizationResult) {
|
||||
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -87,14 +87,4 @@ public @interface EnableMethodSecurity {
|
||||
*/
|
||||
AdviceMode mode() default AdviceMode.PROXY;
|
||||
|
||||
/**
|
||||
* Indicate additional offset in the ordering of the execution of the security
|
||||
* interceptors when multiple advices are applied at a specific joinpoint. I.e.,
|
||||
* precedence of each security interceptor enabled by this annotation will be
|
||||
* calculated as sum of its default precedence and offset. The default is 0.
|
||||
* @return the offset in the order the security advisor should be applied
|
||||
* @since 6.3
|
||||
*/
|
||||
int offset() default 0;
|
||||
|
||||
}
|
||||
|
||||
+17
-22
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -27,6 +27,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
@@ -68,7 +69,7 @@ import org.springframework.security.access.vote.RoleVoter;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
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.core.GrantedAuthorityDefaults;
|
||||
@@ -83,7 +84,6 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Eddú Meléndez
|
||||
* @author Ngoc Nhan
|
||||
* @since 3.2
|
||||
* @see EnableGlobalMethodSecurity
|
||||
* @deprecated Use {@link PrePostMethodSecurityConfiguration},
|
||||
@@ -97,7 +97,7 @@ public class GlobalMethodSecurityConfiguration implements ImportAware, SmartInit
|
||||
|
||||
private static final Log logger = LogFactory.getLog(GlobalMethodSecurityConfiguration.class);
|
||||
|
||||
private ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<>() {
|
||||
private ObjectPostProcessor<Object> objectPostProcessor = new ObjectPostProcessor<Object>() {
|
||||
|
||||
@Override
|
||||
public <T> T postProcess(T object) {
|
||||
@@ -168,19 +168,19 @@ public class GlobalMethodSecurityConfiguration implements ImportAware, SmartInit
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
PermissionEvaluator permissionEvaluator = getBeanOrNull(PermissionEvaluator.class);
|
||||
PermissionEvaluator permissionEvaluator = getSingleBeanOrNull(PermissionEvaluator.class);
|
||||
if (permissionEvaluator != null) {
|
||||
this.defaultMethodExpressionHandler.setPermissionEvaluator(permissionEvaluator);
|
||||
}
|
||||
RoleHierarchy roleHierarchy = getBeanOrNull(RoleHierarchy.class);
|
||||
RoleHierarchy roleHierarchy = getSingleBeanOrNull(RoleHierarchy.class);
|
||||
if (roleHierarchy != null) {
|
||||
this.defaultMethodExpressionHandler.setRoleHierarchy(roleHierarchy);
|
||||
}
|
||||
AuthenticationTrustResolver trustResolver = getBeanOrNull(AuthenticationTrustResolver.class);
|
||||
AuthenticationTrustResolver trustResolver = getSingleBeanOrNull(AuthenticationTrustResolver.class);
|
||||
if (trustResolver != null) {
|
||||
this.defaultMethodExpressionHandler.setTrustResolver(trustResolver);
|
||||
}
|
||||
GrantedAuthorityDefaults grantedAuthorityDefaults = getBeanOrNull(GrantedAuthorityDefaults.class);
|
||||
GrantedAuthorityDefaults grantedAuthorityDefaults = getSingleBeanOrNull(GrantedAuthorityDefaults.class);
|
||||
if (grantedAuthorityDefaults != null) {
|
||||
this.defaultMethodExpressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
|
||||
}
|
||||
@@ -188,8 +188,13 @@ public class GlobalMethodSecurityConfiguration implements ImportAware, SmartInit
|
||||
this.defaultMethodExpressionHandler = this.objectPostProcessor.postProcess(this.defaultMethodExpressionHandler);
|
||||
}
|
||||
|
||||
private <T> T getBeanOrNull(Class<T> type) {
|
||||
return this.context.getBeanProvider(type).getIfUnique();
|
||||
private <T> T getSingleBeanOrNull(Class<T> type) {
|
||||
try {
|
||||
return this.context.getBean(type);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void initializeMethodSecurityInterceptor() throws Exception {
|
||||
@@ -257,7 +262,7 @@ public class GlobalMethodSecurityConfiguration implements ImportAware, SmartInit
|
||||
decisionVoters.add(new Jsr250Voter());
|
||||
}
|
||||
RoleVoter roleVoter = new RoleVoter();
|
||||
GrantedAuthorityDefaults grantedAuthorityDefaults = getBeanOrNull(GrantedAuthorityDefaults.class);
|
||||
GrantedAuthorityDefaults grantedAuthorityDefaults = getSingleBeanOrNull(GrantedAuthorityDefaults.class);
|
||||
if (grantedAuthorityDefaults != null) {
|
||||
roleVoter.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());
|
||||
}
|
||||
@@ -368,7 +373,7 @@ public class GlobalMethodSecurityConfiguration implements ImportAware, SmartInit
|
||||
sources.add(new SecuredAnnotationSecurityMetadataSource());
|
||||
}
|
||||
if (isJsr250Enabled) {
|
||||
GrantedAuthorityDefaults grantedAuthorityDefaults = getBeanOrNull(GrantedAuthorityDefaults.class);
|
||||
GrantedAuthorityDefaults grantedAuthorityDefaults = getSingleBeanOrNull(GrantedAuthorityDefaults.class);
|
||||
Jsr250MethodSecurityMetadataSource jsr250MethodSecurityMetadataSource = this.context
|
||||
.getBean(Jsr250MethodSecurityMetadataSource.class);
|
||||
if (grantedAuthorityDefaults != null) {
|
||||
@@ -407,16 +412,6 @@ public class GlobalMethodSecurityConfiguration implements ImportAware, SmartInit
|
||||
this.objectPostProcessor = objectPostProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated(since = "6.4", forRemoval = true)
|
||||
@Autowired(required = false)
|
||||
public void setObjectPostProcessor(
|
||||
org.springframework.security.config.annotation.ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
this.objectPostProcessor = objectPostProcessor;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setMethodSecurityExpressionHandler(List<MethodSecurityExpressionHandler> handlers) {
|
||||
if (handlers.size() != 1) {
|
||||
|
||||
+18
-62
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 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,29 +16,20 @@
|
||||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||
import org.springframework.security.authorization.AuthoritiesAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.Jsr250AuthorizationManager;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
|
||||
/**
|
||||
@@ -49,61 +40,26 @@ import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
* @since 5.6
|
||||
* @see EnableMethodSecurity
|
||||
*/
|
||||
@Configuration(value = "_jsr250MethodSecurityConfiguration", proxyBeanMethods = false)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
final class Jsr250MethodSecurityConfiguration implements ImportAware, AopInfrastructureBean {
|
||||
|
||||
private static final Pointcut pointcut = AuthorizationManagerBeforeMethodInterceptor.jsr250().getPointcut();
|
||||
|
||||
private final Jsr250AuthorizationManager authorizationManager = new Jsr250AuthorizationManager();
|
||||
|
||||
private final AuthorizationManagerBeforeMethodInterceptor methodInterceptor;
|
||||
|
||||
Jsr250MethodSecurityConfiguration(
|
||||
ObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocation>>> postProcessors) {
|
||||
ObjectPostProcessor<AuthorizationManager<MethodInvocation>> postProcessor = postProcessors
|
||||
.getIfUnique(ObjectPostProcessor::identity);
|
||||
AuthorizationManager<MethodInvocation> manager = postProcessor.postProcess(this.authorizationManager);
|
||||
this.methodInterceptor = AuthorizationManagerBeforeMethodInterceptor.jsr250(manager);
|
||||
}
|
||||
final class Jsr250MethodSecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static MethodInterceptor jsr250AuthorizationMethodInterceptor(
|
||||
ObjectProvider<Jsr250MethodSecurityConfiguration> _jsr250MethodSecurityConfiguration) {
|
||||
Supplier<AuthorizationManagerBeforeMethodInterceptor> supplier = () -> {
|
||||
Jsr250MethodSecurityConfiguration configuration = _jsr250MethodSecurityConfiguration.getObject();
|
||||
return configuration.methodInterceptor;
|
||||
};
|
||||
return new DeferringMethodInterceptor<>(pointcut, supplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
EnableMethodSecurity annotation = importMetadata.getAnnotations().get(EnableMethodSecurity.class).synthesize();
|
||||
this.methodInterceptor.setOrder(this.methodInterceptor.getOrder() + annotation.offset());
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setGrantedAuthorityDefaults(GrantedAuthorityDefaults defaults) {
|
||||
this.authorizationManager.setRolePrefix(defaults.getRolePrefix());
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setRoleHierarchy(RoleHierarchy roleHierarchy) {
|
||||
AuthoritiesAuthorizationManager authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager();
|
||||
authoritiesAuthorizationManager.setRoleHierarchy(roleHierarchy);
|
||||
this.authorizationManager.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
|
||||
this.methodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setEventPublisher(AuthorizationEventPublisher eventPublisher) {
|
||||
this.methodInterceptor.setAuthorizationEventPublisher(eventPublisher);
|
||||
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
|
||||
ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
|
||||
ObjectProvider<ObservationRegistry> registryProvider) {
|
||||
Jsr250AuthorizationManager jsr250 = new Jsr250AuthorizationManager();
|
||||
defaultsProvider.ifAvailable((d) -> jsr250.setRolePrefix(d.getRolePrefix()));
|
||||
SecurityContextHolderStrategy strategy = strategyProvider
|
||||
.getIfAvailable(SecurityContextHolder::getContextHolderStrategy);
|
||||
AuthorizationManager<MethodInvocation> manager = new DeferringObservationAuthorizationManager<>(
|
||||
registryProvider, jsr250);
|
||||
AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor
|
||||
.jsr250(manager);
|
||||
interceptor.setSecurityContextHolderStrategy(strategy);
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
class MethodObservationConfiguration {
|
||||
|
||||
private static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()
|
||||
.shouldObserveRequests(true)
|
||||
.shouldObserveAuthentications(true)
|
||||
.shouldObserveAuthorizations(true)
|
||||
.build();
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static ObjectPostProcessor<AuthorizationManager<MethodInvocation>> methodAuthorizationManagerPostProcessor(
|
||||
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||
return new ObjectPostProcessor<>() {
|
||||
@Override
|
||||
public AuthorizationManager postProcess(AuthorizationManager object) {
|
||||
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();
|
||||
return active ? new ObservationAuthorizationManager<>(r, object) : object;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static ObjectPostProcessor<AuthorizationManager<MethodInvocationResult>> methodResultAuthorizationManagerPostProcessor(
|
||||
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||
return new ObjectPostProcessor<>() {
|
||||
@Override
|
||||
public AuthorizationManager postProcess(AuthorizationManager object) {
|
||||
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();
|
||||
return active ? new ObservationAuthorizationManager<>(r, object) : object;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
+2
-55
@@ -16,24 +16,12 @@
|
||||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.PointcutAdvisor;
|
||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisor;
|
||||
|
||||
class MethodSecurityAdvisorRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
|
||||
@@ -45,7 +33,6 @@ class MethodSecurityAdvisorRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
registerAsAdvisor("postAuthorizeAuthorization", registry);
|
||||
registerAsAdvisor("securedAuthorization", registry);
|
||||
registerAsAdvisor("jsr250Authorization", registry);
|
||||
registerAsAdvisor("authorizeReturnObject", registry);
|
||||
}
|
||||
|
||||
private void registerAsAdvisor(String prefix, BeanDefinitionRegistry registry) {
|
||||
@@ -61,49 +48,9 @@ class MethodSecurityAdvisorRegistrar implements ImportBeanDefinitionRegistrar {
|
||||
if (!(definition instanceof RootBeanDefinition)) {
|
||||
return;
|
||||
}
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(AdvisorWrapper.class);
|
||||
builder.setFactoryMethod("of");
|
||||
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
builder.addConstructorArgReference(interceptorName);
|
||||
RootBeanDefinition advisor = (RootBeanDefinition) builder.getBeanDefinition();
|
||||
RootBeanDefinition advisor = new RootBeanDefinition((RootBeanDefinition) definition);
|
||||
advisor.setTargetType(Advisor.class);
|
||||
registry.registerBeanDefinition(advisorName, advisor);
|
||||
}
|
||||
|
||||
public static final class AdvisorWrapper
|
||||
implements PointcutAdvisor, MethodInterceptor, Ordered, AopInfrastructureBean {
|
||||
|
||||
private final AuthorizationAdvisor advisor;
|
||||
|
||||
private AdvisorWrapper(AuthorizationAdvisor advisor) {
|
||||
this.advisor = advisor;
|
||||
}
|
||||
|
||||
public static AdvisorWrapper of(AuthorizationAdvisor advisor) {
|
||||
return new AdvisorWrapper(advisor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Advice getAdvice() {
|
||||
return this.advisor.getAdvice();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pointcut getPointcut() {
|
||||
return this.advisor.getPointcut();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.advisor.getOrder();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
|
||||
return this.advisor.invoke(invocation);
|
||||
}
|
||||
|
||||
registry.registerBeanDefinition(prefix + "Advisor", advisor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-15
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,7 +26,6 @@ import org.springframework.context.annotation.AutoProxyRegistrar;
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Dynamically determines which imports to include using the {@link EnableMethodSecurity}
|
||||
@@ -38,12 +37,6 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
final class MethodSecuritySelector implements ImportSelector {
|
||||
|
||||
private static final boolean isDataPresent = ClassUtils
|
||||
.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);
|
||||
|
||||
private static final boolean isObservabilityPresent = ClassUtils
|
||||
.isPresent("io.micrometer.observation.ObservationRegistry", null);
|
||||
|
||||
private final ImportSelector autoProxy = new AutoProxyRegistrarSelector();
|
||||
|
||||
@Override
|
||||
@@ -63,13 +56,6 @@ final class MethodSecuritySelector implements ImportSelector {
|
||||
if (annotation.jsr250Enabled()) {
|
||||
imports.add(Jsr250MethodSecurityConfiguration.class.getName());
|
||||
}
|
||||
imports.add(AuthorizationProxyConfiguration.class.getName());
|
||||
if (isDataPresent) {
|
||||
imports.add(AuthorizationProxyDataConfiguration.class.getName());
|
||||
}
|
||||
if (isObservabilityPresent) {
|
||||
imports.add(MethodObservationConfiguration.class.getName());
|
||||
}
|
||||
return imports.toArray(new String[0]);
|
||||
}
|
||||
|
||||
|
||||
+101
-138
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 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,41 +16,35 @@
|
||||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
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.ImportAware;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||
import org.springframework.security.aot.hint.PrePostAuthorizeHintsRegistrar;
|
||||
import org.springframework.security.aot.hint.SecurityHintsRegistrar;
|
||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.PrePostTemplateDefaults;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.util.function.SingletonSupplier;
|
||||
|
||||
/**
|
||||
* Base {@link Configuration} for enabling Spring Security Method Security.
|
||||
@@ -60,153 +54,122 @@ import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
* @since 5.6
|
||||
* @see EnableMethodSecurity
|
||||
*/
|
||||
@Configuration(value = "_prePostMethodSecurityConfiguration", proxyBeanMethods = false)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
final class PrePostMethodSecurityConfiguration implements ImportAware, ApplicationContextAware, AopInfrastructureBean {
|
||||
|
||||
private static final Pointcut preFilterPointcut = new PreFilterAuthorizationMethodInterceptor().getPointcut();
|
||||
|
||||
private static final Pointcut preAuthorizePointcut = AuthorizationManagerBeforeMethodInterceptor.preAuthorize()
|
||||
.getPointcut();
|
||||
|
||||
private static final Pointcut postAuthorizePointcut = AuthorizationManagerAfterMethodInterceptor.postAuthorize()
|
||||
.getPointcut();
|
||||
|
||||
private static final Pointcut postFilterPointcut = new PostFilterAuthorizationMethodInterceptor().getPointcut();
|
||||
|
||||
private final PreAuthorizeAuthorizationManager preAuthorizeAuthorizationManager = new PreAuthorizeAuthorizationManager();
|
||||
|
||||
private final PostAuthorizeAuthorizationManager postAuthorizeAuthorizationManager = new PostAuthorizeAuthorizationManager();
|
||||
|
||||
private final PreFilterAuthorizationMethodInterceptor preFilterMethodInterceptor = new PreFilterAuthorizationMethodInterceptor();
|
||||
|
||||
private final AuthorizationManagerBeforeMethodInterceptor preAuthorizeMethodInterceptor;
|
||||
|
||||
private final AuthorizationManagerAfterMethodInterceptor postAuthorizeMethodInterceptor;
|
||||
|
||||
private final PostFilterAuthorizationMethodInterceptor postFilterMethodInterceptor = new PostFilterAuthorizationMethodInterceptor();
|
||||
|
||||
private final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||
|
||||
PrePostMethodSecurityConfiguration(
|
||||
ObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocation>>> preAuthorizeProcessor,
|
||||
ObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocationResult>>> postAuthorizeProcessor) {
|
||||
this.preFilterMethodInterceptor.setExpressionHandler(this.expressionHandler);
|
||||
this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
|
||||
this.postAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
|
||||
this.postFilterMethodInterceptor.setExpressionHandler(this.expressionHandler);
|
||||
AuthorizationManager<MethodInvocation> preAuthorize = preAuthorizeProcessor
|
||||
.getIfUnique(ObjectPostProcessor::identity)
|
||||
.postProcess(this.preAuthorizeAuthorizationManager);
|
||||
this.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor.preAuthorize(preAuthorize);
|
||||
AuthorizationManager<MethodInvocationResult> postAuthorize = postAuthorizeProcessor
|
||||
.getIfUnique(ObjectPostProcessor::identity)
|
||||
.postProcess(this.postAuthorizeAuthorizationManager);
|
||||
this.postAuthorizeMethodInterceptor = AuthorizationManagerAfterMethodInterceptor.postAuthorize(postAuthorize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.expressionHandler.setApplicationContext(context);
|
||||
this.preAuthorizeAuthorizationManager.setApplicationContext(context);
|
||||
this.postAuthorizeAuthorizationManager.setApplicationContext(context);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) {
|
||||
this.expressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setRoleHierarchy(RoleHierarchy roleHierarchy) {
|
||||
this.expressionHandler.setRoleHierarchy(roleHierarchy);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {
|
||||
this.preFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
|
||||
this.preAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);
|
||||
this.postAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);
|
||||
this.postFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setTemplateDefaults(PrePostTemplateDefaults templateDefaults) {
|
||||
this.preFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
|
||||
this.preAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);
|
||||
this.postAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);
|
||||
this.postFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
|
||||
this.preFilterMethodInterceptor.setExpressionHandler(expressionHandler);
|
||||
this.preAuthorizeAuthorizationManager.setExpressionHandler(expressionHandler);
|
||||
this.postAuthorizeAuthorizationManager.setExpressionHandler(expressionHandler);
|
||||
this.postFilterMethodInterceptor.setExpressionHandler(expressionHandler);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
|
||||
this.preFilterMethodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);
|
||||
this.preAuthorizeMethodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);
|
||||
this.postAuthorizeMethodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);
|
||||
this.postFilterMethodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setAuthorizationEventPublisher(AuthorizationEventPublisher publisher) {
|
||||
this.preAuthorizeMethodInterceptor.setAuthorizationEventPublisher(publisher);
|
||||
this.postAuthorizeMethodInterceptor.setAuthorizationEventPublisher(publisher);
|
||||
}
|
||||
final class PrePostMethodSecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static MethodInterceptor preFilterAuthorizationMethodInterceptor(
|
||||
ObjectProvider<PrePostMethodSecurityConfiguration> _prePostMethodSecurityConfiguration) {
|
||||
return new DeferringMethodInterceptor<>(preFilterPointcut,
|
||||
() -> _prePostMethodSecurityConfiguration.getObject().preFilterMethodInterceptor);
|
||||
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
|
||||
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
|
||||
ObjectProvider<SecurityContextHolderStrategy> strategyProvider, ApplicationContext context) {
|
||||
PreFilterAuthorizationMethodInterceptor preFilter = new PreFilterAuthorizationMethodInterceptor();
|
||||
strategyProvider.ifAvailable(preFilter::setSecurityContextHolderStrategy);
|
||||
preFilter.setExpressionHandler(
|
||||
new DeferringMethodSecurityExpressionHandler(expressionHandlerProvider, defaultsProvider, context));
|
||||
return preFilter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static MethodInterceptor preAuthorizeAuthorizationMethodInterceptor(
|
||||
ObjectProvider<PrePostMethodSecurityConfiguration> _prePostMethodSecurityConfiguration) {
|
||||
return new DeferringMethodInterceptor<>(preAuthorizePointcut,
|
||||
() -> _prePostMethodSecurityConfiguration.getObject().preAuthorizeMethodInterceptor);
|
||||
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
|
||||
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
|
||||
ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
|
||||
ObjectProvider<AuthorizationEventPublisher> eventPublisherProvider,
|
||||
ObjectProvider<ObservationRegistry> registryProvider, ApplicationContext context) {
|
||||
PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager();
|
||||
manager.setExpressionHandler(
|
||||
new DeferringMethodSecurityExpressionHandler(expressionHandlerProvider, defaultsProvider, context));
|
||||
AuthorizationManagerBeforeMethodInterceptor preAuthorize = AuthorizationManagerBeforeMethodInterceptor
|
||||
.preAuthorize(manager(manager, registryProvider));
|
||||
strategyProvider.ifAvailable(preAuthorize::setSecurityContextHolderStrategy);
|
||||
eventPublisherProvider.ifAvailable(preAuthorize::setAuthorizationEventPublisher);
|
||||
return preAuthorize;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static MethodInterceptor postAuthorizeAuthorizationMethodInterceptor(
|
||||
ObjectProvider<PrePostMethodSecurityConfiguration> _prePostMethodSecurityConfiguration) {
|
||||
return new DeferringMethodInterceptor<>(postAuthorizePointcut,
|
||||
() -> _prePostMethodSecurityConfiguration.getObject().postAuthorizeMethodInterceptor);
|
||||
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
|
||||
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
|
||||
ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
|
||||
ObjectProvider<AuthorizationEventPublisher> eventPublisherProvider,
|
||||
ObjectProvider<ObservationRegistry> registryProvider, ApplicationContext context) {
|
||||
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
|
||||
manager.setExpressionHandler(
|
||||
new DeferringMethodSecurityExpressionHandler(expressionHandlerProvider, defaultsProvider, context));
|
||||
AuthorizationManagerAfterMethodInterceptor postAuthorize = AuthorizationManagerAfterMethodInterceptor
|
||||
.postAuthorize(manager(manager, registryProvider));
|
||||
strategyProvider.ifAvailable(postAuthorize::setSecurityContextHolderStrategy);
|
||||
eventPublisherProvider.ifAvailable(postAuthorize::setAuthorizationEventPublisher);
|
||||
return postAuthorize;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static MethodInterceptor postFilterAuthorizationMethodInterceptor(
|
||||
ObjectProvider<PrePostMethodSecurityConfiguration> _prePostMethodSecurityConfiguration) {
|
||||
return new DeferringMethodInterceptor<>(postFilterPointcut,
|
||||
() -> _prePostMethodSecurityConfiguration.getObject().postFilterMethodInterceptor);
|
||||
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider,
|
||||
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
|
||||
ObjectProvider<SecurityContextHolderStrategy> strategyProvider, ApplicationContext context) {
|
||||
PostFilterAuthorizationMethodInterceptor postFilter = new PostFilterAuthorizationMethodInterceptor();
|
||||
strategyProvider.ifAvailable(postFilter::setSecurityContextHolderStrategy);
|
||||
postFilter.setExpressionHandler(
|
||||
new DeferringMethodSecurityExpressionHandler(expressionHandlerProvider, defaultsProvider, context));
|
||||
return postFilter;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static SecurityHintsRegistrar prePostAuthorizeExpressionHintsRegistrar() {
|
||||
return new PrePostAuthorizeHintsRegistrar();
|
||||
private static MethodSecurityExpressionHandler defaultExpressionHandler(
|
||||
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider, ApplicationContext context) {
|
||||
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
|
||||
defaultsProvider.ifAvailable((d) -> handler.setDefaultRolePrefix(d.getRolePrefix()));
|
||||
handler.setApplicationContext(context);
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
EnableMethodSecurity annotation = importMetadata.getAnnotations().get(EnableMethodSecurity.class).synthesize();
|
||||
this.preFilterMethodInterceptor.setOrder(this.preFilterMethodInterceptor.getOrder() + annotation.offset());
|
||||
this.preAuthorizeMethodInterceptor
|
||||
.setOrder(this.preAuthorizeMethodInterceptor.getOrder() + annotation.offset());
|
||||
this.postAuthorizeMethodInterceptor
|
||||
.setOrder(this.postAuthorizeMethodInterceptor.getOrder() + annotation.offset());
|
||||
this.postFilterMethodInterceptor.setOrder(this.postFilterMethodInterceptor.getOrder() + annotation.offset());
|
||||
static <T> AuthorizationManager<T> manager(AuthorizationManager<T> delegate,
|
||||
ObjectProvider<ObservationRegistry> registryProvider) {
|
||||
return new DeferringObservationAuthorizationManager<>(registryProvider, delegate);
|
||||
}
|
||||
|
||||
private static final class DeferringMethodSecurityExpressionHandler implements MethodSecurityExpressionHandler {
|
||||
|
||||
private final Supplier<MethodSecurityExpressionHandler> expressionHandler;
|
||||
|
||||
private DeferringMethodSecurityExpressionHandler(
|
||||
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlerProvider,
|
||||
ObjectProvider<GrantedAuthorityDefaults> defaultsProvider, ApplicationContext applicationContext) {
|
||||
this.expressionHandler = SingletonSupplier.of(() -> expressionHandlerProvider
|
||||
.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, applicationContext)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionParser getExpressionParser() {
|
||||
return this.expressionHandler.get().getExpressionParser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EvaluationContext createEvaluationContext(Authentication authentication, MethodInvocation invocation) {
|
||||
return this.expressionHandler.get().createEvaluationContext(authentication, invocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication,
|
||||
MethodInvocation invocation) {
|
||||
return this.expressionHandler.get().createEvaluationContext(authentication, invocation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object filter(Object filterTarget, Expression filterExpression, EvaluationContext ctx) {
|
||||
return this.expressionHandler.get().filter(filterTarget, filterExpression, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReturnObject(Object returnObject, EvaluationContext ctx) {
|
||||
this.expressionHandler.get().setReturnObject(returnObject, ctx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+27
-104
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2023 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,20 +16,14 @@
|
||||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
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.Fallback;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
||||
@@ -42,10 +36,7 @@ import org.springframework.security.authorization.method.PostAuthorizeReactiveAu
|
||||
import org.springframework.security.authorization.method.PostFilterAuthorizationReactiveMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.PreAuthorizeReactiveAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.PreFilterAuthorizationReactiveMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.PrePostTemplateDefaults;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
|
||||
|
||||
/**
|
||||
* Configuration for a {@link ReactiveAuthenticationManager} based Method Security.
|
||||
@@ -53,116 +44,43 @@ import org.springframework.security.core.annotation.AnnotationTemplateExpression
|
||||
* @author Evgeniy Cheban
|
||||
* @since 5.8
|
||||
*/
|
||||
@Configuration(value = "_reactiveMethodSecurityConfiguration", proxyBeanMethods = false)
|
||||
final class ReactiveAuthorizationManagerMethodSecurityConfiguration
|
||||
implements AopInfrastructureBean, ApplicationContextAware {
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
final class ReactiveAuthorizationManagerMethodSecurityConfiguration {
|
||||
|
||||
private static final Pointcut preFilterPointcut = new PreFilterAuthorizationReactiveMethodInterceptor()
|
||||
.getPointcut();
|
||||
|
||||
private static final Pointcut preAuthorizePointcut = AuthorizationManagerBeforeReactiveMethodInterceptor
|
||||
.preAuthorize()
|
||||
.getPointcut();
|
||||
|
||||
private static final Pointcut postAuthorizePointcut = AuthorizationManagerAfterReactiveMethodInterceptor
|
||||
.postAuthorize()
|
||||
.getPointcut();
|
||||
|
||||
private static final Pointcut postFilterPointcut = new PostFilterAuthorizationReactiveMethodInterceptor()
|
||||
.getPointcut();
|
||||
|
||||
private PreFilterAuthorizationReactiveMethodInterceptor preFilterMethodInterceptor = new PreFilterAuthorizationReactiveMethodInterceptor();
|
||||
|
||||
private PreAuthorizeReactiveAuthorizationManager preAuthorizeAuthorizationManager = new PreAuthorizeReactiveAuthorizationManager();
|
||||
|
||||
private PostAuthorizeReactiveAuthorizationManager postAuthorizeAuthorizationManager = new PostAuthorizeReactiveAuthorizationManager();
|
||||
|
||||
private PostFilterAuthorizationReactiveMethodInterceptor postFilterMethodInterceptor = new PostFilterAuthorizationReactiveMethodInterceptor();
|
||||
|
||||
private final AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorizeMethodInterceptor;
|
||||
|
||||
private final AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeMethodInterceptor;
|
||||
|
||||
ReactiveAuthorizationManagerMethodSecurityConfiguration(
|
||||
ObjectProvider<MethodSecurityExpressionHandler> expressionHandlers,
|
||||
ObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocation>>> preAuthorizePostProcessor,
|
||||
ObjectProvider<ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocationResult>>> postAuthorizePostProcessor) {
|
||||
MethodSecurityExpressionHandler expressionHandler = expressionHandlers.getIfUnique();
|
||||
if (expressionHandler != null) {
|
||||
this.preFilterMethodInterceptor = new PreFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
|
||||
this.preAuthorizeAuthorizationManager = new PreAuthorizeReactiveAuthorizationManager(expressionHandler);
|
||||
this.postFilterMethodInterceptor = new PostFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
|
||||
this.postAuthorizeAuthorizationManager = new PostAuthorizeReactiveAuthorizationManager(expressionHandler);
|
||||
}
|
||||
ReactiveAuthorizationManager<MethodInvocation> preAuthorize = preAuthorizePostProcessor
|
||||
.getIfUnique(ObjectPostProcessor::identity)
|
||||
.postProcess(this.preAuthorizeAuthorizationManager);
|
||||
this.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeReactiveMethodInterceptor
|
||||
.preAuthorize(preAuthorize);
|
||||
ReactiveAuthorizationManager<MethodInvocationResult> postAuthorize = postAuthorizePostProcessor
|
||||
.getIfAvailable(ObjectPostProcessor::identity)
|
||||
.postProcess(this.postAuthorizeAuthorizationManager);
|
||||
this.postAuthorizeMethodInterceptor = AuthorizationManagerAfterReactiveMethodInterceptor
|
||||
.postAuthorize(postAuthorize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||
this.preAuthorizeAuthorizationManager.setApplicationContext(context);
|
||||
this.postAuthorizeAuthorizationManager.setApplicationContext(context);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setTemplateDefaults(PrePostTemplateDefaults templateDefaults) {
|
||||
this.preFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
|
||||
this.preAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);
|
||||
this.postAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);
|
||||
this.postFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {
|
||||
this.preFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
|
||||
this.preAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);
|
||||
this.postAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults);
|
||||
this.postFilterMethodInterceptor.setTemplateDefaults(templateDefaults);
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static PreFilterAuthorizationReactiveMethodInterceptor preFilterInterceptor(
|
||||
MethodSecurityExpressionHandler expressionHandler) {
|
||||
return new PreFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static MethodInterceptor preFilterAuthorizationMethodInterceptor(
|
||||
ObjectProvider<ReactiveAuthorizationManagerMethodSecurityConfiguration> _reactiveMethodSecurityConfiguration) {
|
||||
return new DeferringMethodInterceptor<>(preFilterPointcut,
|
||||
() -> _reactiveMethodSecurityConfiguration.getObject().preFilterMethodInterceptor);
|
||||
static AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorizeInterceptor(
|
||||
MethodSecurityExpressionHandler expressionHandler, ObjectProvider<ObservationRegistry> registryProvider) {
|
||||
ReactiveAuthorizationManager<MethodInvocation> authorizationManager = manager(
|
||||
new PreAuthorizeReactiveAuthorizationManager(expressionHandler), registryProvider);
|
||||
return AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize(authorizationManager);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static MethodInterceptor preAuthorizeAuthorizationMethodInterceptor(
|
||||
ObjectProvider<ReactiveAuthorizationManagerMethodSecurityConfiguration> _reactiveMethodSecurityConfiguration) {
|
||||
return new DeferringMethodInterceptor<>(preAuthorizePointcut,
|
||||
() -> _reactiveMethodSecurityConfiguration.getObject().preAuthorizeMethodInterceptor);
|
||||
static PostFilterAuthorizationReactiveMethodInterceptor postFilterInterceptor(
|
||||
MethodSecurityExpressionHandler expressionHandler) {
|
||||
return new PostFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static MethodInterceptor postFilterAuthorizationMethodInterceptor(
|
||||
ObjectProvider<ReactiveAuthorizationManagerMethodSecurityConfiguration> _reactiveMethodSecurityConfiguration) {
|
||||
return new DeferringMethodInterceptor<>(postFilterPointcut,
|
||||
() -> _reactiveMethodSecurityConfiguration.getObject().postFilterMethodInterceptor);
|
||||
static AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeInterceptor(
|
||||
MethodSecurityExpressionHandler expressionHandler, ObjectProvider<ObservationRegistry> registryProvider) {
|
||||
ReactiveAuthorizationManager<MethodInvocationResult> authorizationManager = manager(
|
||||
new PostAuthorizeReactiveAuthorizationManager(expressionHandler), registryProvider);
|
||||
return AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize(authorizationManager);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static MethodInterceptor postAuthorizeAuthorizationMethodInterceptor(
|
||||
ObjectProvider<ReactiveAuthorizationManagerMethodSecurityConfiguration> _reactiveMethodSecurityConfiguration) {
|
||||
return new DeferringMethodInterceptor<>(postAuthorizePointcut,
|
||||
() -> _reactiveMethodSecurityConfiguration.getObject().postAuthorizeMethodInterceptor);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
@Fallback
|
||||
static DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(
|
||||
@Autowired(required = false) GrantedAuthorityDefaults grantedAuthorityDefaults) {
|
||||
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
|
||||
@@ -172,4 +90,9 @@ final class ReactiveAuthorizationManagerMethodSecurityConfiguration
|
||||
return handler;
|
||||
}
|
||||
|
||||
static <T> ReactiveAuthorizationManager<T> manager(ReactiveAuthorizationManager<T> delegate,
|
||||
ObjectProvider<ObservationRegistry> registryProvider) {
|
||||
return new DeferringObservationReactiveAuthorizationManager<>(registryProvider, delegate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
|
||||
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
class ReactiveMethodObservationConfiguration {
|
||||
|
||||
private static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()
|
||||
.shouldObserveRequests(true)
|
||||
.shouldObserveAuthentications(true)
|
||||
.shouldObserveAuthorizations(true)
|
||||
.build();
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocation>> methodAuthorizationManagerPostProcessor(
|
||||
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||
return new ObjectPostProcessor<>() {
|
||||
@Override
|
||||
public ReactiveAuthorizationManager postProcess(ReactiveAuthorizationManager object) {
|
||||
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();
|
||||
return active ? new ObservationReactiveAuthorizationManager<>(r, object) : object;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static ObjectPostProcessor<ReactiveAuthorizationManager<MethodInvocationResult>> methodResultAuthorizationManagerPostProcessor(
|
||||
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||
return new ObjectPostProcessor<>() {
|
||||
@Override
|
||||
public ReactiveAuthorizationManager postProcess(ReactiveAuthorizationManager object) {
|
||||
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();
|
||||
return active ? new ObservationReactiveAuthorizationManager<>(r, object) : object;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
-2
@@ -22,7 +22,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Fallback;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
@@ -83,7 +82,6 @@ class ReactiveMethodSecurityConfiguration implements ImportAware {
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
@Fallback
|
||||
static DefaultMethodSecurityExpressionHandler methodSecurityExpressionHandler(
|
||||
ReactiveMethodSecurityConfiguration configuration) {
|
||||
DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
|
||||
|
||||
+1
-16
@@ -26,7 +26,6 @@ import org.springframework.context.annotation.AutoProxyRegistrar;
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
@@ -35,12 +34,6 @@ import org.springframework.util.ClassUtils;
|
||||
*/
|
||||
class ReactiveMethodSecuritySelector implements ImportSelector {
|
||||
|
||||
private static final boolean isDataPresent = ClassUtils
|
||||
.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);
|
||||
|
||||
private static final boolean isObservabilityPresent = ClassUtils
|
||||
.isPresent("io.micrometer.observation.ObservationRegistry", null);
|
||||
|
||||
private final ImportSelector autoProxy = new AutoProxyRegistrarSelector();
|
||||
|
||||
@Override
|
||||
@@ -58,21 +51,13 @@ class ReactiveMethodSecuritySelector implements ImportSelector {
|
||||
else {
|
||||
imports.add(ReactiveMethodSecurityConfiguration.class.getName());
|
||||
}
|
||||
if (isDataPresent) {
|
||||
imports.add(AuthorizationProxyDataConfiguration.class.getName());
|
||||
}
|
||||
if (isObservabilityPresent) {
|
||||
imports.add(ReactiveMethodObservationConfiguration.class.getName());
|
||||
}
|
||||
imports.add(AuthorizationProxyConfiguration.class.getName());
|
||||
return imports.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private static final class AutoProxyRegistrarSelector
|
||||
extends AdviceModeImportSelector<EnableReactiveMethodSecurity> {
|
||||
|
||||
private static final String[] IMPORTS = new String[] { AutoProxyRegistrar.class.getName(),
|
||||
MethodSecurityAdvisorRegistrar.class.getName() };
|
||||
private static final String[] IMPORTS = new String[] { AutoProxyRegistrar.class.getName() };
|
||||
|
||||
@Override
|
||||
protected String[] selectImports(@NonNull AdviceMode adviceMode) {
|
||||
|
||||
+16
-57
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 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,29 +16,20 @@
|
||||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportAware;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||
import org.springframework.security.authorization.AuthoritiesAuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.SecuredAuthorizationManager;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
|
||||
/**
|
||||
@@ -49,56 +40,24 @@ import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
* @since 5.6
|
||||
* @see EnableMethodSecurity
|
||||
*/
|
||||
@Configuration(value = "_securedMethodSecurityConfiguration", proxyBeanMethods = false)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
final class SecuredMethodSecurityConfiguration implements ImportAware, AopInfrastructureBean {
|
||||
|
||||
private static final Pointcut pointcut = AuthorizationManagerBeforeMethodInterceptor.secured().getPointcut();
|
||||
|
||||
private final SecuredAuthorizationManager authorizationManager = new SecuredAuthorizationManager();
|
||||
|
||||
private final AuthorizationManagerBeforeMethodInterceptor methodInterceptor;
|
||||
|
||||
SecuredMethodSecurityConfiguration(
|
||||
ObjectProvider<ObjectPostProcessor<AuthorizationManager<MethodInvocation>>> postProcessors) {
|
||||
ObjectPostProcessor<AuthorizationManager<MethodInvocation>> postProcessor = postProcessors
|
||||
.getIfUnique(ObjectPostProcessor::identity);
|
||||
AuthorizationManager<MethodInvocation> manager = postProcessor.postProcess(this.authorizationManager);
|
||||
this.methodInterceptor = AuthorizationManagerBeforeMethodInterceptor.secured(manager);
|
||||
}
|
||||
final class SecuredMethodSecurityConfiguration {
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static MethodInterceptor securedAuthorizationMethodInterceptor(
|
||||
ObjectProvider<SecuredMethodSecurityConfiguration> securedMethodSecurityConfiguration) {
|
||||
Supplier<AuthorizationManagerBeforeMethodInterceptor> supplier = () -> {
|
||||
SecuredMethodSecurityConfiguration configuration = securedMethodSecurityConfiguration.getObject();
|
||||
return configuration.methodInterceptor;
|
||||
};
|
||||
return new DeferringMethodInterceptor<>(pointcut, supplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImportMetadata(AnnotationMetadata importMetadata) {
|
||||
EnableMethodSecurity annotation = importMetadata.getAnnotations().get(EnableMethodSecurity.class).synthesize();
|
||||
this.methodInterceptor.setOrder(this.methodInterceptor.getOrder() + annotation.offset());
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setRoleHierarchy(RoleHierarchy roleHierarchy) {
|
||||
AuthoritiesAuthorizationManager authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager();
|
||||
authoritiesAuthorizationManager.setRoleHierarchy(roleHierarchy);
|
||||
this.authorizationManager.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
|
||||
this.methodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy);
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setEventPublisher(AuthorizationEventPublisher eventPublisher) {
|
||||
this.methodInterceptor.setAuthorizationEventPublisher(eventPublisher);
|
||||
ObjectProvider<SecurityContextHolderStrategy> strategyProvider,
|
||||
ObjectProvider<ObservationRegistry> registryProvider) {
|
||||
SecuredAuthorizationManager secured = new SecuredAuthorizationManager();
|
||||
SecurityContextHolderStrategy strategy = strategyProvider
|
||||
.getIfAvailable(SecurityContextHolder::getContextHolderStrategy);
|
||||
AuthorizationManager<MethodInvocation> manager = new DeferringObservationAuthorizationManager<>(
|
||||
registryProvider, secured);
|
||||
AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor
|
||||
.secured(manager);
|
||||
interceptor.setSecurityContextHolderStrategy(strategy);
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-2
@@ -35,8 +35,7 @@ import org.springframework.context.annotation.Import;
|
||||
@Documented
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Import({ RSocketSecurityConfiguration.class, SecuritySocketAcceptorInterceptorConfiguration.class,
|
||||
ReactiveObservationImportSelector.class })
|
||||
@Import({ RSocketSecurityConfiguration.class, SecuritySocketAcceptorInterceptorConfiguration.class })
|
||||
public @interface EnableRSocketSecurity {
|
||||
|
||||
}
|
||||
|
||||
+6
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2024 the original author or authors.
|
||||
* Copyright 2019-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.
|
||||
@@ -108,7 +108,6 @@ import org.springframework.security.rsocket.util.matcher.RoutePayloadExchangeMat
|
||||
* @author Luis Felipe Vega
|
||||
* @author Manuel Tejeda
|
||||
* @author Ebert Toribio
|
||||
* @author Ngoc Nhan
|
||||
* @since 5.2
|
||||
*/
|
||||
public class RSocketSecurity {
|
||||
@@ -239,12 +238,15 @@ public class RSocketSecurity {
|
||||
return getBeanOrNull(ResolvableType.forClass(beanClass));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T getBeanOrNull(ResolvableType type) {
|
||||
if (this.context == null) {
|
||||
return null;
|
||||
}
|
||||
return (T) this.context.getBeanProvider(type).getIfUnique();
|
||||
String[] names = this.context.getBeanNamesForType(type);
|
||||
if (names.length == 1) {
|
||||
return (T) this.context.getBean(names[0]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
|
||||
+10
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2019-2024 the original author or authors.
|
||||
* Copyright 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.
|
||||
@@ -16,16 +16,16 @@
|
||||
|
||||
package org.springframework.security.config.annotation.rsocket;
|
||||
|
||||
import java.util.Map;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.security.authentication.ObservationReactiveAuthenticationManager;
|
||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
|
||||
@@ -46,7 +46,7 @@ class RSocketSecurityConfiguration {
|
||||
|
||||
private PasswordEncoder passwordEncoder;
|
||||
|
||||
private ObjectPostProcessor<ReactiveAuthenticationManager> postProcessor = ObjectPostProcessor.identity();
|
||||
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
|
||||
|
||||
@Autowired(required = false)
|
||||
void setAuthenticationManager(ReactiveAuthenticationManager authenticationManager) {
|
||||
@@ -64,12 +64,8 @@ class RSocketSecurityConfiguration {
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setAuthenticationManagerPostProcessor(
|
||||
Map<String, ObjectPostProcessor<ReactiveAuthenticationManager>> postProcessors) {
|
||||
if (postProcessors.size() == 1) {
|
||||
this.postProcessor = postProcessors.values().iterator().next();
|
||||
}
|
||||
this.postProcessor = postProcessors.get("rSocketAuthenticationManagerPostProcessor");
|
||||
void setObservationRegistry(ObservationRegistry observationRegistry) {
|
||||
this.observationRegistry = observationRegistry;
|
||||
}
|
||||
|
||||
@Bean(name = RSOCKET_SECURITY_BEAN_NAME)
|
||||
@@ -90,7 +86,10 @@ class RSocketSecurityConfiguration {
|
||||
if (this.passwordEncoder != null) {
|
||||
manager.setPasswordEncoder(this.passwordEncoder);
|
||||
}
|
||||
return this.postProcessor.postProcess(manager);
|
||||
if (!this.observationRegistry.isNoop()) {
|
||||
return new ObservationReactiveAuthenticationManager(this.observationRegistry, manager);
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.rsocket;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.security.authentication.ObservationReactiveAuthenticationManager;
|
||||
import org.springframework.security.authentication.ReactiveAuthenticationManager;
|
||||
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
|
||||
import org.springframework.security.authorization.ReactiveAuthorizationManager;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||
import org.springframework.security.rsocket.api.PayloadExchange;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
class ReactiveObservationConfiguration {
|
||||
|
||||
private static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()
|
||||
.shouldObserveRequests(true)
|
||||
.shouldObserveAuthentications(true)
|
||||
.shouldObserveAuthorizations(true)
|
||||
.build();
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static ObjectPostProcessor<ReactiveAuthorizationManager<PayloadExchange>> rSocketAuthorizationManagerPostProcessor(
|
||||
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||
return new ObjectPostProcessor<>() {
|
||||
@Override
|
||||
public ReactiveAuthorizationManager postProcess(ReactiveAuthorizationManager object) {
|
||||
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();
|
||||
return active ? new ObservationReactiveAuthorizationManager<>(r, object) : object;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static ObjectPostProcessor<ReactiveAuthenticationManager> rSocketAuthenticationManagerPostProcessor(
|
||||
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||
return new ObjectPostProcessor<>() {
|
||||
@Override
|
||||
public ReactiveAuthenticationManager postProcess(ReactiveAuthenticationManager object) {
|
||||
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthentications();
|
||||
return active ? new ObservationReactiveAuthenticationManager(r, object) : object;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.rsocket;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Used by {@link EnableWebFluxSecurity} to conditionally import observation configuration
|
||||
* when {@link ObservationRegistry} is present.
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 6.4
|
||||
*/
|
||||
class ReactiveObservationImportSelector implements ImportSelector {
|
||||
|
||||
private static final boolean observabilityPresent;
|
||||
|
||||
static {
|
||||
ClassLoader classLoader = ReactiveObservationImportSelector.class.getClassLoader();
|
||||
observabilityPresent = ClassUtils.isPresent("io.micrometer.observation.ObservationRegistry", classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
|
||||
if (!observabilityPresent) {
|
||||
return new String[0];
|
||||
}
|
||||
return new String[] { ReactiveObservationConfiguration.class.getName() };
|
||||
}
|
||||
|
||||
}
|
||||
+2
-21
@@ -34,12 +34,10 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractConfigAttributeRequestMatcherRegistry;
|
||||
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
@@ -115,9 +113,7 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||
*/
|
||||
protected final List<MvcRequestMatcher> createMvcMatchers(HttpMethod method, String... mvcPatterns) {
|
||||
Assert.state(!this.anyRequestConfigured, "Can't configure mvcMatchers after anyRequest");
|
||||
ResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class, Object.class);
|
||||
ObjectProvider<ObjectPostProcessor<Object>> postProcessors = this.context.getBeanProvider(type);
|
||||
ObjectPostProcessor<Object> opp = postProcessors.getObject();
|
||||
ObjectPostProcessor<Object> opp = this.context.getBean(ObjectPostProcessor.class);
|
||||
if (!this.context.containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
|
||||
throw new NoSuchBeanDefinitionException("A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME
|
||||
+ " of type " + HandlerMappingIntrospector.class.getName()
|
||||
@@ -199,12 +195,6 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||
* @since 5.8
|
||||
*/
|
||||
public C requestMatchers(HttpMethod method, String... patterns) {
|
||||
if (anyPathsDontStartWithLeadingSlash(patterns)) {
|
||||
this.logger.warn("One of the patterns in " + Arrays.toString(patterns)
|
||||
+ " is missing a leading slash. This is discouraged; please include the "
|
||||
+ "leading slash in all your request matcher patterns. In future versions of "
|
||||
+ "Spring Security, leaving out the leading slash will result in an exception.");
|
||||
}
|
||||
if (!mvcPresent) {
|
||||
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
|
||||
}
|
||||
@@ -225,15 +215,6 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
|
||||
}
|
||||
|
||||
private boolean anyPathsDontStartWithLeadingSlash(String... patterns) {
|
||||
for (String pattern : patterns) {
|
||||
if (!pattern.startsWith("/")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private RequestMatcher resolve(AntPathRequestMatcher ant, MvcRequestMatcher mvc, ServletContext servletContext) {
|
||||
Map<String, ? extends ServletRegistration> registrations = mappableServletRegistrations(servletContext);
|
||||
if (registrations.isEmpty()) {
|
||||
|
||||
-1
@@ -157,7 +157,6 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>
|
||||
* <li>{@link DigestAuthenticationFilter}</li>
|
||||
* <li>{@link BearerTokenAuthenticationFilter}</li>
|
||||
* <li>{@link BasicAuthenticationFilter}</li>
|
||||
* <li>{@link org.springframework.security.web.authentication.AuthenticationFilter}</li>
|
||||
* <li>{@link RequestCacheAwareFilter}</li>
|
||||
* <li>{@link SecurityContextHolderAwareRequestFilter}</li>
|
||||
* <li>{@link JaasApiIntegrationFilter}</li>
|
||||
|
||||
-8
@@ -27,18 +27,14 @@ import org.springframework.security.web.access.channel.ChannelProcessingFilter;
|
||||
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.AuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||
import org.springframework.security.web.authentication.ott.GenerateOneTimeTokenFilter;
|
||||
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
|
||||
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.switchuser.SwitchUserFilter;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
|
||||
import org.springframework.security.web.authentication.ui.DefaultOneTimeTokenSubmitPageGeneratingFilter;
|
||||
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;
|
||||
import org.springframework.security.web.context.SecurityContextHolderFilter;
|
||||
@@ -91,7 +87,6 @@ final class FilterOrderRegistration {
|
||||
this.filterToOrder.put(
|
||||
"org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter",
|
||||
order.next());
|
||||
put(GenerateOneTimeTokenFilter.class, order.next());
|
||||
put(X509AuthenticationFilter.class, order.next());
|
||||
put(AbstractPreAuthenticatedProcessingFilter.class, order.next());
|
||||
this.filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order.next());
|
||||
@@ -102,17 +97,14 @@ final class FilterOrderRegistration {
|
||||
order.next());
|
||||
put(UsernamePasswordAuthenticationFilter.class, order.next());
|
||||
order.next(); // gh-8105
|
||||
put(DefaultResourcesFilter.class, order.next());
|
||||
put(DefaultLoginPageGeneratingFilter.class, order.next());
|
||||
put(DefaultLogoutPageGeneratingFilter.class, order.next());
|
||||
put(DefaultOneTimeTokenSubmitPageGeneratingFilter.class, order.next());
|
||||
put(ConcurrentSessionFilter.class, order.next());
|
||||
put(DigestAuthenticationFilter.class, order.next());
|
||||
this.filterToOrder.put(
|
||||
"org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter",
|
||||
order.next());
|
||||
put(BasicAuthenticationFilter.class, order.next());
|
||||
put(AuthenticationFilter.class, order.next());
|
||||
put(RequestCacheAwareFilter.class, order.next());
|
||||
put(SecurityContextHolderAwareRequestFilter.class, order.next());
|
||||
put(JaasApiIntegrationFilter.class, order.next());
|
||||
|
||||
+21
-105
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,6 +21,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
@@ -29,16 +30,15 @@ import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.OrderComparator;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.ObservationAuthenticationManager;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
@@ -67,13 +67,11 @@ import org.springframework.security.config.annotation.web.configurers.RequestCac
|
||||
import org.springframework.security.config.annotation.web.configurers.SecurityContextConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.ServletApiConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.WebAuthnConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.X509Configurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2ClientConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.client.OidcLogoutConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.ott.OneTimeTokenLoginConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2LoginConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2LogoutConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.saml2.Saml2MetadataConfigurer;
|
||||
@@ -140,7 +138,6 @@ import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Joe Grandja
|
||||
* @author Ngoc Nhan
|
||||
* @since 3.2
|
||||
* @see EnableWebSecurity
|
||||
*/
|
||||
@@ -188,23 +185,6 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
this.requestMatcherConfigurer = new RequestMatcherConfigurer(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated(since = "6.4", forRemoval = true)
|
||||
@SuppressWarnings("unchecked")
|
||||
public HttpSecurity(org.springframework.security.config.annotation.ObjectPostProcessor<Object> objectPostProcessor,
|
||||
AuthenticationManagerBuilder authenticationBuilder, Map<Class<?>, Object> sharedObjects) {
|
||||
super(objectPostProcessor);
|
||||
Assert.notNull(authenticationBuilder, "authenticationBuilder cannot be null");
|
||||
setSharedObject(AuthenticationManagerBuilder.class, authenticationBuilder);
|
||||
for (Map.Entry<Class<?>, Object> entry : sharedObjects.entrySet()) {
|
||||
setSharedObject((Class<Object>) entry.getKey(), entry.getValue());
|
||||
}
|
||||
ApplicationContext context = (ApplicationContext) sharedObjects.get(ApplicationContext.class);
|
||||
this.requestMatcherConfigurer = new RequestMatcherConfigurer(context);
|
||||
}
|
||||
|
||||
private ApplicationContext getContext() {
|
||||
return getSharedObject(ApplicationContext.class);
|
||||
}
|
||||
@@ -1131,10 +1111,9 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
* </pre>
|
||||
* @return the {@link ExpressionUrlAuthorizationConfigurer} for further customizations
|
||||
* @throws Exception
|
||||
* @deprecated For removal in 7.0. Use {@link #authorizeHttpRequests(Customizer)}
|
||||
* instead
|
||||
* @deprecated For removal in 7.0. Use {@link #authorizeHttpRequests()} instead
|
||||
*/
|
||||
@Deprecated(since = "6.1", forRemoval = true)
|
||||
@Deprecated
|
||||
public ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests()
|
||||
throws Exception {
|
||||
ApplicationContext context = getContext();
|
||||
@@ -1247,10 +1226,9 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
* for the {@link ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry}
|
||||
* @return the {@link HttpSecurity} for further customizations
|
||||
* @throws Exception
|
||||
* @deprecated For removal in 7.0. Use {@link #authorizeHttpRequests(Customizer)}
|
||||
* instead
|
||||
* @deprecated For removal in 7.0. Use {@link #authorizeHttpRequests} instead
|
||||
*/
|
||||
@Deprecated(since = "6.1", forRemoval = true)
|
||||
@Deprecated
|
||||
public HttpSecurity authorizeRequests(
|
||||
Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> authorizeRequestsCustomizer)
|
||||
throws Exception {
|
||||
@@ -3000,45 +2978,6 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
return HttpSecurity.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures One-Time Token Login Support.
|
||||
*
|
||||
* <h2>Example Configuration</h2>
|
||||
*
|
||||
* <pre>
|
||||
* @Configuration
|
||||
* @EnableWebSecurity
|
||||
* public class SecurityConfig {
|
||||
*
|
||||
* @Bean
|
||||
* public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
* http
|
||||
* .authorizeHttpRequests((authorize) -> authorize
|
||||
* .anyRequest().authenticated()
|
||||
* )
|
||||
* .oneTimeTokenLogin(Customizer.withDefaults());
|
||||
* return http.build();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* public OneTimeTokenGenerationSuccessHandler oneTimeTokenGenerationSuccessHandler() {
|
||||
* return new MyMagicLinkOneTimeTokenGenerationSuccessHandler();
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
* @param oneTimeTokenLoginConfigurerCustomizer the {@link Customizer} to provide more
|
||||
* options for the {@link OneTimeTokenLoginConfigurer}
|
||||
* @return the {@link HttpSecurity} for further customizations
|
||||
* @throws Exception
|
||||
*/
|
||||
public HttpSecurity oneTimeTokenLogin(
|
||||
Customizer<OneTimeTokenLoginConfigurer<HttpSecurity>> oneTimeTokenLoginConfigurerCustomizer)
|
||||
throws Exception {
|
||||
oneTimeTokenLoginConfigurerCustomizer.customize(getOrApply(new OneTimeTokenLoginConfigurer<>(getContext())));
|
||||
return HttpSecurity.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures channel security. In order for this configuration to be useful at least
|
||||
* one mapping to a required channel must be provided.
|
||||
@@ -3297,10 +3236,13 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
setSharedObject(AuthenticationManager.class, this.authenticationManager);
|
||||
}
|
||||
else {
|
||||
ObjectPostProcessor<AuthenticationManager> postProcessor = getAuthenticationManagerPostProcessor();
|
||||
ObservationRegistry registry = getObservationRegistry();
|
||||
AuthenticationManager manager = getAuthenticationRegistry().build();
|
||||
if (manager != null) {
|
||||
setSharedObject(AuthenticationManager.class, postProcessor.postProcess(manager));
|
||||
if (!registry.isNoop() && manager != null) {
|
||||
setSharedObject(AuthenticationManager.class, new ObservationAuthenticationManager(registry, manager));
|
||||
}
|
||||
else {
|
||||
setSharedObject(AuthenticationManager.class, manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3692,31 +3634,6 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies webAuthn/passkeys based authentication.
|
||||
*
|
||||
* <pre>
|
||||
* @Bean
|
||||
* SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
* http
|
||||
* // ...
|
||||
* .webAuthn((webAuthn) -> webAuthn
|
||||
* .rpName("Spring Security Relying Party")
|
||||
* .rpId("example.com")
|
||||
* .allowedOrigins("https://example.com")
|
||||
* );
|
||||
* return http.build();
|
||||
* }
|
||||
* </pre>
|
||||
* @param webAuthn the customizer to apply
|
||||
* @return the {@link HttpSecurity} for further customizations
|
||||
* @throws Exception
|
||||
*/
|
||||
public HttpSecurity webAuthn(Customizer<WebAuthnConfigurer<HttpSecurity>> webAuthn) throws Exception {
|
||||
webAuthn.customize(getOrApply(new WebAuthnConfigurer<>()));
|
||||
return HttpSecurity.this;
|
||||
}
|
||||
|
||||
private List<RequestMatcher> createAntMatchers(String... patterns) {
|
||||
List<RequestMatcher> matchers = new ArrayList<>(patterns.length);
|
||||
for (String pattern : patterns) {
|
||||
@@ -3726,9 +3643,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
}
|
||||
|
||||
private List<RequestMatcher> createMvcMatchers(String... mvcPatterns) {
|
||||
ResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class, Object.class);
|
||||
ObjectProvider<ObjectPostProcessor<Object>> postProcessors = getContext().getBeanProvider(type);
|
||||
ObjectPostProcessor<Object> opp = postProcessors.getObject();
|
||||
ObjectPostProcessor<Object> opp = getContext().getBean(ObjectPostProcessor.class);
|
||||
if (!getContext().containsBean(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
|
||||
throw new NoSuchBeanDefinitionException("A Bean named " + HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME
|
||||
+ " of type " + HandlerMappingIntrospector.class.getName()
|
||||
@@ -3763,12 +3678,13 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
return apply(configurer);
|
||||
}
|
||||
|
||||
private ObjectPostProcessor<AuthenticationManager> getAuthenticationManagerPostProcessor() {
|
||||
private ObservationRegistry getObservationRegistry() {
|
||||
ApplicationContext context = getContext();
|
||||
ResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class,
|
||||
AuthenticationManager.class);
|
||||
ObjectProvider<ObjectPostProcessor<AuthenticationManager>> manager = context.getBeanProvider(type);
|
||||
return manager.getIfUnique(ObjectPostProcessor::identity);
|
||||
String[] names = context.getBeanNamesForType(ObservationRegistry.class);
|
||||
if (names.length == 1) {
|
||||
return (ObservationRegistry) context.getBean(names[0]);
|
||||
}
|
||||
return ObservationRegistry.NOOP;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+8
-36
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -28,16 +28,14 @@ import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||
@@ -47,8 +45,8 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
import org.springframework.security.web.FilterChainProxy.FilterChainDecorator;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
import org.springframework.security.web.ObservationFilterChainDecorator;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator;
|
||||
import org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator.HttpServletRequestTransformer;
|
||||
@@ -65,7 +63,6 @@ import org.springframework.security.web.firewall.HttpStatusRequestRejectedHandle
|
||||
import org.springframework.security.web.firewall.ObservationMarkingRequestRejectedHandler;
|
||||
import org.springframework.security.web.firewall.RequestRejectedHandler;
|
||||
import org.springframework.security.web.firewall.StrictHttpFirewall;
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -112,9 +109,6 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
|
||||
private ObservationRegistry observationRegistry = ObservationRegistry.NOOP;
|
||||
|
||||
private ObjectPostProcessor<FilterChainDecorator> filterChainDecoratorPostProcessor = ObjectPostProcessor
|
||||
.identity();
|
||||
|
||||
private HttpServletRequestTransformer privilegeEvaluatorRequestTransformer;
|
||||
|
||||
private DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
|
||||
@@ -135,14 +129,6 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
super(objectPostProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated(since = "6.4", forRemoval = true)
|
||||
public WebSecurity(org.springframework.security.config.annotation.ObjectPostProcessor<Object> objectPostProcessor) {
|
||||
super(objectPostProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Allows adding {@link RequestMatcher} instances that Spring Security should ignore.
|
||||
@@ -310,20 +296,8 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
requestMatcherPrivilegeEvaluatorsEntries
|
||||
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
|
||||
}
|
||||
DefaultSecurityFilterChain anyRequestFilterChain = null;
|
||||
for (SecurityBuilder<? extends SecurityFilterChain> securityFilterChainBuilder : this.securityFilterChainBuilders) {
|
||||
SecurityFilterChain securityFilterChain = securityFilterChainBuilder.build();
|
||||
if (anyRequestFilterChain != null) {
|
||||
String message = "A filter chain that matches any request [" + anyRequestFilterChain
|
||||
+ "] has already been configured, which means that this filter chain [" + securityFilterChain
|
||||
+ "] will never get invoked. Please use `HttpSecurity#securityMatcher` to ensure that there is only one filter chain configured for 'any request' and that the 'any request' filter chain is published last.";
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
if (securityFilterChain instanceof DefaultSecurityFilterChain defaultSecurityFilterChain) {
|
||||
if (defaultSecurityFilterChain.getRequestMatcher() instanceof AnyRequestMatcher) {
|
||||
anyRequestFilterChain = defaultSecurityFilterChain;
|
||||
}
|
||||
}
|
||||
securityFilterChains.add(securityFilterChain);
|
||||
requestMatcherPrivilegeEvaluatorsEntries
|
||||
.add(getRequestMatcherPrivilegeEvaluatorsEntry(securityFilterChain));
|
||||
@@ -418,11 +392,6 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
}
|
||||
ResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class,
|
||||
FilterChainDecorator.class);
|
||||
ObjectProvider<ObjectPostProcessor<FilterChainDecorator>> postProcessor = applicationContext
|
||||
.getBeanProvider(type);
|
||||
this.filterChainDecoratorPostProcessor = postProcessor.getIfUnique(ObjectPostProcessor::identity);
|
||||
Class<HttpServletRequestTransformer> requestTransformerClass = HttpServletRequestTransformer.class;
|
||||
this.privilegeEvaluatorRequestTransformer = applicationContext.getBeanProvider(requestTransformerClass)
|
||||
.getIfUnique();
|
||||
@@ -433,8 +402,11 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
FilterChainDecorator getFilterChainDecorator() {
|
||||
return this.filterChainDecoratorPostProcessor.postProcess(new FilterChainProxy.VirtualFilterChainDecorator());
|
||||
FilterChainProxy.FilterChainDecorator getFilterChainDecorator() {
|
||||
if (this.observationRegistry.isNoop()) {
|
||||
return new FilterChainProxy.VirtualFilterChainDecorator();
|
||||
}
|
||||
return new ObservationFilterChainDecorator(this.observationRegistry);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+1
-1
@@ -82,7 +82,7 @@ import org.springframework.security.web.SecurityFilterChain;
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,
|
||||
HttpSecurityConfiguration.class, ObservationImportSelector.class })
|
||||
HttpSecurityConfiguration.class })
|
||||
@EnableGlobalAuthentication
|
||||
public @interface EnableWebSecurity {
|
||||
|
||||
|
||||
+17
-16
@@ -20,6 +20,7 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -29,7 +30,7 @@ import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.security.authentication.AuthenticationEventPublisher;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.DefaultAuthenticationEventPublisher;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
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.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
|
||||
@@ -55,7 +56,6 @@ import static org.springframework.security.config.Customizer.withDefaults;
|
||||
*
|
||||
* @author Eleftheria Stein
|
||||
* @author Jinwoo Bae
|
||||
* @author Ngoc Nhan
|
||||
* @since 5.4
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@@ -178,17 +178,6 @@ class HttpSecurityConfiguration {
|
||||
this.defaultPasswordEncoder = defaultPasswordEncoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated(since = "6.4", forRemoval = true)
|
||||
DefaultPasswordEncoderAuthenticationManagerBuilder(
|
||||
org.springframework.security.config.annotation.ObjectPostProcessor<Object> objectPostProcessor,
|
||||
PasswordEncoder defaultPasswordEncoder) {
|
||||
super(objectPostProcessor);
|
||||
this.defaultPasswordEncoder = defaultPasswordEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> inMemoryAuthentication()
|
||||
throws Exception {
|
||||
@@ -237,9 +226,21 @@ class HttpSecurityConfiguration {
|
||||
if (this.passwordEncoder != null) {
|
||||
return this.passwordEncoder;
|
||||
}
|
||||
this.passwordEncoder = this.applicationContext.getBeanProvider(PasswordEncoder.class)
|
||||
.getIfUnique(PasswordEncoderFactories::createDelegatingPasswordEncoder);
|
||||
return this.passwordEncoder;
|
||||
PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
|
||||
if (passwordEncoder == null) {
|
||||
passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
|
||||
}
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
return passwordEncoder;
|
||||
}
|
||||
|
||||
private <T> T getBeanOrNull(Class<T> type) {
|
||||
try {
|
||||
return this.applicationContext.getBean(type);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+27
-42
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,6 +29,7 @@ import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
@@ -50,13 +51,11 @@ import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
|
||||
import org.springframework.security.oauth2.client.PasswordOAuth2AuthorizedClientProvider;
|
||||
import org.springframework.security.oauth2.client.RefreshTokenOAuth2AuthorizedClientProvider;
|
||||
import org.springframework.security.oauth2.client.TokenExchangeOAuth2AuthorizedClientProvider;
|
||||
import org.springframework.security.oauth2.client.endpoint.JwtBearerGrantRequest;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2ClientCredentialsGrantRequest;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2PasswordGrantRequest;
|
||||
import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;
|
||||
import org.springframework.security.oauth2.client.endpoint.TokenExchangeGrantRequest;
|
||||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
|
||||
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
|
||||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
|
||||
@@ -117,19 +116,11 @@ final class OAuth2ClientConfiguration {
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
static class OAuth2ClientWebMvcSecurityConfiguration implements WebMvcConfigurer {
|
||||
|
||||
private final OAuth2AuthorizedClientManager authorizedClientManager;
|
||||
private OAuth2AuthorizedClientManager authorizedClientManager;
|
||||
|
||||
private final ObjectProvider<SecurityContextHolderStrategy> securityContextHolderStrategy;
|
||||
private SecurityContextHolderStrategy securityContextHolderStrategy;
|
||||
|
||||
private final OAuth2AuthorizedClientManagerRegistrar authorizedClientManagerRegistrar;
|
||||
|
||||
OAuth2ClientWebMvcSecurityConfiguration(ObjectProvider<OAuth2AuthorizedClientManager> authorizedClientManager,
|
||||
ObjectProvider<SecurityContextHolderStrategy> securityContextHolderStrategy,
|
||||
OAuth2AuthorizedClientManagerRegistrar authorizedClientManagerRegistrar) {
|
||||
this.authorizedClientManager = authorizedClientManager.getIfUnique();
|
||||
this.securityContextHolderStrategy = securityContextHolderStrategy;
|
||||
this.authorizedClientManagerRegistrar = authorizedClientManagerRegistrar;
|
||||
}
|
||||
private OAuth2AuthorizedClientManagerRegistrar authorizedClientManagerRegistrar;
|
||||
|
||||
@Override
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
@@ -137,11 +128,31 @@ final class OAuth2ClientConfiguration {
|
||||
if (authorizedClientManager != null) {
|
||||
OAuth2AuthorizedClientArgumentResolver resolver = new OAuth2AuthorizedClientArgumentResolver(
|
||||
authorizedClientManager);
|
||||
this.securityContextHolderStrategy.ifAvailable(resolver::setSecurityContextHolderStrategy);
|
||||
if (this.securityContextHolderStrategy != null) {
|
||||
resolver.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
||||
}
|
||||
argumentResolvers.add(resolver);
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setAuthorizedClientManager(List<OAuth2AuthorizedClientManager> authorizedClientManagers) {
|
||||
if (authorizedClientManagers.size() == 1) {
|
||||
this.authorizedClientManager = authorizedClientManagers.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy) {
|
||||
this.securityContextHolderStrategy = strategy;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
void setAuthorizedClientManagerRegistrar(
|
||||
OAuth2AuthorizedClientManagerRegistrar authorizedClientManagerRegistrar) {
|
||||
this.authorizedClientManagerRegistrar = authorizedClientManagerRegistrar;
|
||||
}
|
||||
|
||||
private OAuth2AuthorizedClientManager getAuthorizedClientManager() {
|
||||
if (this.authorizedClientManager != null) {
|
||||
return this.authorizedClientManager;
|
||||
@@ -172,8 +183,7 @@ final class OAuth2ClientConfiguration {
|
||||
RefreshTokenOAuth2AuthorizedClientProvider.class,
|
||||
ClientCredentialsOAuth2AuthorizedClientProvider.class,
|
||||
PasswordOAuth2AuthorizedClientProvider.class,
|
||||
JwtBearerOAuth2AuthorizedClientProvider.class,
|
||||
TokenExchangeOAuth2AuthorizedClientProvider.class
|
||||
JwtBearerOAuth2AuthorizedClientProvider.class
|
||||
);
|
||||
// @formatter:on
|
||||
|
||||
@@ -245,12 +255,6 @@ final class OAuth2ClientConfiguration {
|
||||
authorizedClientProviders.add(jwtBearerAuthorizedClientProvider);
|
||||
}
|
||||
|
||||
OAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider = getTokenExchangeAuthorizedClientProvider(
|
||||
authorizedClientProviderBeans);
|
||||
if (tokenExchangeAuthorizedClientProvider != null) {
|
||||
authorizedClientProviders.add(tokenExchangeAuthorizedClientProvider);
|
||||
}
|
||||
|
||||
authorizedClientProviders.addAll(getAdditionalAuthorizedClientProviders(authorizedClientProviderBeans));
|
||||
authorizedClientProvider = new DelegatingOAuth2AuthorizedClientProvider(authorizedClientProviders);
|
||||
}
|
||||
@@ -360,25 +364,6 @@ final class OAuth2ClientConfiguration {
|
||||
return authorizedClientProvider;
|
||||
}
|
||||
|
||||
private OAuth2AuthorizedClientProvider getTokenExchangeAuthorizedClientProvider(
|
||||
Collection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {
|
||||
TokenExchangeOAuth2AuthorizedClientProvider authorizedClientProvider = getAuthorizedClientProviderByType(
|
||||
authorizedClientProviders, TokenExchangeOAuth2AuthorizedClientProvider.class);
|
||||
|
||||
OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> accessTokenResponseClient = getBeanOfType(
|
||||
ResolvableType.forClassWithGenerics(OAuth2AccessTokenResponseClient.class,
|
||||
TokenExchangeGrantRequest.class));
|
||||
if (accessTokenResponseClient != null) {
|
||||
if (authorizedClientProvider == null) {
|
||||
authorizedClientProvider = new TokenExchangeOAuth2AuthorizedClientProvider();
|
||||
}
|
||||
|
||||
authorizedClientProvider.setAccessTokenResponseClient(accessTokenResponseClient);
|
||||
}
|
||||
|
||||
return authorizedClientProvider;
|
||||
}
|
||||
|
||||
private List<OAuth2AuthorizedClientProvider> getAdditionalAuthorizedClientProviders(
|
||||
Collection<OAuth2AuthorizedClientProvider> authorizedClientProviders) {
|
||||
List<OAuth2AuthorizedClientProvider> additionalAuthorizedClientProviders = new ArrayList<>(
|
||||
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.web.configuration;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.ObservationAuthenticationManager;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||
import org.springframework.security.web.FilterChainProxy.FilterChainDecorator;
|
||||
import org.springframework.security.web.ObservationFilterChainDecorator;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
class ObservationConfiguration {
|
||||
|
||||
private static final SecurityObservationSettings all = SecurityObservationSettings.withDefaults()
|
||||
.shouldObserveRequests(true)
|
||||
.shouldObserveAuthentications(true)
|
||||
.shouldObserveAuthorizations(true)
|
||||
.build();
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static ObjectPostProcessor<AuthorizationManager<HttpServletRequest>> webAuthorizationManagerPostProcessor(
|
||||
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||
return new ObjectPostProcessor<>() {
|
||||
@Override
|
||||
public AuthorizationManager postProcess(AuthorizationManager object) {
|
||||
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthorizations();
|
||||
return active ? new ObservationAuthorizationManager<>(r, object) : object;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static ObjectPostProcessor<AuthenticationManager> authenticationManagerPostProcessor(
|
||||
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||
return new ObjectPostProcessor<>() {
|
||||
@Override
|
||||
public AuthenticationManager postProcess(AuthenticationManager object) {
|
||||
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveAuthentications();
|
||||
return active ? new ObservationAuthenticationManager(r, object) : object;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static ObjectPostProcessor<FilterChainDecorator> filterChainDecoratorPostProcessor(
|
||||
ObjectProvider<ObservationRegistry> registry, ObjectProvider<SecurityObservationSettings> predicate) {
|
||||
return new ObjectPostProcessor<>() {
|
||||
@Override
|
||||
public FilterChainDecorator postProcess(FilterChainDecorator object) {
|
||||
ObservationRegistry r = registry.getIfUnique(() -> ObservationRegistry.NOOP);
|
||||
boolean active = !r.isNoop() && predicate.getIfUnique(() -> all).shouldObserveRequests();
|
||||
return active ? new ObservationFilterChainDecorator(r) : object;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.web.configuration;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Used by {@link EnableWebSecurity} to conditionally import observation configuration
|
||||
* when {@link ObservationRegistry} is present.
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 6.4
|
||||
*/
|
||||
class ObservationImportSelector implements ImportSelector {
|
||||
|
||||
private static final boolean observabilityPresent;
|
||||
|
||||
static {
|
||||
ClassLoader classLoader = ObservationImportSelector.class.getClassLoader();
|
||||
observabilityPresent = ClassUtils.isPresent("io.micrometer.observation.ObservationRegistry", classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
|
||||
if (!observabilityPresent) {
|
||||
return new String[0];
|
||||
}
|
||||
return new String[] { ObservationConfiguration.class.getName() };
|
||||
}
|
||||
|
||||
}
|
||||
+1
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -40,7 +40,6 @@ import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.expression.BeanFactoryResolver;
|
||||
import org.springframework.expression.BeanResolver;
|
||||
import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
@@ -83,22 +82,18 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
|
||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
.getContextHolderStrategy();
|
||||
|
||||
private AnnotationTemplateExpressionDefaults templateDefaults;
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
|
||||
AuthenticationPrincipalArgumentResolver authenticationPrincipalResolver = new AuthenticationPrincipalArgumentResolver();
|
||||
authenticationPrincipalResolver.setBeanResolver(this.beanResolver);
|
||||
authenticationPrincipalResolver.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
||||
authenticationPrincipalResolver.setTemplateDefaults(this.templateDefaults);
|
||||
argumentResolvers.add(authenticationPrincipalResolver);
|
||||
argumentResolvers
|
||||
.add(new org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver());
|
||||
CurrentSecurityContextArgumentResolver currentSecurityContextArgumentResolver = new CurrentSecurityContextArgumentResolver();
|
||||
currentSecurityContextArgumentResolver.setBeanResolver(this.beanResolver);
|
||||
currentSecurityContextArgumentResolver.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
||||
currentSecurityContextArgumentResolver.setTemplateDefaults(this.templateDefaults);
|
||||
argumentResolvers.add(currentSecurityContextArgumentResolver);
|
||||
argumentResolvers.add(new CsrfTokenArgumentResolver());
|
||||
}
|
||||
@@ -114,9 +109,6 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex
|
||||
if (applicationContext.getBeanNamesForType(SecurityContextHolderStrategy.class).length == 1) {
|
||||
this.securityContextHolderStrategy = applicationContext.getBean(SecurityContextHolderStrategy.class);
|
||||
}
|
||||
if (applicationContext.getBeanNamesForType(AnnotationTemplateExpressionDefaults.class).length == 1) {
|
||||
this.templateDefaults = applicationContext.getBean(AnnotationTemplateExpressionDefaults.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-1
@@ -38,7 +38,7 @@ import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
@@ -79,6 +79,9 @@ public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAwa
|
||||
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
@Autowired(required = false)
|
||||
private ObjectPostProcessor<Object> objectObjectPostProcessor;
|
||||
|
||||
@Autowired(required = false)
|
||||
private HttpSecurity httpSecurity;
|
||||
|
||||
|
||||
+2
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -17,7 +17,7 @@
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
@@ -54,17 +54,6 @@ public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T,
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated(since = "6.4", forRemoval = true)
|
||||
@SuppressWarnings("unchecked")
|
||||
public T withObjectPostProcessor(
|
||||
org.springframework.security.config.annotation.ObjectPostProcessor<?> objectPostProcessor) {
|
||||
addObjectPostProcessor(objectPostProcessor);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
protected SecurityContextHolderStrategy getSecurityContextHolderStrategy() {
|
||||
if (this.securityContextHolderStrategy != null) {
|
||||
return this.securityContextHolderStrategy;
|
||||
|
||||
+21
-92
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 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,14 +17,12 @@
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
||||
@@ -32,13 +30,12 @@ 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.AuthorizationManagers;
|
||||
import org.springframework.security.authorization.ObservationAuthorizationManager;
|
||||
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.access.intercept.AuthorizationFilter;
|
||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||
import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;
|
||||
@@ -68,9 +65,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
|
||||
private String rolePrefix = "ROLE_";
|
||||
|
||||
private ObjectPostProcessor<AuthorizationManager<HttpServletRequest>> postProcessor = ObjectPostProcessor
|
||||
.identity();
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
* @param context the {@link ApplicationContext} to use
|
||||
@@ -90,11 +84,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBean(GrantedAuthorityDefaults.class);
|
||||
this.rolePrefix = grantedAuthorityDefaults.getRolePrefix();
|
||||
}
|
||||
ResolvableType type = ResolvableType.forClassWithGenerics(ObjectPostProcessor.class,
|
||||
ResolvableType.forClassWithGenerics(AuthorizationManager.class, HttpServletRequest.class));
|
||||
ObjectProvider<ObjectPostProcessor<AuthorizationManager<HttpServletRequest>>> provider = context
|
||||
.getBeanProvider(type);
|
||||
provider.ifUnique((postProcessor) -> this.postProcessor = postProcessor);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,6 +120,17 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
private ObservationRegistry getObservationRegistry() {
|
||||
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
|
||||
String[] names = context.getBeanNamesForType(ObservationRegistry.class);
|
||||
if (names.length == 1) {
|
||||
return context.getBean(ObservationRegistry.class);
|
||||
}
|
||||
else {
|
||||
return ObservationRegistry.NOOP;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registry for mapping a {@link RequestMatcher} to an {@link AuthorizationManager}.
|
||||
*
|
||||
@@ -170,9 +170,13 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
+ ". Try completing it with something like requestUrls().<something>.hasRole('USER')");
|
||||
Assert.state(this.mappingCount > 0,
|
||||
"At least one mapping is required (for example, authorizeHttpRequests().anyRequest().authenticated())");
|
||||
ObservationRegistry registry = getObservationRegistry();
|
||||
AuthorizationManager<HttpServletRequest> manager = postProcess(
|
||||
(AuthorizationManager<HttpServletRequest>) this.managerBuilder.build());
|
||||
return AuthorizeHttpRequestsConfigurer.this.postProcessor.postProcess(manager);
|
||||
if (registry.isNoop()) {
|
||||
return manager;
|
||||
}
|
||||
return new ObservationAuthorizationManager<>(registry, manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -193,16 +197,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated(since = "6.4", forRemoval = true)
|
||||
public AuthorizationManagerRequestMatcherRegistry withObjectPostProcessor(
|
||||
org.springframework.security.config.annotation.ObjectPostProcessor<?> objectPostProcessor) {
|
||||
addObjectPostProcessor(objectPostProcessor);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether all dispatcher types should be filtered.
|
||||
* @param shouldFilter should filter all dispatcher types. Default is {@code true}
|
||||
@@ -251,14 +245,11 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
* {@link RequestMatcher}s.
|
||||
*
|
||||
* @author Evgeniy Cheban
|
||||
* @author Josh Cummings
|
||||
*/
|
||||
public class AuthorizedUrl {
|
||||
|
||||
private final List<? extends RequestMatcher> matchers;
|
||||
|
||||
private boolean not;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
* @param matchers the {@link RequestMatcher} instances to map
|
||||
@@ -271,16 +262,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
return this.matchers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Negates the following authorization rule.
|
||||
* @return the {@link AuthorizedUrl} for further customization
|
||||
* @since 6.3
|
||||
*/
|
||||
public AuthorizedUrl not() {
|
||||
this.not = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that URLs are allowed by anyone.
|
||||
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||
@@ -393,21 +374,6 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
return access(AuthenticatedAuthorizationManager.anonymous());
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify that a path variable in URL to be compared.
|
||||
*
|
||||
* <p>
|
||||
* For example, <pre>
|
||||
* requestMatchers("/user/{username}").hasVariable("username").equalTo(Authentication::getName)
|
||||
* </pre>
|
||||
* @param variable the variable in URL template to compare.
|
||||
* @return {@link AuthorizedUrlVariable} for further customization.
|
||||
* @since 6.3
|
||||
*/
|
||||
public AuthorizedUrlVariable hasVariable(String variable) {
|
||||
return new AuthorizedUrlVariable(variable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows specifying a custom {@link AuthorizationManager}.
|
||||
* @param manager the {@link AuthorizationManager} to use
|
||||
@@ -417,44 +383,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||
public AuthorizationManagerRequestMatcherRegistry access(
|
||||
AuthorizationManager<RequestAuthorizationContext> manager) {
|
||||
Assert.notNull(manager, "manager cannot be null");
|
||||
return (this.not)
|
||||
? AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, AuthorizationManagers.not(manager))
|
||||
: AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that allows configuring {@link RequestMatcher}s with URI path
|
||||
* variables
|
||||
*
|
||||
* @author Taehong Kim
|
||||
* @since 6.3
|
||||
*/
|
||||
public final class AuthorizedUrlVariable {
|
||||
|
||||
private final String variable;
|
||||
|
||||
private AuthorizedUrlVariable(String variable) {
|
||||
this.variable = variable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the value of a path variable in the URI with an `Authentication`
|
||||
* attribute
|
||||
* <p>
|
||||
* For example, <pre>
|
||||
* requestMatchers("/user/{username}").hasVariable("username").equalTo(Authentication::getName));
|
||||
* </pre>
|
||||
* @param function a function to get value from {@link Authentication}.
|
||||
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
|
||||
* customization.
|
||||
*/
|
||||
public AuthorizationManagerRequestMatcherRegistry equalTo(Function<Authentication, String> function) {
|
||||
return access((auth, requestContext) -> {
|
||||
String value = requestContext.getVariables().get(this.variable);
|
||||
return new AuthorizationDecision(function.apply(auth.get()).equals(value));
|
||||
});
|
||||
}
|
||||
|
||||
return AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2023 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,7 +25,7 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.SecurityBuilder;
|
||||
import org.springframework.security.config.annotation.SecurityConfigurer;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
@@ -169,16 +169,6 @@ public final class ChannelSecurityConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated(since = "6.4", forRemoval = true)
|
||||
public ChannelRequestMatcherRegistry withObjectPostProcessor(
|
||||
org.springframework.security.config.annotation.ObjectPostProcessor<?> objectPostProcessor) {
|
||||
addObjectPostProcessor(objectPostProcessor);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ChannelProcessor} instances to use in
|
||||
* {@link ChannelDecisionManagerImpl}
|
||||
|
||||
-2
@@ -26,7 +26,6 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
|
||||
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
|
||||
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
|
||||
import org.springframework.security.web.csrf.CsrfToken;
|
||||
|
||||
/**
|
||||
@@ -99,7 +98,6 @@ public final class DefaultLoginPageConfigurer<H extends HttpSecurityBuilder<H>>
|
||||
if (this.loginPageGeneratingFilter.isEnabled() && authenticationEntryPoint == null) {
|
||||
this.loginPageGeneratingFilter = postProcess(this.loginPageGeneratingFilter);
|
||||
http.addFilter(this.loginPageGeneratingFilter);
|
||||
http.addFilter(DefaultResourcesFilter.css());
|
||||
LogoutConfigurer<H> logoutConfigurer = http.getConfigurer(LogoutConfigurer.class);
|
||||
if (logoutConfigurer != null) {
|
||||
http.addFilter(this.logoutPageGeneratingFilter);
|
||||
|
||||
+22
-21
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -29,7 +29,7 @@ import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.config.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||
@@ -75,7 +75,6 @@ import org.springframework.util.StringUtils;
|
||||
* @param <H> the type of {@link HttpSecurityBuilder} that is being configured
|
||||
* @author Rob Winch
|
||||
* @author Yanming Zhou
|
||||
* @author Ngoc Nhan
|
||||
* @since 3.2
|
||||
* @see org.springframework.security.config.annotation.web.builders.HttpSecurity#authorizeRequests()
|
||||
* @deprecated Use {@link AuthorizeHttpRequestsConfigurer} instead
|
||||
@@ -107,9 +106,10 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
|
||||
* @see HttpSecurity#authorizeRequests()
|
||||
*/
|
||||
public ExpressionUrlAuthorizationConfigurer(ApplicationContext context) {
|
||||
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBeanProvider(GrantedAuthorityDefaults.class)
|
||||
.getIfUnique();
|
||||
if (grantedAuthorityDefaults != null) {
|
||||
String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class);
|
||||
if (grantedAuthorityDefaultsBeanNames.length == 1) {
|
||||
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBean(grantedAuthorityDefaultsBeanNames[0],
|
||||
GrantedAuthorityDefaults.class);
|
||||
this.rolePrefix = grantedAuthorityDefaults.getRolePrefix();
|
||||
}
|
||||
else {
|
||||
@@ -167,11 +167,22 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
|
||||
}
|
||||
ApplicationContext context = http.getSharedObject(ApplicationContext.class);
|
||||
if (context != null) {
|
||||
context.getBeanProvider(RoleHierarchy.class).ifUnique(defaultHandler::setRoleHierarchy);
|
||||
context.getBeanProvider(GrantedAuthorityDefaults.class)
|
||||
.ifUnique((grantedAuthorityDefaults) -> defaultHandler
|
||||
.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix()));
|
||||
context.getBeanProvider(PermissionEvaluator.class).ifUnique(defaultHandler::setPermissionEvaluator);
|
||||
String[] roleHiearchyBeanNames = context.getBeanNamesForType(RoleHierarchy.class);
|
||||
if (roleHiearchyBeanNames.length == 1) {
|
||||
defaultHandler.setRoleHierarchy(context.getBean(roleHiearchyBeanNames[0], RoleHierarchy.class));
|
||||
}
|
||||
String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class);
|
||||
if (grantedAuthorityDefaultsBeanNames.length == 1) {
|
||||
GrantedAuthorityDefaults grantedAuthorityDefaults = context
|
||||
.getBean(grantedAuthorityDefaultsBeanNames[0], GrantedAuthorityDefaults.class);
|
||||
defaultHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
|
||||
}
|
||||
String[] permissionEvaluatorBeanNames = context.getBeanNamesForType(PermissionEvaluator.class);
|
||||
if (permissionEvaluatorBeanNames.length == 1) {
|
||||
PermissionEvaluator permissionEvaluator = context.getBean(permissionEvaluatorBeanNames[0],
|
||||
PermissionEvaluator.class);
|
||||
defaultHandler.setPermissionEvaluator(permissionEvaluator);
|
||||
}
|
||||
}
|
||||
this.expressionHandler = postProcess(defaultHandler);
|
||||
return this.expressionHandler;
|
||||
@@ -238,16 +249,6 @@ public final class ExpressionUrlAuthorizationConfigurer<H extends HttpSecurityBu
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated(since = "6.4", forRemoval = true)
|
||||
public ExpressionInterceptUrlRegistry withObjectPostProcessor(
|
||||
org.springframework.security.config.annotation.ObjectPostProcessor<?> objectPostProcessor) {
|
||||
addObjectPostProcessor(objectPostProcessor);
|
||||
return this;
|
||||
}
|
||||
|
||||
public H and() {
|
||||
return ExpressionUrlAuthorizationConfigurer.this.and();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user