Compare commits
206 Commits
7.0.5
..
docs-build
| Author | SHA1 | Date | |
|---|---|---|---|
| e448e122b8 | |||
| 3b992b33d1 | |||
| 5c2f33c03b | |||
| 116e29ff2f | |||
| d2ecc270fd | |||
| 7be81ead2e | |||
| a0f4340f3f | |||
| 23bac293f2 | |||
| b340936cb2 | |||
| de66811c51 | |||
| fe7bbd36d8 | |||
| dfc06488bd | |||
| 392dcf4b72 | |||
| 2fd4caa169 | |||
| 678d3ab242 | |||
| 9730db490c | |||
| 5b9d225816 | |||
| ba69471f6f | |||
| 1c66fcd510 | |||
| 86bd220997 | |||
| 9f3d60ebf3 | |||
| 67cff1140e | |||
| db898fd9cf | |||
| da88ee4dc4 | |||
| 6671432a5b | |||
| f77f801fed | |||
| aa0e22911a | |||
| 29bf045c65 | |||
| 38702d4eb9 | |||
| 9f214978a4 | |||
| cc6f1d65b2 | |||
| 0a540d2cb0 | |||
| 8316c38b1e | |||
| 1a96ec4539 | |||
| 57dacd975f | |||
| cdcc9130a1 | |||
| d3e0e197e8 | |||
| 9aa28236cd | |||
| 38549ca60a | |||
| 90258a5a85 | |||
| f5d53c31b0 | |||
| 23008b97d4 | |||
| ec3df2ed66 | |||
| 92225c8b8d | |||
| 8f83df8321 | |||
| 93f7296492 | |||
| 6e540b20b7 | |||
| 42a6b9f57e | |||
| 301f447680 | |||
| 5e0aa3e29f | |||
| 4f929d1a8f | |||
| 009bfd3c44 | |||
| 5c7651012a | |||
| 5bbb75923c | |||
| ed3fffcf48 | |||
| 0701464480 | |||
| 72f0f4eb74 | |||
| be4fafde5d | |||
| 928b4147a4 | |||
| 3fae3e72b7 | |||
| 7a7aed07af | |||
| 8f681accb7 | |||
| 342f7f89a1 | |||
| 5885bd6f95 | |||
| ac2dd3863d | |||
| 9fb2ceceb8 | |||
| a5cae32a72 | |||
| ad14fed8d8 | |||
| 43edfccc25 | |||
| 3d48b7060f | |||
| 891adb60f9 | |||
| 536c12703e | |||
| 2e72c2a174 | |||
| abc06ea10c | |||
| 7dca6269bb | |||
| e56bcb3cfd | |||
| 816736d0db | |||
| 4a4410c40e | |||
| 78eca084f1 | |||
| 16d9a90e2c | |||
| 12c03dd5a3 | |||
| b7888bc6d1 | |||
| 845a423135 | |||
| ace979014f | |||
| e4eeeb95c2 | |||
| d44606f362 | |||
| ed1ef9b889 | |||
| cd04ea3675 | |||
| ce6ba7fb9d | |||
| 7d398dce6b | |||
| cf339d682b | |||
| 477276b52e | |||
| 3104bdb90d | |||
| 20814c6aa6 | |||
| b156b25602 | |||
| e5c05bc746 | |||
| e001a53454 | |||
| 3bf79cc70c | |||
| e7468571ae | |||
| f11a669dc7 | |||
| 629e2ea110 | |||
| baed662780 | |||
| 5536e21690 | |||
| 06961caba7 | |||
| cfd02619cf | |||
| 7285fc8b52 | |||
| c27dcf03b5 | |||
| 46d52e8fee | |||
| ac3935da09 | |||
| dcc044a654 | |||
| 71c9c82233 | |||
| 5930496049 | |||
| a469b2e229 | |||
| ed73d77e33 | |||
| 85ccc20121 | |||
| 8ba2c34175 | |||
| 1485ad6951 | |||
| bcdd981578 | |||
| ed6205da5e | |||
| 991939af06 | |||
| f7363052de | |||
| 1e9893122b | |||
| 75a280b68d | |||
| 4414d76720 | |||
| eb66d1ba41 | |||
| 0a5f56e0de | |||
| a5c8d737bd | |||
| a69cd7d5d9 | |||
| 17444d56a8 | |||
| bda6e79e7c | |||
| 0df1dea1f8 | |||
| 7c562e2c88 | |||
| 6f2d43e03d | |||
| 48d5488185 | |||
| bdbeffc901 | |||
| 1b954f1383 | |||
| fce9d893f9 | |||
| 8aa641410a | |||
| 058a49ad3d | |||
| 99afa32fae | |||
| 61f33d2d29 | |||
| 739641966f | |||
| 060a8f6cdb | |||
| 02c9df1371 | |||
| b5ff77339b | |||
| 26827157f9 | |||
| cca087f04f | |||
| 06d05691b9 | |||
| 02587c7407 | |||
| 8a56d49d35 | |||
| 8940bc8979 | |||
| 53acce43bc | |||
| d9bb586257 | |||
| c4ab7e2353 | |||
| ff30a7e3db | |||
| 06cb8bfe6a | |||
| 0f0c341cfa | |||
| 576e4d4711 | |||
| 765f4f65fd | |||
| f2aa1c7687 | |||
| fcc344dfa2 | |||
| ac8e3f6810 | |||
| 33b1038eb6 | |||
| 063b48c739 | |||
| 9442191a84 | |||
| ea5260adad | |||
| 1c181dc81c | |||
| fdfbdcc645 | |||
| 5ac63ed64f | |||
| 5855bbb378 | |||
| 409bd29abd | |||
| 2fb91e6266 | |||
| f500c0fbe0 | |||
| c9ee194832 | |||
| 7757eab714 | |||
| dd44928051 | |||
| 35bdfa87a0 | |||
| 51fa7c0c34 | |||
| 7491f8a76d | |||
| b211b55043 | |||
| 32e2793167 | |||
| e2f3ba2245 | |||
| c7d14e0c02 | |||
| 466b882d82 | |||
| d7d1ee2b7e | |||
| 40f6324d8f | |||
| d6ccf679c4 | |||
| c4bff97fe8 | |||
| 649823f401 | |||
| 957566ebd7 | |||
| f2f7eb7482 | |||
| 6bd10b4056 | |||
| 79a23b8b27 | |||
| 3f979ac958 | |||
| bcdde1c850 | |||
| 826f776d9b | |||
| edf2f55db1 | |||
| 93cae95b12 | |||
| f2020a2cf4 | |||
| 7a8ba42d57 | |||
| ca1a83fd06 | |||
| ba7a6d1c64 | |||
| a3024326a3 | |||
| 7cea7b49aa | |||
| c993da749c | |||
| 743e37e481 |
@@ -1,18 +0,0 @@
|
||||
# Normalize line endings to auto.
|
||||
* text auto
|
||||
|
||||
# Ensure that line endings for DOS batch files are not modified.
|
||||
*.bat -text
|
||||
|
||||
# Ensure the following are treated as binary.
|
||||
*.cer binary
|
||||
*.graffle binary
|
||||
*.jar binary
|
||||
*.jpeg binary
|
||||
*.jpg binary
|
||||
*.keystore binary
|
||||
*.odg binary
|
||||
*.otg binary
|
||||
*.png binary
|
||||
*.hsx binary
|
||||
*.serialized binary
|
||||
@@ -1,40 +0,0 @@
|
||||
<!--
|
||||
For Security Vulnerabilities, please use https://spring.io/security-policy
|
||||
-->
|
||||
|
||||
### Summary
|
||||
|
||||
<!--
|
||||
Please provide a high level summary of the issue you are having
|
||||
-->
|
||||
|
||||
### Actual Behavior
|
||||
|
||||
<!--
|
||||
Please describe step by step the behavior you are observing
|
||||
-->
|
||||
|
||||
### Expected Behavior
|
||||
|
||||
<!--
|
||||
Please describe step by step the behavior you expect
|
||||
-->
|
||||
|
||||
### Configuration
|
||||
|
||||
<!--
|
||||
Please provide any configuration you have.
|
||||
-->
|
||||
|
||||
### Version
|
||||
|
||||
<!--
|
||||
Please describe what version you are using. Does the problem occur in other versions?
|
||||
-->
|
||||
|
||||
### Sample
|
||||
|
||||
<!--
|
||||
Providing a complete sample (i.e. link to a github repository) will give this issue higher
|
||||
priority than issues that do not have a complete sample
|
||||
-->
|
||||
@@ -1,28 +0,0 @@
|
||||
---
|
||||
name: Bug
|
||||
about: Create a bug report to help us improve
|
||||
title: ''
|
||||
labels: 'status: waiting-for-triage, type: bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Do NOT report Security Vulnerabilities here. Please use https://github.com/spring-projects/spring-security/security/policy
|
||||
-->
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Sample**
|
||||
|
||||
A link to a GitHub repository with a [minimal, reproducible sample](https://stackoverflow.com/help/minimal-reproducible-example).
|
||||
|
||||
Reports that include a sample will take priority over reports that do not.
|
||||
At times, we may require a sample, so it is good to try and include a sample up front.
|
||||
@@ -1,5 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Community Support
|
||||
url: https://stackoverflow.com/questions/tagged/spring-security
|
||||
about: Please ask and answer questions on StackOverflow with the tag `spring-security`.
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
name: Enhancement
|
||||
about: Suggest an enhancement for this project
|
||||
title: ''
|
||||
labels: 'status: waiting-for-triage, type: enhancement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Expected Behavior**
|
||||
|
||||
<!--- Tell us how it should work -->
|
||||
|
||||
**Current Behavior**
|
||||
|
||||
<!--- Explain the difference from current behavior -->
|
||||
|
||||
**Context**
|
||||
|
||||
<!---
|
||||
How has this issue affected you?
|
||||
What are you trying to accomplish?
|
||||
What other alternatives have you considered?
|
||||
Are you aware of any workarounds?
|
||||
-->
|
||||
@@ -1,9 +0,0 @@
|
||||
<!--
|
||||
For Security Vulnerabilities, please use https://pivotal.io/security#reporting
|
||||
-->
|
||||
|
||||
<!--
|
||||
Before creating new features, we recommend creating an issue to discuss the feature. This ensures that everyone is on the same page before extensive work is done.
|
||||
|
||||
Thanks for contributing to Spring Security. Please provide a brief description of your pull-request and reference any related issue numbers (prefix references with gh-).
|
||||
-->
|
||||
@@ -1,2 +0,0 @@
|
||||
require:
|
||||
members: false
|
||||
@@ -1,113 +0,0 @@
|
||||
version: 2
|
||||
registries:
|
||||
spring-milestones:
|
||||
type: maven-repository
|
||||
url: https://repo.spring.io/milestone
|
||||
shibboleth:
|
||||
type: maven-repository
|
||||
url: https://build.shibboleth.net/maven/releases
|
||||
updates:
|
||||
- package-ecosystem: gradle
|
||||
target-branch: 6.5.x
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
time: '03:00'
|
||||
timezone: Etc/UTC
|
||||
labels:
|
||||
- 'type: dependency-upgrade'
|
||||
registries:
|
||||
- spring-milestones
|
||||
- shibboleth
|
||||
ignore:
|
||||
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
||||
- dependency-name: org.python:jython
|
||||
- dependency-name: org.apache.directory.server:*
|
||||
- dependency-name: org.apache.directory.shared:*
|
||||
- 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.4.x
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
time: '03:00'
|
||||
timezone: Etc/UTC
|
||||
labels:
|
||||
- 'type: dependency-upgrade'
|
||||
registries:
|
||||
- spring-milestones
|
||||
- shibboleth
|
||||
ignore:
|
||||
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
||||
- dependency-name: org.python:jython
|
||||
- dependency-name: org.apache.directory.server:*
|
||||
- dependency-name: org.apache.directory.shared:*
|
||||
- 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: /
|
||||
schedule:
|
||||
interval: daily
|
||||
time: '03:00'
|
||||
timezone: Etc/UTC
|
||||
labels:
|
||||
- 'type: dependency-upgrade'
|
||||
registries:
|
||||
- spring-milestones
|
||||
- shibboleth
|
||||
ignore:
|
||||
- dependency-name: com.nimbusds:nimbus-jose-jwt
|
||||
- dependency-name: org.python:jython
|
||||
- dependency-name: org.apache.directory.server:*
|
||||
- dependency-name: org.apache.directory.shared:*
|
||||
- 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
|
||||
- version-update:semver-minor
|
||||
|
||||
- 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'
|
||||
@@ -1,38 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch: # Manual trigger
|
||||
|
||||
env:
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
snapshot-test:
|
||||
name: Test Against Snapshots
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/test.yml@ed473b4dafba053c63a453d2d88a89df3b3e18b3 # 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,https://oss.sonatype.org/content/repositories/snapshots -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=7.+ -PreactorVersion=2025.+ -PspringDataVersion=2025.+ --stacktrace
|
||||
secrets: inherit
|
||||
send-notification:
|
||||
name: Send Notification
|
||||
needs: [ snapshot-test ]
|
||||
if: ${{ !success() }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Notification
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@ed473b4dafba053c63a453d2d88a89df3b3e18b3 # v1
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
@@ -1,23 +0,0 @@
|
||||
name: Clean build artifacts
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
permissions:
|
||||
contents: none
|
||||
steps:
|
||||
- name: Delete artifacts in cron job
|
||||
env:
|
||||
GH_ACTIONS_REPO_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
run: |
|
||||
echo "Running clean build artifacts logic"
|
||||
output=$(curl -X GET -H "Authorization: token $GH_ACTIONS_REPO_TOKEN" https://api.github.com/repos/spring-projects/spring-security/actions/artifacts | grep '"id"' | cut -d : -f2 | sed 's/,*$//g')
|
||||
echo Output is $output
|
||||
for id in $output; do curl -X DELETE -H "Authorization: token $GH_ACTIONS_REPO_TOKEN" https://api.github.com/repos/spring-projects/spring-security/actions/artifacts/$id; done;
|
||||
@@ -1,17 +1,80 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL Advanced"
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
push: # run if we update the workflow
|
||||
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
|
||||
- cron: '39 13 * * 4'
|
||||
|
||||
jobs:
|
||||
codeql-analysis-call:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||
# - https://gh.io/supported-runners-and-hardware-resources
|
||||
# - https://gh.io/using-larger-runners (GitHub.com only)
|
||||
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# required to fetch internal or private CodeQL packs
|
||||
packages: read
|
||||
|
||||
# only required for workflows in private repositories
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
uses: spring-io/github-actions/.github/workflows/codeql-analysis.yml@e415dadd0910c901e7a7fabd67bbb355b2324500 # 1
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- language: actions
|
||||
build-mode: none
|
||||
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
|
||||
# Use `c-cpp` to analyze code written in C, C++ or both
|
||||
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
||||
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
||||
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
||||
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
||||
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
# Add any setup steps before running the `github/codeql-action/init` action.
|
||||
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
||||
# or others). This is typically only required for manual builds.
|
||||
# - name: Setup runtime (example)
|
||||
# uses: actions/setup-example@v1
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
queries: security-extended,security-and-quality
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
@@ -1,66 +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@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest ]
|
||||
jdk: [ 17 ]
|
||||
with:
|
||||
runs-on: ${{ matrix.os }}
|
||||
java-version: ${{ matrix.jdk }}
|
||||
distribution: temurin
|
||||
secrets: inherit
|
||||
deploy-artifacts:
|
||||
name: Deploy Artifacts
|
||||
needs: [ build]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
with:
|
||||
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
default-publish-milestones-central: true
|
||||
secrets: inherit
|
||||
deploy-schema:
|
||||
name: Deploy Schema
|
||||
needs: [ build ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
with:
|
||||
should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
secrets: inherit
|
||||
perform-release:
|
||||
name: Perform Release
|
||||
needs: [ deploy-artifacts, deploy-schema ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
with:
|
||||
should-perform-release: ${{ needs.deploy-artifacts.outputs.artifacts-deployed }}
|
||||
project-version: ${{ needs.deploy-artifacts.outputs.project-version }}
|
||||
milestone-repo-url: https://repo1.maven.org/maven2
|
||||
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@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
@@ -1,76 +0,0 @@
|
||||
name: Defer Issues
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
defer-issues:
|
||||
name: Defer Issues
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Compute Version
|
||||
id: compute-version
|
||||
uses: spring-io/spring-release-actions/compute-version@2420148725bebe44bd59a575a9b1961ca4459b0b # 0.0.4
|
||||
- name: Get Today's Release Version
|
||||
id: todays-release
|
||||
uses: spring-io/spring-release-actions/get-todays-release-version@2420148725bebe44bd59a575a9b1961ca4459b0b # 0.0.4
|
||||
with:
|
||||
snapshot-version: ${{ steps.compute-version.outputs.version }}
|
||||
milestone-repository: ${{ github.repository }}
|
||||
milestone-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Compute Next Version
|
||||
id: next-version
|
||||
uses: spring-io/spring-release-actions/compute-next-version@2420148725bebe44bd59a575a9b1961ca4459b0b # 0.0.4
|
||||
with:
|
||||
version: ${{ steps.todays-release.outputs.release-version }}
|
||||
- name: Schedule Next Milestone
|
||||
uses: spring-io/spring-release-actions/schedule-milestone@2420148725bebe44bd59a575a9b1961ca4459b0b # 0.0.4
|
||||
with:
|
||||
version: ${{ steps.next-version.outputs.version }}
|
||||
version-date: ${{ steps.next-version.outputs.version-date }}
|
||||
repository: ${{ github.repository }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Move Open Issues to Next Milestone
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CURRENT_MILESTONE: ${{ steps.todays-release.outputs.release-version }}
|
||||
NEXT_MILESTONE: ${{ steps.next-version.outputs.version }}
|
||||
run: |
|
||||
current_milestone_number=$(gh api repos/${{ github.repository }}/milestones \
|
||||
--jq ".[] | select(.title == \"$CURRENT_MILESTONE\") | .number")
|
||||
if [ -z "$current_milestone_number" ]; then
|
||||
echo "No milestone found for $CURRENT_MILESTONE"
|
||||
exit 0
|
||||
fi
|
||||
next_milestone_number=$(gh api repos/${{ github.repository }}/milestones \
|
||||
--jq ".[] | select(.title == \"$NEXT_MILESTONE\") | .number")
|
||||
if [ -z "$next_milestone_number" ]; then
|
||||
echo "No milestone found for $NEXT_MILESTONE"
|
||||
exit 1
|
||||
fi
|
||||
echo "Moving open issues from milestone '$CURRENT_MILESTONE' (#$current_milestone_number) to '$NEXT_MILESTONE' (#$next_milestone_number)"
|
||||
page=1
|
||||
while true; do
|
||||
issues=$(gh api "repos/${{ github.repository }}/issues?milestone=$current_milestone_number&state=open&per_page=100&page=$page" \
|
||||
--jq '.[].number')
|
||||
if [ -z "$issues" ]; then
|
||||
break
|
||||
fi
|
||||
for issue in $issues; do
|
||||
echo "Moving issue/PR #$issue to milestone $NEXT_MILESTONE"
|
||||
gh api repos/${{ github.repository }}/issues/$issue \
|
||||
--method PATCH \
|
||||
--field milestone=$next_milestone_number \
|
||||
--silent
|
||||
done
|
||||
page=$((page + 1))
|
||||
done
|
||||
echo "Done."
|
||||
@@ -1,33 +1,51 @@
|
||||
name: Deploy Docs
|
||||
run-name: ${{ format('{0} ({1})', github.workflow, github.event.inputs.build-refname || 'all') }}
|
||||
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:
|
||||
inputs:
|
||||
build-refname:
|
||||
description: Enter git refname to build (e.g., 5.7.x).
|
||||
required: false
|
||||
push:
|
||||
branches: docs-build
|
||||
env:
|
||||
GRADLE_ENTERPRISE_SECRET_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
permissions: read-all
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@v6
|
||||
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)
|
||||
fetch-depth: 5
|
||||
- name: Set Up Gradle
|
||||
uses: spring-io/spring-gradle-build-action@v2
|
||||
with:
|
||||
# Use JDK 17 since 6.5.x Kotlin version does not support JDK 25 and thus will fail
|
||||
java-version: '17'
|
||||
distribution: temurin
|
||||
- name: Set up refname build
|
||||
if: github.event.inputs.build-refname
|
||||
run: |
|
||||
git fetch --depth 1 https://github.com/$GITHUB_REPOSITORY ${{ github.event.inputs.build-refname }}
|
||||
echo BUILD_REFNAME=${{ github.event.inputs.build-refname }} >> $GITHUB_ENV
|
||||
echo BUILD_VERSION=$(git cat-file --textconv FETCH_HEAD:gradle.properties | sed -n '/^version=/ { s/^version=//;p }') >> $GITHUB_ENV
|
||||
- name: Run Antora
|
||||
run: ./gradlew antora
|
||||
- name: Copy the cache to be included in the site
|
||||
run: cp -rf build/antora/inject-collector-cache-config-extension/.cache build/site/
|
||||
- name: Publish Docs
|
||||
uses: spring-io/spring-doc-actions/rsync-antora-reference@v0.0.22
|
||||
with:
|
||||
docs-username: ${{ secrets.DOCS_USERNAME }}
|
||||
docs-host: ${{ secrets.DOCS_HOST }}
|
||||
docs-ssh-key: ${{ secrets.DOCS_SSH_KEY }}
|
||||
docs-ssh-host-key: ${{ secrets.DOCS_SSH_HOST_KEY }}
|
||||
- name: Bust Clouflare Cache
|
||||
uses: spring-io/spring-doc-actions/bust-cloudflare-antora-cache@v0.0.22
|
||||
with:
|
||||
context-root: spring-security
|
||||
cloudflare-zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID }}
|
||||
cloudflare-cache-token: ${{ secrets.CLOUDFLARE_CACHE_TOKEN }}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
name: Finalize Release
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Manual trigger
|
||||
inputs:
|
||||
version:
|
||||
description: The Spring Security release to finalize (e.g. 7.0.0-RC2)
|
||||
required: true
|
||||
|
||||
env:
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
perform-release:
|
||||
name: Perform Release
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@ed473b4dafba053c63a453d2d88a89df3b3e18b3 # v1
|
||||
with:
|
||||
should-perform-release: true
|
||||
project-version: ${{ inputs.version }}
|
||||
milestone-repo-url: https://repo1.maven.org/maven2
|
||||
release-repo-url: https://repo1.maven.org/maven2
|
||||
artifact-path: org/springframework/security/spring-security-core
|
||||
slack-announcing-id: spring-security-announcing
|
||||
secrets: inherit
|
||||
@@ -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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Set up Gradle
|
||||
uses: gradle/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
||||
- name: Upgrade Wrappers
|
||||
run: ./gradlew clean upgradeGradleWrapperAll --continue -Porg.gradle.java.installations.auto-download=false
|
||||
env:
|
||||
WRAPPER_UPGRADE_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,30 @@
|
||||
name: Merge Dependabot PR
|
||||
|
||||
on: pull_request_target
|
||||
|
||||
run-name: Merge Dependabot PR ${{ github.ref_name }}
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
merge-dependabot-pr:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.user.login == 'dependabot[bot]'
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
show-progress: false
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: Merge Dependabot pull request
|
||||
run: gh pr merge ${{ github.event.pull_request.number }} --auto --rebase
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_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@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@c8668747d7c264864c8c7f7026d0d277d14a78dc # v2.0.6
|
||||
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@c8668747d7c264864c8c7f7026d0d277d14a78dc # v2.0.6
|
||||
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@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: docs
|
||||
path: docs/build/site
|
||||
overwrite: true
|
||||
send-notification:
|
||||
name: Send Notification
|
||||
needs: [ build, generate-docs ]
|
||||
if: ${{ failure() && github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'spring-projects/spring-security' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Notification
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
@@ -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.5.x, 6.4.x, 6.3.x ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
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: [ '6.4.x', '6.5.x', 'main' ]
|
||||
steps:
|
||||
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf # v0.0.22
|
||||
name: Update
|
||||
with:
|
||||
docs-branch: ${{ matrix.branch }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
antora-file-path: 'docs/antora-playbook.yml'
|
||||
update-antora-ui-spring-docs-build:
|
||||
runs-on: ubuntu-latest
|
||||
name: Update on docs-build
|
||||
steps:
|
||||
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf # v0.0.22
|
||||
name: Update
|
||||
with:
|
||||
docs-branch: 'docs-build'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -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@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
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@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
+13
-33
@@ -1,33 +1,13 @@
|
||||
classes/
|
||||
target/
|
||||
*/src/*/java/META-INF
|
||||
*/src/META-INF/
|
||||
*/src/*/java/META-INF/
|
||||
.classpath
|
||||
.springBeans
|
||||
.project
|
||||
.DS_Store
|
||||
.settings/
|
||||
.idea/*
|
||||
out/
|
||||
bin/
|
||||
intellij/
|
||||
build/
|
||||
*.log
|
||||
*.log.*
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.gradle/
|
||||
atlassian-ide-plugin.xml
|
||||
!etc/eclipse/.checkstyle
|
||||
.checkstyle
|
||||
s101plugin.state
|
||||
.attach_pid*
|
||||
.~lock.*#
|
||||
.kotlin/
|
||||
|
||||
!.idea/checkstyle-idea.xml
|
||||
!.idea/externalDependencies.xml
|
||||
|
||||
node_modules
|
||||
/.gradle/
|
||||
/.idea/*
|
||||
/.settings/
|
||||
/.classpath
|
||||
/.project
|
||||
/build/
|
||||
/node_modules/
|
||||
/package-lock.json
|
||||
/*.iml
|
||||
/*.ipr
|
||||
/*.iws
|
||||
!/.idea/checkstyle-idea.xml
|
||||
!/.idea/externalDependencies.xml
|
||||
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"java.import.gradle.enabled": false
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
= Contributing to Spring Security
|
||||
|
||||
First off, thank you for taking the time to contribute! :+1: :tada:
|
||||
|
||||
== Table of Contents
|
||||
|
||||
* <<code-of-conduct>>
|
||||
* <<how-to-contribute>>
|
||||
* <<ask-questions>>
|
||||
* <<find-an-issue>>
|
||||
* <<create-an-issue>>
|
||||
* <<issue-lifecycle>>
|
||||
* <<submit-a-pull-request>>
|
||||
* <<build-from-source>>
|
||||
* <<code-style>>
|
||||
|
||||
[[code-of-conduct]]
|
||||
== Code of Conduct
|
||||
|
||||
This project is governed by the https://github.com/spring-projects/.github/blob/main/CODE_OF_CONDUCT.md[Spring code of conduct].
|
||||
By participating you are expected to uphold this code.
|
||||
Please report unacceptable behavior to spring-code-of-conduct@pivotal.io.
|
||||
|
||||
[[how-to-contribute]]
|
||||
== How to Contribute
|
||||
|
||||
[[ask-questions]]
|
||||
=== Ask Questions
|
||||
|
||||
If you have a question, check Stack Overflow using
|
||||
https://stackoverflow.com/questions/tagged/spring-security+or+spring-ldap+or+spring-authorization-server+or+spring-session?tab=Newest[this list of tags].
|
||||
Find an existing discussion, or start a new one if necessary.
|
||||
|
||||
If you believe there is an issue, search through https://github.com/spring-projects/spring-security/issues[existing issues] trying a few different ways to find discussions, past or current, that are related to the issue.
|
||||
Reading those discussions helps you to learn about the issue, and helps us to make a decision.
|
||||
|
||||
[[find-an-issue]]
|
||||
=== Find an Existing Issue
|
||||
|
||||
There are many issues in Spring Security with the labels https://github.com/spring-projects/spring-security/issues?q=is%3Aissue+is%3Aopen+label%3A%22status%3A+ideal-for-contribution%22[`ideal-for-contribution`] or https://github.com/spring-projects/spring-security/issues?q=is%3Aissue+is%3Aopen+label%3A%22status%3A+first-timers-only%22[`first-timers-only`] that are a great way to contribute to a discussion or <<submit-a-pull-request,to a PR>>.
|
||||
You can volunteer by commenting on these tickets, and we will assign them to you.
|
||||
|
||||
[[create-an-issue]]
|
||||
=== Create an Issue
|
||||
|
||||
Reporting an issue or making a feature request is a great way to contribute.
|
||||
Your feedback and the conversations that result from it provide a continuous flow of ideas.
|
||||
However, before creating a ticket, please take the time to <<ask-questions,ask and research>> first.
|
||||
|
||||
If you create an issue after a discussion on Stack Overflow, please provide a description in the issue instead of simply referring to Stack Overflow.
|
||||
The issue tracker is an important place of record for design discussions and should be self-sufficient.
|
||||
|
||||
Once you're ready, create an issue on https://github.com/spring-projects/spring-security/issues[GitHub].
|
||||
|
||||
Many issues are caused by subtle behavior, typos, and unintended configuration.
|
||||
Creating a https://stackoverflow.com/help/minimal-reproducible-example[Minimal Reproducible Example] (starting with https://start.spring.io for example) of the problem helps the team quickly triage your issue and get to the core of the problem.
|
||||
|
||||
We love contributors, and we may ask you to <<submit-a-pull-request,submit a PR with a fix>>.
|
||||
|
||||
[[issue-lifecycle]]
|
||||
=== Issue Lifecycle
|
||||
|
||||
When an issue is first created, it is flagged `waiting-for-triage` waiting for a team member to triage it.
|
||||
Once the issue has been reviewed, the team may ask for further information if needed, and based on the findings, the issue is either assigned a target branch (or no branch if a feature) or is closed with a specific status.
|
||||
The target branch is https://spring.io/projects/spring-security#support[the earliest supported branch] where <<choose-a-branch,the change will be applied>>.
|
||||
|
||||
When a fix is ready, the issue is closed and may still be re-opened until the fix is released.
|
||||
After that the issue will typically no longer be reopened.
|
||||
In rare cases if the issue was not at all fixed, the issue may be re-opened.
|
||||
In most cases however any follow-up reports will need to be created as new issues with a fresh description.
|
||||
|
||||
[[build-from-source]]
|
||||
=== Build from Source
|
||||
|
||||
See https://github.com/spring-projects/spring-security/tree/main#building-from-source[Build from Source] for instructions on how to check out, build, and import the Spring Security source code into your IDE.
|
||||
|
||||
[[code-style]]
|
||||
=== Source Code Style
|
||||
|
||||
The wiki pages https://github.com/spring-projects/spring-framework/wiki/Code-Style[Code Style] and https://github.com/spring-projects/spring-framework/wiki/IntelliJ-IDEA-Editor-Settings[IntelliJ IDEA Editor Settings] define the source file coding standards we use along with some IDEA editor settings we customize.
|
||||
|
||||
Additionally, since Streams are https://github.com/spring-projects/spring-security/issues/7154[much slower] than `for` loops, please use them judiciously.
|
||||
The team may ask you to change to a `for` loop if the given code is along a hot path.
|
||||
|
||||
To format the code as well as check the style, run `./gradlew format && ./gradlew check`.
|
||||
|
||||
[[submit-a-pull-request]]
|
||||
=== Submit a Pull Request
|
||||
|
||||
We are excited for your pull request! :heart:
|
||||
|
||||
Please do your best to follow these steps.
|
||||
Don't worry if you don't get them all correct the first time, we will help you.
|
||||
|
||||
1. [[sign-cla]] All commits must include a __Signed-off-by__ trailer at the end of each commit message to indicate that the contributor agrees to the Developer Certificate of Origin.
|
||||
For additional details, please refer to the blog post https://spring.io/blog/2025/01/06/hello-dco-goodbye-cla-simplifying-contributions-to-spring[Hello DCO, Goodbye CLA: Simplifying Contributions to Spring].
|
||||
2. [[create-an-issue-list]] Must you https://github.com/spring-projects/spring-security/issues/new/choose[create an issue] first? No, but it is recommended for features and larger bug fixes. It's easier discuss with the team first to determine the right fix or enhancement.
|
||||
For typos and straightforward bug fixes, starting with a pull request is encouraged.
|
||||
Please include a description for context and motivation.
|
||||
Note that the team may close your pull request if it's not a fit for the project.
|
||||
3. [[choose-a-branch]] Always check out the branch indicated in the milestone and submit pull requests against it (for example, for milestone `5.8.3` use the `5.8.x` branch).
|
||||
If there is no milestone, choose `main`.
|
||||
Once merged, the fix will be forwarded-ported to applicable branches including `main`.
|
||||
4. [[create-a-local-branch]] Create a local branch
|
||||
If this is for an issue, consider a branch name with the issue number, like `gh-22276`.
|
||||
5. [[write-tests]] Add documentation and JUnit Tests for your changes.
|
||||
6. [[update-copyright]] In all files you edited, if the copyright header is of the form 2002-20xx, update the final copyright year to the current year.
|
||||
7. [[add-since]] If on `main`, add `@since` JavaDoc attributes to new public APIs that your PR adds
|
||||
8. [[change-rnc]] If you are updating the XSD, please instead update the RNC file and then run `./gradlew :spring-security-config:rncToXsd`.
|
||||
9. [[format-code]] For each commit, build the code using `./gradlew format && ./gradlew check`.
|
||||
This command ensures the code meets most of <<code-style,the style guide>>; a notable exception is import order.
|
||||
10. [[commit-atomically]] Choose the granularity of your commits consciously and squash commits that represent
|
||||
multiple edits or corrections of the same logical change.
|
||||
See https://git-scm.com/book/en/Git-Tools-Rewriting-History[Rewriting History section of Pro Git] for an overview of streamlining the commit history.
|
||||
11. [[format-commit-messages]] Format commit messages using 55 characters for the subject line, 72 characters per line
|
||||
for the description, followed by the issue fixed, for example, `Closes gh-22276`.
|
||||
See the https://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines[Commit Guidelines section of Pro Git] for best practices around commit messages, and use `git log` to see some examples.
|
||||
Favor imperative tense over present tense (use "Fix" instead of "Fixes"); avoid past tense (use "Fix" instead of "Fixed").
|
||||
+
|
||||
[indent=0]
|
||||
----
|
||||
Address NullPointerException
|
||||
|
||||
Closes gh-22276
|
||||
----
|
||||
[[reference-issue]]
|
||||
1. If there is a prior issue, reference the GitHub issue number in the description of the pull request.
|
||||
+
|
||||
[indent=0]
|
||||
----
|
||||
Closes gh-22276
|
||||
----
|
||||
|
||||
If accepted, your contribution may be heavily modified as needed prior to merging.
|
||||
You will likely retain author attribution for your Git commits granted that the bulk of your changes remain intact.
|
||||
You may also be asked to rework the submission.
|
||||
|
||||
If asked to make corrections, simply push the changes against the same branch, and your pull request will be updated.
|
||||
In other words, you do not need to create a new pull request when asked to make changes.
|
||||
When it is time to merge, you'll be asked to squash your commits.
|
||||
|
||||
==== Participate in Reviews
|
||||
|
||||
Helping to review pull requests is another great way to contribute.
|
||||
Your feedback can help to shape the implementation of new features.
|
||||
When reviewing pull requests, however, please refrain from approving or rejecting a PR unless you are a core committer for Spring Security.
|
||||
-202
@@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
https://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
+444
-67
@@ -1,101 +1,478 @@
|
||||
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]
|
||||
= Spring Security Docs Home
|
||||
ifndef::env-github[:toc:]
|
||||
ifdef::env-github[]
|
||||
:important-caption: :exclamation:
|
||||
:note-caption: :paperclip:
|
||||
endif::[]
|
||||
|
||||
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"]
|
||||
This README describes the processes and tools used to build the documentation for production as well as how to generate a local preview of the documentation.
|
||||
|
||||
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"]
|
||||
== Overview
|
||||
|
||||
= Spring Security
|
||||
The Spring Security reference docs are generated using Antora.
|
||||
The Gradle Antora Plugin is used as the primary interface to Antora.
|
||||
|
||||
Spring Security provides security services for the https://docs.spring.io[Spring IO Platform]. Spring Security 6.0 requires Spring 6.0 as
|
||||
a minimum and also requires Java 17.
|
||||
You're viewing the playbook branch for the Spring Security project.
|
||||
The playbook branch hosts the docs build used to build and publish the production docs site, and is thus the documentation home base.
|
||||
If you're a docs developer, you'll mostly work in this branch.
|
||||
If you're a writer, you'll mostly work in software branches.
|
||||
|
||||
For a detailed list of features and access to the latest release, please visit https://spring.io/projects[Spring projects].
|
||||
=== Layout
|
||||
|
||||
== Code of Conduct
|
||||
Please see our https://github.com/spring-projects/.github/blob/main/CODE_OF_CONDUCT.md[code of conduct]
|
||||
The playbook branch, named *docs-build*, hosts the primary documentation build.
|
||||
The documentation itself is located in a dedicated subproject in each software branch (i.e., the docs are stored alongside the code).
|
||||
Software branches, also referred to as release line branches or content branches, follow the pattern `major.minor.x` (e.g., 6.0.x), or `main` for the latest release line.
|
||||
Software tags follow the pattern `major.minor.patch` (e.g., 6.0.1).
|
||||
The latest version of the docs for each release line is always sourced from a tag.
|
||||
The release line branches host the prerelease materials for the next version.
|
||||
|
||||
== Downloading Artifacts
|
||||
See https://docs.spring.io/spring-security/reference/getting-spring-security.html[Getting Spring Security] for how to obtain Spring Security.
|
||||
=== Builds
|
||||
|
||||
== Documentation
|
||||
Be sure to read the https://docs.spring.io/spring-security/reference/[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].
|
||||
The build for the production site is stored at the root of the playbook branch.
|
||||
This branch also holds the search crawler configuration and runner for the production site.
|
||||
This build is only needed for building the production site (or for developing and testing the docs build itself).
|
||||
The build for each content branch is located in a subproject, typically the folder named _docs_.
|
||||
This is the build that authors use to preview the docs for a single version when writing content.
|
||||
|
||||
You may also want to check out https://docs.spring.io/spring-security/reference/whats-new.html[what's new in the latest release].
|
||||
Regardless of how the docs site is built, Antora is configured to run a separate command to compute the version of the docs per git reference.
|
||||
This command is run by an Antora extension named Antora Collector.
|
||||
Using Collector allows the version of the docs to be maintained centrally in the _gradle.properties_ file in each git reference.
|
||||
The command also populates a collection of AsciiDoc attributes that provide access to software versions and resource URLs.
|
||||
_The docs build will not work without Collector._
|
||||
|
||||
== 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.
|
||||
=== UI
|
||||
|
||||
== Building from Source
|
||||
Spring Security uses a https://gradle.org[Gradle]-based build system.
|
||||
In the instructions below, https://vimeo.com/34436402[`./gradlew`] is invoked from the root of the source tree and serves as
|
||||
a cross-platform, self-contained bootstrap mechanism for the build.
|
||||
The UI is developed in a separate project named https://github.com/spring-io/antora-ui-spring[antora-ui-spring].
|
||||
That project generates and publishes a UI bundle, which the docs build refers to using its public bundle URL.
|
||||
There is one UI for all versions of the documentation.
|
||||
|
||||
=== Prerequisites
|
||||
https://docs.github.com/en/get-started/quickstart/set-up-git[Git] and the https://www.oracle.com/java/technologies/downloads/#java17[JDK17 build].
|
||||
The UI only shows the latest version in each release line.
|
||||
In order to access other versions that are published, the URL must be known in advance.
|
||||
|
||||
Be sure that your `JAVA_HOME` environment variable points to the `jdk-17` folder extracted from the JDK download.
|
||||
[#usage]
|
||||
== Usage
|
||||
|
||||
=== Check out sources
|
||||
[indent=0]
|
||||
----
|
||||
git clone git@github.com:spring-projects/spring-security.git
|
||||
----
|
||||
To prepare your system for building the documentation, <<prerequisites,install the prerequisites>>, then <<prepare-workspace,prepare your workspace>>.
|
||||
Then you can build the documentation in either the main branch or 6.0.x branch.
|
||||
|
||||
=== Install all `spring-*.jar` into your local Maven repository.
|
||||
.Branch checkout instead of worktrees
|
||||
[NOTE]
|
||||
====
|
||||
If you prefer to set up your workspace without worktrees, complete the steps in <<prerequisites,Prerequisites>> and clone the project repository onto your computer.
|
||||
Then, check out the desired branch and follow the instructions in each section starting from the `sdk env || sdk env install` step.
|
||||
====
|
||||
|
||||
[indent=0]
|
||||
----
|
||||
./gradlew publishToMavenLocal
|
||||
----
|
||||
[#prerequisites]
|
||||
=== Prerequisites (everyone)
|
||||
|
||||
=== Compile and test; build all JARs, distribution zips, and docs
|
||||
These instructions assume you already have basic tools on your system, including bash, zip, unzip, git, and curl.
|
||||
In addition to these basic tools, you need https://sdkman.io/install[SDKMAN!] installed so that the correct JDK is set for each branch.
|
||||
|
||||
[indent=0]
|
||||
----
|
||||
./gradlew build
|
||||
----
|
||||
. Open your terminal and enter the following command:
|
||||
+
|
||||
--
|
||||
$ curl -s "https://get.sdkman.io" | bash
|
||||
|
||||
The reference docs are not currently included in the distribution zip.
|
||||
You can build the reference docs for this branch by running the following command:
|
||||
This command downloads and installs SDKMAN!
|
||||
Once installation is complete, you should see a command displayed in your terminal that will initiate SDKMAN!.
|
||||
--
|
||||
|
||||
----
|
||||
./gradlew :spring-security-docs:antora
|
||||
----
|
||||
. Copy the command displayed in your terminal and run it.
|
||||
In the following command, `$HOME` is the path unique to your computer (e.g., _home/username/.sdkman/bin/sdkman-init.sh_).
|
||||
|
||||
That command publishes the docs site to the `_docs/build/site_` directory.
|
||||
The https://github.com/spring-projects/spring-security/tree/docs-build[playbook branch] describes how to build the reference docs in detail.
|
||||
$ source "$HOME/.sdkman/bin/sdkman-init.sh"
|
||||
|
||||
Discover more commands with `./gradlew tasks`.
|
||||
You'll use SDKMAN! in the following sections to install and switch to the JDK required for each branch.
|
||||
Now you're ready to prepare your workspace.
|
||||
|
||||
=== IDE setup (IntelliJ)
|
||||
[#prepare-workspace]
|
||||
=== Prepare your workspace (everyone)
|
||||
|
||||
No special steps are needed to open Spring Security in IntelliJ.
|
||||
Your workspace will be the folder that contains the git worktrees of the project.
|
||||
|
||||
=== IDE setup (Eclipse and VS Code)
|
||||
. In your terminal, create a directory for the project and then change into that directory.
|
||||
|
||||
To work in Eclipse or VS Code, first generate Eclipse metadata so you can import the project into Eclipse or VS Code:
|
||||
$ mkdir spring-security
|
||||
$ cd spring-security
|
||||
|
||||
[indent=0]
|
||||
----
|
||||
./gradlew cleanEclipse eclipse
|
||||
----
|
||||
. Clone the project repository and create the worktree for the main branch.
|
||||
Then change into the new *main* worktree.
|
||||
|
||||
If you have not built the project yet, run `./gradlew publishToMavenLocal` first so dependencies are resolved.
|
||||
$ git clone https://github.com/spring-projects/spring-security main
|
||||
$ cd main
|
||||
|
||||
*VS Code:* Open the repository root as a folder. The repository includes `.vscode/settings.json` which disables automatic Gradle import so that the generated Eclipse metadata (`.classpath`, `.project`) is used. Do not use the Gradle for Java extension to import the project.
|
||||
. Set up a worktree for the 6.0.x branch by running the following commands:
|
||||
|
||||
*Eclipse:* File → Import → General → Existing Projects into Workspace, then select the repository root.
|
||||
$ git worktree add ../6.0.x 6.0.x --track
|
||||
|
||||
The build uses a custom Eclipse plugin to work around Gradle dependency cycles that confuse IDE metadata generation. You may see Eclipse warnings about `xml-apis` from some test dependencies; those are excluded in the build and can be ignored.
|
||||
. Repeat the last step for all release line branches (e.g., 5.8.x, 5.7.x, etc) you intend to work with.
|
||||
|
||||
== Getting Support
|
||||
Check out the https://stackoverflow.com/questions/tagged/spring-security[Spring Security tags on Stack Overflow].
|
||||
https://spring.io/support[Commercial support] is available too.
|
||||
Now you're ready to build the docs in the main or 6.0.x branches.
|
||||
|
||||
== Contributing
|
||||
https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request[Pull requests] are welcome; see the https://github.com/spring-projects/spring-security/blob/main/CONTRIBUTING.adoc[contributor guidelines] for details.
|
||||
=== Build the documentation in the main branch (writers)
|
||||
|
||||
== License
|
||||
Spring Security is Open Source software released under the
|
||||
https://www.apache.org/licenses/LICENSE-2.0.html[Apache 2.0 license].
|
||||
NOTE: The instructions in this section assume you've completed the steps in <<prepare-workspace>>.
|
||||
|
||||
. First, make sure you're in the *main* worktree.
|
||||
. Switch to the required JDK using SDKMAN! by running the following command:
|
||||
+
|
||||
--
|
||||
$ sdk env || sdk env install
|
||||
|
||||
SDKMAN! will switch to the required JDK, provisioning it first if it isn't already available on your machine.
|
||||
--
|
||||
|
||||
. Generate the documentation with Antora using the following command:
|
||||
+
|
||||
--
|
||||
$ ./gradlew -PbuildSrc.skipTests=true :spring-security-docs:antora
|
||||
|
||||
You can also run this command directly from the _docs_ folder:
|
||||
|
||||
$ cd docs
|
||||
$ ../gradlew -PbuildSrc.skipTests=true antora
|
||||
|
||||
This command will build the documentation for the *main* branch.
|
||||
The Antora playbook is retrieved by the playbook provider from the *docs-build* branch.
|
||||
The retrieved playbook file will be cached as _cached-antora-playbook.yml_.
|
||||
--
|
||||
|
||||
. Navigate to the local file URI shown in the terminal to view the generated documentation.
|
||||
|
||||
=== Build the documentation in the 6.0.x branch (writers)
|
||||
|
||||
NOTE: The instructions in this section assume you've completed the steps in <<prepare-workspace>>.
|
||||
|
||||
. First, change to the *6.0.x* worktree.
|
||||
|
||||
$ cd ../6.0.x
|
||||
|
||||
. Switch to the required JDK using SDKMAN! by running the following command:
|
||||
+
|
||||
--
|
||||
$ sdk env || sdk env install
|
||||
|
||||
SDKMAN! will switch to the required JDK, provisioning it first if it isn't already available on your machine.
|
||||
--
|
||||
|
||||
. Generate the documentation with the following command:
|
||||
+
|
||||
--
|
||||
$ ./gradlew -PbuildSrc.skipTests=true :spring-security-docs:antora
|
||||
|
||||
This command will build the documentation for the *6.0.x* branch.
|
||||
The Antora playbook is retrieved by the playbook provider from the *docs-build* branch.
|
||||
The retrieved playbook file will be cached as _cached-antora-playbook.yml_.
|
||||
--
|
||||
|
||||
. Navigate to the local file URI shown in the terminal to view the generated documentation.
|
||||
|
||||
=== Build the documentation for production (docs developer)
|
||||
|
||||
NOTE: The instructions in this section assume you've completed the steps in <<prepare-workspace>>.
|
||||
|
||||
To build the project's production site, you'll set up a worktree for the *docs-build* branch of the repository.
|
||||
|
||||
. To add a worktree, you have to be in the main worktree.
|
||||
In your terminal, change to the *main* worktree if you aren't already in it.
|
||||
|
||||
$ cd ../main
|
||||
|
||||
. Run the following command to set up the worktree for the *docs-build* branch.
|
||||
Then change into the new _docs-build_ directory.
|
||||
|
||||
$ git worktree add ../docs-build docs-build --track
|
||||
$ cd ../docs-build
|
||||
|
||||
. Switch to the required JDK or install it.
|
||||
|
||||
$ sdk env || sdk env install
|
||||
|
||||
. Generate the documentation for the project's production site using the following command:
|
||||
+
|
||||
--
|
||||
$ ./gradlew antora
|
||||
|
||||
This command will build all of the documentation for the production site from the git repository on GitHub.
|
||||
|
||||
(Optional) To build the documentation using the current clone, using any available worktrees, run the following command instead:
|
||||
|
||||
$ ./gradlew antora --playbook local-antora-playbook.yml
|
||||
--
|
||||
|
||||
. Navigate to the local file URI shown in the terminal to view the generated documentation.
|
||||
|
||||
=== Content authoring
|
||||
|
||||
We highly recommend relying on the https://intellij-asciidoc-plugin.ahus1.de/docs/users-guide/index.html[IntelliJ AsciiDoc Plugin] while writing.
|
||||
It provides assistance and autocompletion for the AsciiDoc syntax, Antora resource IDs, attributes and keys set in the playbook, component version descriptor, etc.
|
||||
It also provides single page preview.
|
||||
|
||||
Once you've completed your edits, you'll build the branch locally to review and validate the changes.
|
||||
*You don't need to build the entire site!*
|
||||
*You don't need to create or edit the playbook or gradle.properties file either.*
|
||||
Rather, you'll interface with the docs build, and it will automatically set up a playbook for your branch and manage any required extensions, right from the docs subproject in your software (content) branch.
|
||||
See <<usage>> to learn how to build the docs.
|
||||
|
||||
If the branch you modified has any AsciiDoc or Antora errors, they'll be printed to your terminal.
|
||||
Once you've fixed any errors and reviewed your changes, submit a pull request to the relevant software branch.
|
||||
If the change applies to multiple versions of the docs, you'll want to submit the pull request to the oldest active software branch.
|
||||
The maintainer will then apply that change to each of the release line branches.
|
||||
|
||||
== CI workflows
|
||||
|
||||
CI workflows are run by GitHub Actions.
|
||||
CI workflows are defined in YAML files in the _.github/workflows_ directory.
|
||||
The CI workflows in the default (i.e., _main_) branch serve as the primary entry.
|
||||
Corresponding CI workflows in a non-default branch may specialize the workflow for that branch.
|
||||
However, the CI workflows in non-default branches do not receive all events and often have to be triggered.
|
||||
A CI workflow must also be present in the default branch in order for it to appear in the list of workflows in the GitHub Actions web UI.
|
||||
|
||||
CI workflows are triggered either by activity, on schedule, by the `gh workflow` call, or manually through the GitHub Actions web UI.
|
||||
Scheduled workflows only run on the default branch (i.e., *main*).
|
||||
However, a scheduled workflow may trigger a workflow in another branch using the `gh workflow` call.
|
||||
Activity on a branch or tag is only picked up by workflows in that reference.
|
||||
However, a workflow running in a branch or tag may trigger a workflow in another branch using the `gh workflow` call.
|
||||
|
||||
There are two key CI workflows that pertain to the docs:
|
||||
|
||||
* Deploy Docs (_.github/workflows/deploy-docs.yml_)
|
||||
* Rebuild Search Index (_.github/workflows/rebuild-search-index.yml_)
|
||||
|
||||
In both cases, the concrete steps are located in the CI workflow in the *docs-build* branch.
|
||||
The CI workflows in the *main* branch only trigger the workflows in the *docs-build* branch.
|
||||
|
||||
The production site is only deployed from the CI workflow in the *docs-build* branch.
|
||||
Often times, activity in a git reference or a scheduled workflow will trigger the CI workflow in the *docs-build* branch.
|
||||
Thus, this workflow is also present in each software branch to pick up on that activity.
|
||||
|
||||
The production search index is built from the CI workflow in the *docs-build* branch.
|
||||
This CI workflow is triggered once per day by the scheduler on the same workflow in the *main* branch.
|
||||
This CI workflow must reside in the *main* branch in order to appear in the list of workflows in the CI branch so it can be triggered manually.
|
||||
|
||||
Here's a list of activity that does and does not trigger the Deploy Docs workflow:
|
||||
|
||||
pull request:: Does not trigger the Deploy Docs workflow.
|
||||
|
||||
push to software branch:: Triggers the Deploy Docs workflow in that branch, which in turn triggers the Deploy Docs workflow in the *docs-build* branch.
|
||||
Attempts to run the docs build as partial build, if applicable.
|
||||
|
||||
push to docs-build branch:: Triggers the Deploy Docs workflow in that branch.
|
||||
Runs the docs build as a full build.
|
||||
|
||||
push tag:: Triggers the Deploy Docs workflow in that tag, which in turn triggers the Deploy Docs workflow in the *docs-build* branch.
|
||||
Always runs the docs build as a full build.
|
||||
|
||||
schedule:: Not configured for the Deploy Docs workflow.
|
||||
|
||||
It's possible to trigger the Deploy Docs manually from the GitHub Actions web UI.
|
||||
Be sure to select docs-build as the branch so that it will run a full build.
|
||||
See <<trigger>> for details.
|
||||
|
||||
Note that updating the UI bundle does not currently trigger the Deploy Docs workflow, though it could be configured to do so.
|
||||
|
||||
The Rebuild Search Index workflow is only triggered on a schedule, currently once per day.
|
||||
|
||||
== Publishing
|
||||
|
||||
The project in the *docs-build* branch supports publishing both full and partial builds of the production docs site.
|
||||
The project uses a Gradle build to run Antora on the playbook file _antora-playbook.yml_ (i.e., `./gradlew antora`).
|
||||
The production docs site is hosted on Linux server running Apache httpd.
|
||||
Files are published over SSH to the server by the .github/actions/publish-docs.sh script.
|
||||
A CDN (CloudFlare) caches URLs for a brief window of time.
|
||||
The publish script attempts to invalidate the cache after publishing the files so new content is available immediately.
|
||||
|
||||
=== Full build
|
||||
|
||||
In a full build, the entire site is rebuilt from the content sources matched by the patterns listed in the playbook file.
|
||||
The UI assets are also published when a full build is run.
|
||||
|
||||
[#partial-builds]
|
||||
=== Partial builds
|
||||
|
||||
A partial build is a single version sources from a single git reference.
|
||||
A partial build requested by git reference using the CI workflow variable *build-refname*.
|
||||
Here's an example of how to trigger the CI workflow for a partial build:
|
||||
|
||||
$ gh workflow run deploy-docs.yml --repo spring-projects/spring-security --ref docs-build -f build-refname=5.7.x
|
||||
|
||||
The partial build is coordinated by the Antora Atlas extension and set up by the @springio/antora-extensions/partial-build-extension extension.
|
||||
See https://github.com/spring-io/antora-extensions#partial-build[Partial Build] for a detailed explanation of the partial build extension and how to configure it.
|
||||
|
||||
During a partial build, Atlas runs in same site mode, which means it creates relative links (rather than absolute links) to files imported from the site manifest.
|
||||
This feature assumes that the built files will be reunited with the previously built files in the published site.
|
||||
The @springio/antora-extensions/partial-build-extension reconfigures the playbook to run a partial build if the BUILD_REFNAME environment variable is set, reverting to a full build if it determines a partial build is not appropriate.
|
||||
|
||||
During a partial build, only the version folder that was built is published to the web server.
|
||||
Files in other folders are untouched.
|
||||
The UI assets are not published when a partial build is run.
|
||||
|
||||
[#trigger]
|
||||
=== Trigger the documentation build workflow (docs developer)
|
||||
|
||||
You can trigger the production document build using the Deploy Docs entry in the GitHub Actions web UI or using the https://cli.github.com/[GitHub CLI].
|
||||
|
||||
==== GitHub Actions web UI
|
||||
|
||||
. In the GitHub Actions web UI, click the Deploy Docs entry.
|
||||
. Click on the "Run workflow" menu.
|
||||
.. *To trigger full build*, select the *docs-build* branch and click "Run workflow".
|
||||
.. *To trigger a partial build*, specify a release line branch name in the input field labeled "Enter git refname to build" and click "Run workflow".
|
||||
|
||||
==== GitHub CLI
|
||||
|
||||
*To trigger full build*, start from within the cloned repository (ideally the playbook branch) and enter the `gh` command and options in the GitHub CLI:
|
||||
|
||||
$ gh workflow run deploy-docs.yml --ref docs-build
|
||||
|
||||
*To trigger a partial build*, enter the `gh` command and options to build a single version (based on the release line branch name):
|
||||
|
||||
$ gh workflow run deploy-docs.yml --ref docs-build -f build-refname=5.7.x
|
||||
|
||||
Run `gh help workflow run` to show the docs for this command and other examples of how to use it.
|
||||
|
||||
If you're not running the `gh` command from within the cloned repository, you can specify the repository using the `--repo` CLI option (e.g., `--repo spring-projects/spring-security`).
|
||||
|
||||
== Extensions
|
||||
|
||||
The Spring Security docs have additional requirements above what Antora provides by default.
|
||||
To fulfill these requirements, the docs build employs a handful of Antora and Asciidoctor extensions to build successfully.
|
||||
You can't build the Spring Security docs using the base distribution of Antora.
|
||||
Fortunately, this extra complexity is encapsulated in the Antora playbook and several distributed extensions.
|
||||
|
||||
IMPORTANT: The order of Antora extensions in the playbook matters.
|
||||
If the order is changed, it could result in files or metadata that an extension relies on not being available at the time it runs.
|
||||
|
||||
For the most part, the extensions are retrieved from the npm package registry (npmjs.com).
|
||||
There are also several local extensions in _lib/antora/extensions_.
|
||||
The local extensions handle logic specific to this project and are only used for the production build.
|
||||
|
||||
Below is a summary of the Antora and Asciidoctor extensions used in the docs build.
|
||||
|
||||
=== Antora extensions
|
||||
|
||||
@springio/antora-extensions/partial-build-extension (prod only):: Configures a partial build, when requested, by setting the `primary-site-url` and `primary-site-manifest-url` AsciiDoc attributes.
|
||||
See <<partial-builds>> for more information.
|
||||
./lib/antora/extensions/inject-collector-config.js (prod only):: Injects configuration for Antora Collector into tags that predated Antora Collector being introduced.
|
||||
See the next extension for details.
|
||||
@antora/collector-extension:: Invokes a command (a Gradle task) to set the docs version from _gradle.properties_ and numerous AsciiDoc attributes that provide access to software versions and resource URLs.
|
||||
The command that Antora Collector runs is essential for Antora to classify the docs properly.
|
||||
./lib/antora/extensions/version-fix.js (prod only):: Fixes invalid metadata in _antora.yml_ and/or _gradle.properties_ in tags.
|
||||
@antora/atlas-extension (prod only):: Generates the site manifest (_site-manifest.json_) and publishes it with the site.
|
||||
Also coordinates the partial build when requested.
|
||||
See <<partial-builds>> for details.
|
||||
@opendevise/antora-release-line-extension:: Abbreviates the version segment (in the URL) of the latest version in each release line from major.minor.patch to major.minor.
|
||||
The version segment of the latest overall version is still abbreviated to empty string by Antora.
|
||||
@springio/antora-extensions/tabs-migration-extension:: Migrates the tabs syntax from Spring Tabs to Asciidoctor Tabs.
|
||||
See <<tabs-migration>> for details.
|
||||
./lib/antora/extensions/publish-docsearch-config.js (prod only):: Publishes the docsearch config file to the production site so the indexer can use it.
|
||||
See <<search>> for details.
|
||||
|
||||
=== Asciidoctor extensions
|
||||
|
||||
@asciidoctor/tabs:: Enables the tabs block in AsciiDoc.
|
||||
See <<tabs-migration>> and the https://github.com/asciidoctor/asciidoctor-tabs[Asciidoctor Tabs README] for details.
|
||||
@springio/asciidoctor-extensions (prod only):: Provides various enhancements to the output generated by Asciidoctor, mostly around code blocks.
|
||||
See https://github.com/spring-io/asciidoctor-extensions[Spring.io Asciidoctor Extensions] for an inventory of extensions and how to activate and configure them.
|
||||
|
||||
[#tabs-migration]
|
||||
== Tabs migration
|
||||
|
||||
The Spring Security docs contain two variations of the tabs syntax, https://github.com/spring-io/spring-asciidoctor-backends#tabs[Spring Tabs] and https://github.com/asciidoctor/asciidoctor-tabs[Asciidoctor Tabs].
|
||||
Moving forward, Asciidoctor Tabs is the syntax that should be used.
|
||||
However, since the Spring Security docs include content from tags that were written before Asciidoctor Tabs was introduced, the docs build must still be able to process the Spring Tabs syntax where it is used.
|
||||
|
||||
When the docs build runs, the Spring Tabs are automatically converted to Asciidoctor Tabs by the @springio/antora-extensions/tabs-migration-extension extension.
|
||||
Spring Tabs are never in the final output (unless the tabs migration extension is switched off).
|
||||
This extension also has the ability to unwrap the example block that encloses adjacent tabs, when possible, so only the tabs block remains.
|
||||
If Spring Tabs are not detected in a document, the migration will not run on that document.
|
||||
See https://github.com/spring-io/antora-extensions#tabs-migration[Tabs Migration] for a detailed explanation of this extension and how to configure it.
|
||||
|
||||
For the Spring Security docs, the tabs migration will always have to be used as long as there are tags in the build that contain Spring Tabs.
|
||||
However, to reduce the amount of work the tabs migration extension has to do, the migration should be made permanent where possible.
|
||||
Thus, we recommend making the migration permanent in release line branches that are active, and thus all future tags.
|
||||
|
||||
Saving the result of the tabs migration is done one software branch at a time.
|
||||
To start, switch to a branch and run the docs build in that branch (this will retrieve the Antora playbook).
|
||||
Next, edit the _cached-antora-playbook.yml_ file and add `save: true` underneath the key `unwrap_example_block`.
|
||||
This setting will save the migrated files back to their original location under the _docs_ folder.
|
||||
Run the docs build in that branch again to apply the tabs migration.
|
||||
Now commit the changed files.
|
||||
Once that's done, the tabs migration won't have to run on any documents in that branch.
|
||||
|
||||
[#search]
|
||||
== Search
|
||||
|
||||
The search component in the docs site is powered by Algolia DocSearch (specifically 2.6).
|
||||
DocSearch is a documentation-oriented toolchain for using Algolia's search solution.
|
||||
It provides both a crawler (aka scraper or indexer) and a search interface.
|
||||
The search index is hosted on the Algolia platform and queried from the search interface via a web API.
|
||||
|
||||
=== Client
|
||||
|
||||
The search interface is integrated into the UI bundle and initialized when the page loads.
|
||||
The search interface is configured using a collection of environment variables: `ALGOLIA_API_KEY`, `ALGOLIA_APP_ID`, and `ALGOLIA_INDEX_NAME`.
|
||||
For now, these environment variables are defined in _build.gradle_ for the production build.
|
||||
The search interface is only activated when all of these values are set.
|
||||
|
||||
NOTE: The docsearch.js 2.6 package is marked as deprecated in npmjs.com.
|
||||
However, the new client (@docsearch/js 3.x) has a completely different interaction model and search result display that's not compatible with customized client adapter currently in use.
|
||||
In other words, switching to it means developing the customizations from scratch.
|
||||
Even if that were to be done, the way the new client displays search results is over simplified.
|
||||
The search results provided by docsearch.js 2.6 have proven to be clearer and easier to comprehend.
|
||||
|
||||
=== Crawler
|
||||
|
||||
The search index is created by the crawler component of DocSearch.
|
||||
There are two steps involved.
|
||||
First, the crawler must be configured.
|
||||
Second, the crawler must be run with that configuration.
|
||||
|
||||
==== Configure
|
||||
|
||||
The behavior of the crawler is configured by a file name _docsearch-config.json_.
|
||||
However, this file is not stored directly in the playbook branch.
|
||||
Rather, it's generated from a template to account for the versions in the published site.
|
||||
|
||||
The generation of the _docsearch-config.js_ file happens during the production build.
|
||||
This file is generated by the Antora extension _lib/antora/extensions/publish-docsearch-config.js_.
|
||||
The extension generates a docsearch config so that docsearch indexes the latest version in each release line.
|
||||
To do so, the extension configures Handlebars to run using a model derived from information in Antora's content catalog.
|
||||
It then evaluates the template at _.github/actions/docsearch-config.json.hbs_ to produce a file at the root of the generated site named _docsearch-config.js_.
|
||||
That file is published as part of the site.
|
||||
|
||||
==== Run
|
||||
|
||||
The crawler is periodically run on the production site by the *Rebuild Search Index* workflow.
|
||||
The crawler creates a fresh search index and replaces the previous one.
|
||||
The name of the index is *spring-security-docs*.
|
||||
|
||||
When the crawler runs, it downloads the _docsearch-config.json_ file from the production site and runs the docsearch action on it.
|
||||
|
||||
NOTE: The crawler only needs to be run on files that are publicly accessible, so it makes sense that the configuration be located there too.
|
||||
|
||||
In order to publish the search records (and thus create the index), the crawler must be configured using a collection of variables: `ALGOLIA_APPLICATION_ID` and `ALGOLIA_WRITE_KEY`.
|
||||
These variables must be configured as secrets in GitHub Actions.
|
||||
The index name is not required here as it is stored in the docsearch config file.
|
||||
|
||||
|
||||
|
||||
== Maintenance
|
||||
|
||||
The docs build requires regular maintenance.
|
||||
Here's an inventory of the files or software versions to check and keep up to date.
|
||||
|
||||
.Playbook branch (i.e., *docs-build*)
|
||||
* Gradle Antora Plugin (_build.gradle_)
|
||||
* GitHub Actions libraries (_.github/workflows/deploy-docs.yml_, _.github/workflows/rebuild-search-index.yml_)
|
||||
* Java version (_.sdkmanrc_)
|
||||
* Node.js packages (_build.gradle_ and _lib/antora/templates/per-branch-antora-playbook.yml_)
|
||||
* Gradle Wrapper (_gradle/wrapper/gradle-wrapper.properties_)
|
||||
* Content sources in playbook (_antora-playbook.yml_ and _local-antora-playbook.yml_; ideally use patterns to minimize maintenance)
|
||||
* List of registered extensions (_antora-playbook.yml_, _local-antora-playbook.yml_, and _lib/antora/templates/per-branch-antora-playbook.yml_)
|
||||
|
||||
.Content branches (e.g., *6.0.x*)
|
||||
* Gradle Antora Plugin (_docs/spring-security-docs.gradle_)
|
||||
* GitHub Actions libraries (_.github/workflows/deploy-docs.yml_, _.github/workflows/rebuild-search-index.yml_)
|
||||
|
||||
Recall that the playbook used for the local docs preview in content branches is maintained in the *docs-build* branch in _lib/antora/templates/per-branch-antora-playbook.yml_.
|
||||
|
||||
-266
@@ -1,266 +0,0 @@
|
||||
= Release Process
|
||||
|
||||
The release process for Spring Security is entirely automated via the https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc[Spring Security Release Plugin] and https://github.com/spring-io/spring-security-release-tools/tree/main/.github/workflows[reusable workflows].
|
||||
The following table outlines the steps that are taken by the automation.
|
||||
|
||||
WARNING: The `5.8.x` branch does not have all of the improvements from the `6.x.x` branches. See "Status (5.8.x)" for which steps are still manual.
|
||||
|
||||
In case of a failure, you can follow the links below to read about each step, which includes instructions for performing the step manually if applicable.
|
||||
See <<frequently-asked-questions,FAQ>> for troubleshooting tips.
|
||||
|
||||
[cols="1,1,1"]
|
||||
|===
|
||||
| Step | Status (5.8.x) | Status (6.0.x+)
|
||||
|
||||
| <<update-dependencies>>
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<check-all-issues-are-closed>>
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<update-release-version>>
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<tag-release>>
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<push-release-commit>>
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<build-locally>>
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<update-release-notes-on-github>>
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<update-version-on-project-page>>
|
||||
| :x: manual
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<close-create-milestone,Close milestone>>
|
||||
| :x: manual
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<announce-release-on-slack>>
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<update-to-next-development-version>>
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<close-create-milestone,Create milestone>>
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<announce-release-on-other-channels>>
|
||||
| :x: manual
|
||||
| :x: manual
|
||||
|===
|
||||
|
||||
[#update-dependencies]
|
||||
== Update dependencies
|
||||
|
||||
Dependency versions are managed in the file xref:./gradle/libs.versions.toml[libs.versions.toml] and are automatically updated by xref:./.github/dependabot.yml[dependabot].
|
||||
|
||||
[#check-all-issues-are-closed]
|
||||
== Check all issues are closed
|
||||
|
||||
The first step of a release is to check if there are any open issues remaining in a milestone.
|
||||
|
||||
NOTE: A scheduled release will not proceed if there are any open issues.
|
||||
|
||||
TIP: If you need to prevent a release from occurring automatically, the easiest way to block a release is to add an unresolved issue to the milestone.
|
||||
|
||||
The https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc#checkMilestoneHasNoOpenIssues[`checkMilestoneHasOpenIssues`] command will check if there are any open issues for the release.
|
||||
Before running the command manually, replace the following values:
|
||||
|
||||
* `<next-version>` - Replace with the title of the milestone you are releasing now (i.e. 5.5.0-RC1)
|
||||
* `<github-personal-access-token>` - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `public_repo`. This is optional since you are unlikely to reach the rate limit for such a simple check.
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew checkMilestoneHasOpenIssues -PnextVersion=<next-version> -PgitHubAccessToken=<github-personal-access-token>
|
||||
----
|
||||
|
||||
Alternatively, you can manually check using the https://github.com/spring-projects/spring-security/milestones[milestones] page.
|
||||
|
||||
[#update-release-version]
|
||||
== Update release version
|
||||
|
||||
If all issues for the release are <<check-all-issues-are-closed,closed>>, the version number is automatically updated using the milestone title.
|
||||
When performing this step manually, update the version number in `gradle.properties` for the release (for example `5.5.0`) and commit the change using the message "Release x.y.z".
|
||||
|
||||
[#tag-release]
|
||||
== Tag release
|
||||
|
||||
The release will automatically be tagged using the milestone title.
|
||||
It is not required to tag manually.
|
||||
However, you can perform this step manually by running the following command:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
git tag 5.5.0
|
||||
----
|
||||
|
||||
[#push-release-commit]
|
||||
== Push release commit
|
||||
|
||||
During a scheduled release, the release commit will automatically be pushed to trigger a build.
|
||||
If performing this step manually, you can push the commit and tag and GitHub actions will build and deploy the artifacts with the following command:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
git push --atomic origin main 5.5.0
|
||||
----
|
||||
|
||||
The build will automatically wait for artifacts to be released to Maven Central.
|
||||
You can get notified manually when uploading is complete by running the following:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./scripts/release/wait-for-done.sh 5.5.0
|
||||
----
|
||||
|
||||
[#build-locally]
|
||||
== Build
|
||||
|
||||
All checks will automatically be performed by the build prior to uploading the artifacts to Maven Central.
|
||||
If something goes wrong, you can run the build locally using:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew check
|
||||
----
|
||||
|
||||
[#update-release-notes-on-github]
|
||||
== Update release notes on GitHub
|
||||
|
||||
Once the release has been uploaded to Maven Central, release notes will automatically be generated and a GitHub release will be created.
|
||||
To do this manually, you can use the https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc#generateChangelog[`generateChangelog`] command to generate the release notes by replacing:
|
||||
|
||||
* `<next-version>` - Replace with the milestone you are releasing now (i.e. 5.5.0)
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew generateChangelog -PnextVersion=<next-version>
|
||||
----
|
||||
|
||||
Then copy the release notes to your clipboard (your mileage may vary with the following command):
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
cat build/changelog/release-notes.md | xclip -selection clipboard
|
||||
----
|
||||
|
||||
Finally, create the
|
||||
https://github.com/spring-projects/spring-security/releases[release on
|
||||
GitHub], associate it with the tag, and paste the generated notes.
|
||||
|
||||
Alternatively, you can run the https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc#createGitHubRelease[`createGitHubRelease`] command to perform these steps automatically, replacing:
|
||||
|
||||
* `<next-version>` - Replace with the milestone you are releasing now (i.e. 5.5.0)
|
||||
* `<branch>` - The name of the branch to be tagged (if the release commit has not already been tagged)
|
||||
* `<github-personal-access-token>` - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `write:org`
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew createGitHubRelease -PnextVersion=<next-version> -Pbranch=<branch> -PcreateRelease=true -PgitHubAccessToken=<github-personal-access-token>
|
||||
----
|
||||
|
||||
[#update-version-on-project-page]
|
||||
== Update version on project page
|
||||
|
||||
The build will automatically update the project versions on https://spring.io/projects/spring-security#learn.
|
||||
To do this manually, you can use the https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc#createSaganRelease[`createSaganRelease`] and https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc#deleteSaganRelease[`deleteSaganRelease`] commands using the following parameters:
|
||||
|
||||
* `<next-version>` - Replace with the milestone you are releasing now (i.e. 5.5.0)
|
||||
* `<previous-version>` - Replace with the previous release which will be removed from the listed versions (i.e. 5.5.0-RC1)
|
||||
* `<github-personal-access-token>` - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `read:org` as https://spring.io/restdocs/index.html#authentication[documented for spring.io api]
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew createSaganRelease deleteSaganRelease -PnextVersion=<next-version> -PpreviousVersion=<previous-version> -PgitHubAccessToken=<github-personal-access-token>
|
||||
----
|
||||
|
||||
Alternatively, you can log into Contentful and update the versions manually on the Spring Security project page.
|
||||
|
||||
[#close-create-milestone]
|
||||
== Close / Create milestone
|
||||
|
||||
The release milestone will be automatically closed once the release is complete.
|
||||
To proceed manually, perform the following steps:
|
||||
|
||||
1. Visit https://github.com/spring-projects/spring-security/milestones[GitHub
|
||||
Milestones] and create a new milestone for the next release version
|
||||
2. Move any open issues from the existing milestone you just released to the new milestone
|
||||
3. Close the milestone for the release
|
||||
|
||||
NOTE: Remember that scheduled releases <<check-all-issues-are-closed,will not proceed>> if there are still open issues in the milestone.
|
||||
|
||||
[#announce-release-on-slack]
|
||||
== Announce release on Slack
|
||||
|
||||
The release will automatically be announced on Slack.
|
||||
If proceeding manually, announce the release on Slack in the channel https://pivotal.slack.com/messages/spring-release[#spring-release], including the keyword `+spring-security-announcing+` in the message.
|
||||
Something like:
|
||||
|
||||
....
|
||||
spring-security-announcing `5.5.0` is available now
|
||||
....
|
||||
|
||||
[#update-to-next-development-version]
|
||||
== Update to next development version
|
||||
|
||||
After the release is complete and artifacts have been uploaded to Maven Central, the build will automatically update to the next development version, commit and push.
|
||||
If proceeding manually, update the version in `gradle.properties` to the next `+SNAPSHOT+` version with the commit message "Next development version" and then push.
|
||||
|
||||
[#announce-release-on-other-channels]
|
||||
== Announce release on other channels
|
||||
|
||||
* Create a blog post on Contentful
|
||||
* Tweet from https://twitter.com/springsecurity[@SpringSecurity]
|
||||
|
||||
[[frequently-asked-questions]]
|
||||
== Frequently Asked Questions
|
||||
|
||||
*When should I update dependencies manually?* Dependencies should be updated at the latest the end of the week prior to the release. This is usually the Friday following the 2nd Monday of the month (counting from the first week with a Monday). When in doubt, check the https://github.com/spring-projects/spring-security/milestones[milestones] page for release due dates.
|
||||
|
||||
*When do scheduled releases occur?* Automated releases are scheduled to occur at *3:15 PM UTC* on the *3rd Monday of the month* (counting from the first week with a Monday).
|
||||
|
||||
[NOTE]
|
||||
The scheduled release process currently runs every Monday but only releases when a release is due. See the performed checks below for more information.
|
||||
|
||||
The automated release process occurs on the following branches:
|
||||
|
||||
* `main`
|
||||
* `6.2.x`
|
||||
* `6.1.x`
|
||||
* `6.0.x` (commercial only)
|
||||
* `5.8.x`
|
||||
|
||||
For each of the above branches, the automated process performs the following checks before proceeding with the release:
|
||||
|
||||
1. _Check if the milestone is due today._ This check compares the current (SNAPSHOT) version of the branch with available milestones and chooses the first match (sorted alphabetically). If the due date on the matched milestone is *not* today, the process stops.
|
||||
2. _Check if all issues are closed._ This check uses the milestone from the previous step and looks for open issues. If any open issues are found, the process stops.
|
||||
|
||||
[IMPORTANT]
|
||||
You should ensure all issues are closed or moved to another milestone prior to a scheduled release.
|
||||
|
||||
If the above checks pass, the version number is updated (in `gradle.properties`) and a commit is pushed to trigger the CI process.
|
||||
|
||||
*How do I trigger a release manually?* You can trigger a release manually in two ways:
|
||||
|
||||
1. Trigger a release for a particular branch via https://github.com/spring-projects/spring-security/actions/workflows/update-scheduled-release-version.yml[`update-scheduled-release-version.yml`] on the desired branch. The above checks are performed for that branch, and the release will proceed if all checks pass. _This is the recommended way to trigger a release that did not pass the above checks during a regularly scheduled release._
|
||||
2. Trigger releases for all branches via https://github.com/spring-projects/spring-security/actions/workflows/release-scheduler.yml[`release-scheduler.yml`] on the `main` branch. The above checks are performed for each branch, and only releases that pass all checks will proceed.
|
||||
|
||||
*When should additional manual steps be performed?* All other automated steps listed above occur during the normal CI process. Additional manual steps can be performed at any time once the builds pass and releases are finished.
|
||||
|
||||
*What if something goes wrong?* If the normal CI process fails, you can retry by re-running the failed jobs with the "Re-run failed jobs" option in GitHub Actions. If changes are required, you should revert the "Release x.y.z" commit, delete the tag, and proceed manually.
|
||||
@@ -1,49 +0,0 @@
|
||||
apply plugin: 'io.spring.convention.spring-module'
|
||||
|
||||
dependencies {
|
||||
management platform(project(":spring-security-dependencies"))
|
||||
api project(':spring-security-crypto')
|
||||
api project(':spring-security-core')
|
||||
api 'org.springframework:spring-aop'
|
||||
api 'org.springframework:spring-beans'
|
||||
api 'org.springframework:spring-context'
|
||||
api 'org.springframework:spring-core'
|
||||
api 'org.springframework:spring-expression'
|
||||
api 'io.micrometer:micrometer-observation'
|
||||
|
||||
optional project(':spring-security-acl')
|
||||
optional project(':spring-security-messaging')
|
||||
optional project(':spring-security-web')
|
||||
optional 'org.springframework:spring-websocket'
|
||||
optional 'com.fasterxml.jackson.core:jackson-databind'
|
||||
optional 'io.micrometer:context-propagation'
|
||||
optional 'io.projectreactor:reactor-core'
|
||||
optional 'jakarta.annotation:jakarta.annotation-api'
|
||||
optional 'org.aspectj:aspectjrt'
|
||||
optional 'org.springframework:spring-jdbc'
|
||||
optional 'org.springframework:spring-tx'
|
||||
optional 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor'
|
||||
|
||||
provided 'jakarta.servlet:jakarta.servlet-api'
|
||||
|
||||
testImplementation project(path : ':spring-security-web', configuration : 'tests')
|
||||
testImplementation 'commons-collections:commons-collections'
|
||||
testImplementation 'io.projectreactor:reactor-test'
|
||||
testImplementation "org.assertj:assertj-core"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-engine"
|
||||
testImplementation "org.mockito:mockito-core"
|
||||
testImplementation "org.mockito:mockito-junit-jupiter"
|
||||
testImplementation "org.springframework:spring-core-test"
|
||||
testImplementation "org.springframework:spring-test"
|
||||
testImplementation 'org.skyscreamer:jsonassert'
|
||||
testImplementation 'org.springframework:spring-test'
|
||||
testImplementation 'org.jetbrains.kotlin:kotlin-reflect'
|
||||
testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
|
||||
testImplementation 'io.mockk:mockk'
|
||||
|
||||
testRuntimeOnly 'org.hsqldb:hsqldb'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.authentication.InsufficientAuthenticationException;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* Makes a final access control (authorization) decision.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated Use {@link AuthorizationManager} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public interface AccessDecisionManager {
|
||||
|
||||
/**
|
||||
* Resolves an access control decision for the passed parameters.
|
||||
* @param authentication the caller invoking the method (not null)
|
||||
* @param object the secured object being called
|
||||
* @param configAttributes the configuration attributes associated with the secured
|
||||
* object being invoked
|
||||
* @throws AccessDeniedException if access is denied as the authentication does not
|
||||
* hold a required authority or ACL privilege
|
||||
* @throws InsufficientAuthenticationException if access is denied as the
|
||||
* authentication does not provide a sufficient level of trust
|
||||
*/
|
||||
void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
|
||||
throws AccessDeniedException, InsufficientAuthenticationException;
|
||||
|
||||
/**
|
||||
* Indicates whether this <code>AccessDecisionManager</code> is able to process
|
||||
* authorization requests presented with the passed <code>ConfigAttribute</code>.
|
||||
* <p>
|
||||
* This allows the <code>AbstractSecurityInterceptor</code> to check every
|
||||
* configuration attribute can be consumed by the configured
|
||||
* <code>AccessDecisionManager</code> and/or <code>RunAsManager</code> and/or
|
||||
* <code>AfterInvocationManager</code>.
|
||||
* </p>
|
||||
* @param attribute a configuration attribute that has been configured against the
|
||||
* <code>AbstractSecurityInterceptor</code>
|
||||
* @return true if this <code>AccessDecisionManager</code> can support the passed
|
||||
* configuration attribute
|
||||
*/
|
||||
boolean supports(ConfigAttribute attribute);
|
||||
|
||||
/**
|
||||
* Indicates whether the <code>AccessDecisionManager</code> implementation is able to
|
||||
* provide access control decisions for the indicated secured object type.
|
||||
* @param clazz the class that is being queried
|
||||
* @return <code>true</code> if the implementation can process the indicated class
|
||||
*/
|
||||
boolean supports(Class<?> clazz);
|
||||
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* Indicates a class is responsible for voting on authorization decisions.
|
||||
* <p>
|
||||
* The coordination of voting (ie polling {@code AccessDecisionVoter}s, tallying their
|
||||
* responses, and making the final authorization decision) is performed by an
|
||||
* {@link org.springframework.security.access.AccessDecisionManager}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated Use {@link AuthorizationManager} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public interface AccessDecisionVoter<S> {
|
||||
|
||||
int ACCESS_GRANTED = 1;
|
||||
|
||||
int ACCESS_ABSTAIN = 0;
|
||||
|
||||
int ACCESS_DENIED = -1;
|
||||
|
||||
/**
|
||||
* Indicates whether this {@code AccessDecisionVoter} is able to vote on the passed
|
||||
* {@code ConfigAttribute}.
|
||||
* <p>
|
||||
* This allows the {@code AbstractSecurityInterceptor} to check every configuration
|
||||
* attribute can be consumed by the configured {@code AccessDecisionManager} and/or
|
||||
* {@code RunAsManager} and/or {@code AfterInvocationManager}.
|
||||
* @param attribute a configuration attribute that has been configured against the
|
||||
* {@code AbstractSecurityInterceptor}
|
||||
* @return true if this {@code AccessDecisionVoter} can support the passed
|
||||
* configuration attribute
|
||||
*/
|
||||
boolean supports(ConfigAttribute attribute);
|
||||
|
||||
/**
|
||||
* Indicates whether the {@code AccessDecisionVoter} implementation is able to provide
|
||||
* access control votes for the indicated secured object type.
|
||||
* @param clazz the class that is being queried
|
||||
* @return true if the implementation can process the indicated class
|
||||
*/
|
||||
boolean supports(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Indicates whether or not access is granted.
|
||||
* <p>
|
||||
* The decision must be affirmative ({@code ACCESS_GRANTED}), negative (
|
||||
* {@code ACCESS_DENIED}) or the {@code AccessDecisionVoter} can abstain (
|
||||
* {@code ACCESS_ABSTAIN}) from voting. Under no circumstances should implementing
|
||||
* classes return any other value. If a weighting of results is desired, this should
|
||||
* be handled in a custom
|
||||
* {@link org.springframework.security.access.AccessDecisionManager} instead.
|
||||
* <p>
|
||||
* Unless an {@code AccessDecisionVoter} is specifically intended to vote on an access
|
||||
* control decision due to a passed method invocation or configuration attribute
|
||||
* parameter, it must return {@code ACCESS_ABSTAIN}. This prevents the coordinating
|
||||
* {@code AccessDecisionManager} from counting votes from those
|
||||
* {@code AccessDecisionVoter}s without a legitimate interest in the access control
|
||||
* decision.
|
||||
* <p>
|
||||
* Whilst the secured object (such as a {@code MethodInvocation}) is passed as a
|
||||
* parameter to maximise flexibility in making access control decisions, implementing
|
||||
* classes should not modify it or cause the represented invocation to take place (for
|
||||
* example, by calling {@code MethodInvocation.proceed()}).
|
||||
* @param authentication the caller making the invocation
|
||||
* @param object the secured object being invoked
|
||||
* @param attributes the configuration attributes associated with the secured object
|
||||
* @return either {@link #ACCESS_GRANTED}, {@link #ACCESS_ABSTAIN} or
|
||||
* {@link #ACCESS_DENIED}
|
||||
*/
|
||||
int vote(Authentication authentication, S object, Collection<ConfigAttribute> attributes);
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.access.intercept.AfterInvocationProviderManager;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* Indicates a class is responsible for participating in an
|
||||
* {@link AfterInvocationProviderManager} decision.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @see org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor
|
||||
* @see org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor
|
||||
* @deprecated Use delegation with {@link AuthorizationManager}
|
||||
*/
|
||||
@Deprecated
|
||||
public interface AfterInvocationProvider {
|
||||
|
||||
Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> attributes,
|
||||
Object returnedObject) throws AccessDeniedException;
|
||||
|
||||
/**
|
||||
* Indicates whether this <code>AfterInvocationProvider</code> is able to participate
|
||||
* in a decision involving the passed <code>ConfigAttribute</code>.
|
||||
* <p>
|
||||
* This allows the <code>AbstractSecurityInterceptor</code> to check every
|
||||
* configuration attribute can be consumed by the configured
|
||||
* <code>AccessDecisionManager</code> and/or <code>RunAsManager</code> and/or
|
||||
* <code>AccessDecisionManager</code>.
|
||||
* </p>
|
||||
* @param attribute a configuration attribute that has been configured against the
|
||||
* <code>AbstractSecurityInterceptor</code>
|
||||
* @return true if this <code>AfterInvocationProvider</code> can support the passed
|
||||
* configuration attribute
|
||||
*/
|
||||
boolean supports(ConfigAttribute attribute);
|
||||
|
||||
/**
|
||||
* Indicates whether the <code>AfterInvocationProvider</code> is able to provide
|
||||
* "after invocation" processing for the indicated secured object type.
|
||||
* @param clazz the class of secure object that is being queried
|
||||
* @return true if the implementation can process the indicated class
|
||||
*/
|
||||
boolean supports(Class<?> clazz);
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
|
||||
import org.springframework.security.access.intercept.RunAsManager;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.annotation.SecurityAnnotationScanner;
|
||||
|
||||
/**
|
||||
* Stores a security system related configuration attribute.
|
||||
*
|
||||
* <p>
|
||||
* When an
|
||||
* {@link org.springframework.security.access.intercept.AbstractSecurityInterceptor} is
|
||||
* set up, a list of configuration attributes is defined for secure object patterns. These
|
||||
* configuration attributes have special meaning to a {@link RunAsManager},
|
||||
* {@link AccessDecisionManager} or <code>AccessDecisionManager</code> delegate.
|
||||
*
|
||||
* <p>
|
||||
* Stored at runtime with other <code>ConfigAttribute</code>s for the same secure object
|
||||
* target.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated In modern Spring Security APIs, each API manages its own configuration
|
||||
* context. As such there is no direct replacement for this interface. In the case of
|
||||
* method security, please see {@link SecurityAnnotationScanner} and
|
||||
* {@link AuthorizationManager}. In the case of channel security, please see
|
||||
* {@code HttpsRedirectFilter}. In the case of web security, please see
|
||||
* {@link AuthorizationManager}.
|
||||
*/
|
||||
@Deprecated
|
||||
@NullUnmarked
|
||||
public interface ConfigAttribute extends Serializable {
|
||||
|
||||
/**
|
||||
* If the <code>ConfigAttribute</code> can be represented as a <code>String</code> and
|
||||
* that <code>String</code> is sufficient in precision to be relied upon as a
|
||||
* configuration parameter by a {@link RunAsManager}, {@link AccessDecisionManager} or
|
||||
* <code>AccessDecisionManager</code> delegate, this method should return such a
|
||||
* <code>String</code>.
|
||||
* <p>
|
||||
* If the <code>ConfigAttribute</code> cannot be expressed with sufficient precision
|
||||
* as a <code>String</code>, <code>null</code> should be returned. Returning
|
||||
* <code>null</code> will require any relying classes to specifically support the
|
||||
* <code>ConfigAttribute</code> implementation, so returning <code>null</code> should
|
||||
* be avoided unless actually required.
|
||||
* @return a representation of the configuration attribute (or <code>null</code> if
|
||||
* the configuration attribute cannot be expressed as a <code>String</code> with
|
||||
* sufficient precision).
|
||||
*/
|
||||
String getAttribute();
|
||||
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.annotation.SecurityAnnotationScanner;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Stores a {@link ConfigAttribute} as a <code>String</code>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated In modern Spring Security APIs, each API manages its own configuration
|
||||
* context. As such there is no direct replacement for this interface. In the case of
|
||||
* method security, please see {@link SecurityAnnotationScanner} and
|
||||
* {@link AuthorizationManager}. In the case of channel security, please see
|
||||
* {@code HttpsRedirectFilter}. In the case of web security, please see
|
||||
* {@link AuthorizationManager}.
|
||||
*/
|
||||
@Deprecated
|
||||
public class SecurityConfig implements ConfigAttribute {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = -7138084564199804304L;
|
||||
|
||||
private final String attrib;
|
||||
|
||||
public SecurityConfig(String config) {
|
||||
Assert.hasText(config, "You must provide a configuration attribute");
|
||||
this.attrib = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof ConfigAttribute attr) {
|
||||
return this.attrib.equals(attr.getAttribute());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttribute() {
|
||||
return this.attrib;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.attrib.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.attrib;
|
||||
}
|
||||
|
||||
public static List<ConfigAttribute> createListFromCommaDelimitedString(String access) {
|
||||
return createList(StringUtils.commaDelimitedListToStringArray(access));
|
||||
}
|
||||
|
||||
public static List<ConfigAttribute> createList(String... attributeNames) {
|
||||
Assert.notNull(attributeNames, "You must supply an array of attribute names");
|
||||
List<ConfigAttribute> attributes = new ArrayList<>(attributeNames.length);
|
||||
for (String attribute : attributeNames) {
|
||||
attributes.add(new SecurityConfig(attribute.trim()));
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.annotation.SecurityAnnotationScanner;
|
||||
|
||||
/**
|
||||
* Implemented by classes that store and can identify the {@link ConfigAttribute}s that
|
||||
* applies to a given secure object invocation.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated In modern Spring Security APIs, each API manages its own configuration
|
||||
* context. As such there is no direct replacement for this interface. In the case of
|
||||
* method security, please see {@link SecurityAnnotationScanner} and
|
||||
* {@link AuthorizationManager}. In the case of channel security, please see
|
||||
* {@code HttpsRedirectFilter}. In the case of web security, please see
|
||||
* {@link AuthorizationManager}.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface SecurityMetadataSource extends AopInfrastructureBean {
|
||||
|
||||
/**
|
||||
* Accesses the {@code ConfigAttribute}s that apply to a given secure object.
|
||||
* @param object the object being secured
|
||||
* @return the attributes that apply to the passed in secured object. Should return an
|
||||
* empty collection if there are no applicable attributes.
|
||||
* @throws IllegalArgumentException if the passed object is not of a type supported by
|
||||
* the <code>SecurityMetadataSource</code> implementation
|
||||
*/
|
||||
Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* If available, returns all of the {@code ConfigAttribute}s defined by the
|
||||
* implementing class.
|
||||
* <p>
|
||||
* This is used by the {@link AbstractSecurityInterceptor} to perform startup time
|
||||
* validation of each {@code ConfigAttribute} configured against it.
|
||||
* @return the {@code ConfigAttribute}s or {@code null} if unsupported
|
||||
*/
|
||||
Collection<ConfigAttribute> getAllConfigAttributes();
|
||||
|
||||
/**
|
||||
* Indicates whether the {@code SecurityMetadataSource} implementation is able to
|
||||
* provide {@code ConfigAttribute}s for the indicated secure object type.
|
||||
* @param clazz the class that is being queried
|
||||
* @return true if the implementation can process the indicated class
|
||||
*/
|
||||
boolean supports(Class<?> clazz);
|
||||
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
|
||||
/**
|
||||
* Strategy to process a custom security annotation to extract the relevant
|
||||
* {@code ConfigAttribute}s for securing a method.
|
||||
* <p>
|
||||
* Used by {@code SecuredAnnotationSecurityMetadataSource}.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @deprecated Used only by now-deprecated classes. Consider
|
||||
* {@link org.springframework.security.authorization.method.SecuredAuthorizationManager}
|
||||
* for `@Secured` methods.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface AnnotationMetadataExtractor<A extends Annotation> {
|
||||
|
||||
Collection<? extends ConfigAttribute> extractAttributes(A securityAnnotation);
|
||||
|
||||
}
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.annotation.security.DenyAll;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.method.AbstractFallbackMethodSecurityMetadataSource;
|
||||
|
||||
/**
|
||||
* Sources method security metadata from major JSR 250 security annotations.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 2.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.method.Jsr250AuthorizationManager}
|
||||
* instead
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public class Jsr250MethodSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource {
|
||||
|
||||
private String defaultRolePrefix = "ROLE_";
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Sets the default prefix to be added to {@link RolesAllowed}. For example, if
|
||||
* {@code @RolesAllowed("ADMIN")} or {@code @RolesAllowed("ADMIN")} is used, then the
|
||||
* role ROLE_ADMIN will be used when the defaultRolePrefix is "ROLE_" (default).
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* If null or empty, then no default role prefix is used.
|
||||
* </p>
|
||||
* @param defaultRolePrefix the default prefix to add to roles. Default "ROLE_".
|
||||
*/
|
||||
public void setDefaultRolePrefix(String defaultRolePrefix) {
|
||||
this.defaultRolePrefix = defaultRolePrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<ConfigAttribute> findAttributes(Class<?> clazz) {
|
||||
return processAnnotations(clazz.getAnnotations());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass) {
|
||||
return processAnnotations(AnnotationUtils.getAnnotations(method));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private @Nullable List<ConfigAttribute> processAnnotations(Annotation @Nullable [] annotations) {
|
||||
if (annotations == null || annotations.length == 0) {
|
||||
return null;
|
||||
}
|
||||
List<ConfigAttribute> attributes = new ArrayList<>();
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation instanceof DenyAll) {
|
||||
attributes.add(Jsr250SecurityConfig.DENY_ALL_ATTRIBUTE);
|
||||
return attributes;
|
||||
}
|
||||
if (annotation instanceof PermitAll) {
|
||||
attributes.add(Jsr250SecurityConfig.PERMIT_ALL_ATTRIBUTE);
|
||||
return attributes;
|
||||
}
|
||||
if (annotation instanceof RolesAllowed ra) {
|
||||
|
||||
for (String allowed : ra.value()) {
|
||||
String defaultedAllowed = getRoleWithDefaultPrefix(allowed);
|
||||
attributes.add(new Jsr250SecurityConfig(defaultedAllowed));
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getRoleWithDefaultPrefix(String role) {
|
||||
if (role == null) {
|
||||
return role;
|
||||
}
|
||||
if (this.defaultRolePrefix == null || this.defaultRolePrefix.length() == 0) {
|
||||
return role;
|
||||
}
|
||||
if (role.startsWith(this.defaultRolePrefix)) {
|
||||
return role;
|
||||
}
|
||||
return this.defaultRolePrefix + role;
|
||||
}
|
||||
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.annotation;
|
||||
|
||||
import jakarta.annotation.security.DenyAll;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||
|
||||
/**
|
||||
* Security config applicable as a JSR 250 annotation attribute.
|
||||
*
|
||||
* @author Ryan Heaton
|
||||
* @since 2.0
|
||||
* @deprecated Use {@link AuthorizationManagerBeforeMethodInterceptor#jsr250()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("serial")
|
||||
public class Jsr250SecurityConfig extends SecurityConfig {
|
||||
|
||||
public static final Jsr250SecurityConfig PERMIT_ALL_ATTRIBUTE = new Jsr250SecurityConfig(PermitAll.class.getName());
|
||||
|
||||
public static final Jsr250SecurityConfig DENY_ALL_ATTRIBUTE = new Jsr250SecurityConfig(DenyAll.class.getName());
|
||||
|
||||
public Jsr250SecurityConfig(String role) {
|
||||
super(role);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.annotation;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* Voter on JSR-250 configuration attributes.
|
||||
*
|
||||
* @author Ryan Heaton
|
||||
* @since 2.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.method.Jsr250AuthorizationManager}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class Jsr250Voter implements AccessDecisionVoter<Object> {
|
||||
|
||||
/**
|
||||
* The specified config attribute is supported if its an instance of a
|
||||
* {@link Jsr250SecurityConfig}.
|
||||
* @param configAttribute The config attribute.
|
||||
* @return whether the config attribute is supported.
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute configAttribute) {
|
||||
return configAttribute instanceof Jsr250SecurityConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* All classes are supported.
|
||||
* @param clazz the class.
|
||||
* @return true
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Votes according to JSR 250.
|
||||
* <p>
|
||||
* If no JSR-250 attributes are found, it will abstain, otherwise it will grant or
|
||||
* deny access based on the attributes that are found.
|
||||
* @param authentication The authentication object.
|
||||
* @param object The access object.
|
||||
* @param definition The configuration definition.
|
||||
* @return The vote.
|
||||
*/
|
||||
@Override
|
||||
public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> definition) {
|
||||
boolean jsr250AttributeFound = false;
|
||||
for (ConfigAttribute attribute : definition) {
|
||||
if (Jsr250SecurityConfig.PERMIT_ALL_ATTRIBUTE.equals(attribute)) {
|
||||
return ACCESS_GRANTED;
|
||||
}
|
||||
if (Jsr250SecurityConfig.DENY_ALL_ATTRIBUTE.equals(attribute)) {
|
||||
return ACCESS_DENIED;
|
||||
}
|
||||
if (supports(attribute)) {
|
||||
jsr250AttributeFound = true;
|
||||
// Attempt to find a matching granted authority
|
||||
for (GrantedAuthority authority : authentication.getAuthorities()) {
|
||||
if (attribute.getAttribute().equals(authority.getAuthority())) {
|
||||
return ACCESS_GRANTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return jsr250AttributeFound ? ACCESS_DENIED : ACCESS_ABSTAIN;
|
||||
}
|
||||
|
||||
}
|
||||
-102
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.access.method.AbstractFallbackMethodSecurityMetadataSource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Sources method security metadata from Spring Security's {@link Secured} annotation.
|
||||
* <p>
|
||||
* Can also be used with custom security annotations by injecting an
|
||||
* {@link AnnotationMetadataExtractor}. The annotation type will then be obtained from the
|
||||
* generic parameter type supplied to this interface
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Luke Taylor
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor#secured}
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public class SecuredAnnotationSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource {
|
||||
|
||||
private AnnotationMetadataExtractor annotationExtractor;
|
||||
|
||||
private @Nullable Class<? extends Annotation> annotationType;
|
||||
|
||||
public SecuredAnnotationSecurityMetadataSource() {
|
||||
this(new SecuredAnnotationMetadataExtractor());
|
||||
}
|
||||
|
||||
public SecuredAnnotationSecurityMetadataSource(AnnotationMetadataExtractor annotationMetadataExtractor) {
|
||||
Assert.notNull(annotationMetadataExtractor, "annotationMetadataExtractor cannot be null");
|
||||
this.annotationExtractor = annotationMetadataExtractor;
|
||||
this.annotationType = (Class<? extends Annotation>) GenericTypeResolver
|
||||
.resolveTypeArgument(this.annotationExtractor.getClass(), AnnotationMetadataExtractor.class);
|
||||
Assert.notNull(this.annotationType, () -> this.annotationExtractor.getClass().getName()
|
||||
+ " must supply a generic parameter for AnnotationMetadataExtractor");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<ConfigAttribute> findAttributes(Class<?> clazz) {
|
||||
return processAnnotation(AnnotationUtils.findAnnotation(clazz, this.annotationType));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass) {
|
||||
return processAnnotation(AnnotationUtils.findAnnotation(method, this.annotationType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
private @Nullable Collection<ConfigAttribute> processAnnotation(@Nullable Annotation annotation) {
|
||||
return (annotation != null) ? this.annotationExtractor.extractAttributes(annotation) : null;
|
||||
}
|
||||
|
||||
static class SecuredAnnotationMetadataExtractor implements AnnotationMetadataExtractor<Secured> {
|
||||
|
||||
@Override
|
||||
public Collection<ConfigAttribute> extractAttributes(Secured secured) {
|
||||
String[] attributeTokens = secured.value();
|
||||
List<ConfigAttribute> attributes = new ArrayList<>(attributeTokens.length);
|
||||
for (String token : attributeTokens) {
|
||||
attributes.add(new SecurityConfig(token));
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.event;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* Abstract superclass for all security interception related events.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated Authorization events have moved. Consider
|
||||
* {@link org.springframework.security.authorization.event.AuthorizationGrantedEvent} and
|
||||
* {@link org.springframework.security.authorization.event.AuthorizationDeniedEvent}
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractAuthorizationEvent extends ApplicationEvent {
|
||||
|
||||
/**
|
||||
* Construct the event, passing in the secure object being intercepted.
|
||||
* @param secureObject the secure object
|
||||
*/
|
||||
public AbstractAuthorizationEvent(Object secureObject) {
|
||||
super(secureObject);
|
||||
}
|
||||
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.event;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Indicates a secure object invocation failed because the <code>Authentication</code>
|
||||
* could not be obtained from the <code>SecurityContextHolder</code>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated Authentication is now separated from authorization. Consider
|
||||
* {@link org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent}
|
||||
* instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("serial")
|
||||
public class AuthenticationCredentialsNotFoundEvent extends AbstractAuthorizationEvent {
|
||||
|
||||
private final AuthenticationCredentialsNotFoundException credentialsNotFoundException;
|
||||
|
||||
private final Collection<ConfigAttribute> configAttribs;
|
||||
|
||||
/**
|
||||
* Construct the event.
|
||||
* @param secureObject the secure object
|
||||
* @param attributes that apply to the secure object
|
||||
* @param credentialsNotFoundException exception returned to the caller (contains
|
||||
* reason)
|
||||
*
|
||||
*/
|
||||
public AuthenticationCredentialsNotFoundEvent(Object secureObject, Collection<ConfigAttribute> attributes,
|
||||
AuthenticationCredentialsNotFoundException credentialsNotFoundException) {
|
||||
super(secureObject);
|
||||
Assert.isTrue(attributes != null && credentialsNotFoundException != null,
|
||||
"All parameters are required and cannot be null");
|
||||
this.configAttribs = attributes;
|
||||
this.credentialsNotFoundException = credentialsNotFoundException;
|
||||
}
|
||||
|
||||
public Collection<ConfigAttribute> getConfigAttributes() {
|
||||
return this.configAttribs;
|
||||
}
|
||||
|
||||
public AuthenticationCredentialsNotFoundException getCredentialsNotFoundException() {
|
||||
return this.credentialsNotFoundException;
|
||||
}
|
||||
|
||||
}
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.event;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Indicates a secure object invocation failed because the principal could not be
|
||||
* authorized for the request.
|
||||
*
|
||||
* <p>
|
||||
* This event might be thrown as a result of either an
|
||||
* {@link org.springframework.security.access.AccessDecisionManager AccessDecisionManager}
|
||||
* or an {@link org.springframework.security.access.intercept.AfterInvocationManager
|
||||
* AfterInvocationManager}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.event.AuthorizationDeniedEvent}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("serial")
|
||||
public class AuthorizationFailureEvent extends AbstractAuthorizationEvent {
|
||||
|
||||
private final AccessDeniedException accessDeniedException;
|
||||
|
||||
private final Authentication authentication;
|
||||
|
||||
private final Collection<ConfigAttribute> configAttributes;
|
||||
|
||||
/**
|
||||
* Construct the event.
|
||||
* @param secureObject the secure object
|
||||
* @param attributes that apply to the secure object
|
||||
* @param authentication that was found in the <code>SecurityContextHolder</code>
|
||||
* @param accessDeniedException that was returned by the
|
||||
* <code>AccessDecisionManager</code>
|
||||
* @throws IllegalArgumentException if any null arguments are presented.
|
||||
*/
|
||||
public AuthorizationFailureEvent(Object secureObject, Collection<ConfigAttribute> attributes,
|
||||
Authentication authentication, AccessDeniedException accessDeniedException) {
|
||||
super(secureObject);
|
||||
Assert.isTrue(attributes != null && authentication != null && accessDeniedException != null,
|
||||
"All parameters are required and cannot be null");
|
||||
this.configAttributes = attributes;
|
||||
this.authentication = authentication;
|
||||
this.accessDeniedException = accessDeniedException;
|
||||
}
|
||||
|
||||
public AccessDeniedException getAccessDeniedException() {
|
||||
return this.accessDeniedException;
|
||||
}
|
||||
|
||||
public Authentication getAuthentication() {
|
||||
return this.authentication;
|
||||
}
|
||||
|
||||
public Collection<ConfigAttribute> getConfigAttributes() {
|
||||
return this.configAttributes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.event;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Event indicating a secure object was invoked successfully.
|
||||
* <P>
|
||||
* Published just before the secure object attempts to proceed.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.event.AuthorizationGrantedEvent}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("serial")
|
||||
public class AuthorizedEvent extends AbstractAuthorizationEvent {
|
||||
|
||||
private final Authentication authentication;
|
||||
|
||||
private final Collection<ConfigAttribute> configAttributes;
|
||||
|
||||
/**
|
||||
* Construct the event.
|
||||
* @param secureObject the secure object
|
||||
* @param attributes that apply to the secure object
|
||||
* @param authentication that successfully called the secure object
|
||||
*
|
||||
*/
|
||||
public AuthorizedEvent(Object secureObject, Collection<ConfigAttribute> attributes, Authentication authentication) {
|
||||
super(secureObject);
|
||||
Assert.isTrue(attributes != null && authentication != null, "All parameters are required and cannot be null");
|
||||
this.configAttributes = attributes;
|
||||
this.authentication = authentication;
|
||||
}
|
||||
|
||||
public Authentication getAuthentication() {
|
||||
return this.authentication;
|
||||
}
|
||||
|
||||
public Collection<ConfigAttribute> getConfigAttributes() {
|
||||
return this.configAttributes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.event;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
|
||||
/**
|
||||
* Outputs interceptor-related application events to Commons Logging.
|
||||
* <p>
|
||||
* All failures are logged at the warning level, with success events logged at the
|
||||
* information level, and public invocation events logged at the debug level.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated Logging is now embedded in Spring Security components. If you need further
|
||||
* logging, please consider using your own {@link ApplicationListener}
|
||||
*/
|
||||
@Deprecated
|
||||
public class LoggerListener implements ApplicationListener<AbstractAuthorizationEvent> {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(LoggerListener.class);
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(AbstractAuthorizationEvent event) {
|
||||
if (event instanceof AuthenticationCredentialsNotFoundEvent) {
|
||||
onAuthenticationCredentialsNotFoundEvent((AuthenticationCredentialsNotFoundEvent) event);
|
||||
}
|
||||
if (event instanceof AuthorizationFailureEvent) {
|
||||
onAuthorizationFailureEvent((AuthorizationFailureEvent) event);
|
||||
}
|
||||
if (event instanceof AuthorizedEvent) {
|
||||
onAuthorizedEvent((AuthorizedEvent) event);
|
||||
}
|
||||
if (event instanceof PublicInvocationEvent) {
|
||||
onPublicInvocationEvent((PublicInvocationEvent) event);
|
||||
}
|
||||
}
|
||||
|
||||
private void onAuthenticationCredentialsNotFoundEvent(AuthenticationCredentialsNotFoundEvent authEvent) {
|
||||
logger.warn(LogMessage.format(
|
||||
"Security interception failed due to: %s; secure object: %s; configuration attributes: %s",
|
||||
authEvent.getCredentialsNotFoundException(), authEvent.getSource(), authEvent.getConfigAttributes()));
|
||||
}
|
||||
|
||||
private void onPublicInvocationEvent(PublicInvocationEvent event) {
|
||||
logger.info(LogMessage.format("Security interception not required for public secure object: %s",
|
||||
event.getSource()));
|
||||
}
|
||||
|
||||
private void onAuthorizedEvent(AuthorizedEvent authEvent) {
|
||||
logger.info(LogMessage.format(
|
||||
"Security authorized for authenticated principal: %s; secure object: %s; configuration attributes: %s",
|
||||
authEvent.getAuthentication(), authEvent.getSource(), authEvent.getConfigAttributes()));
|
||||
}
|
||||
|
||||
private void onAuthorizationFailureEvent(AuthorizationFailureEvent authEvent) {
|
||||
logger.warn(LogMessage.format(
|
||||
"Security authorization failed due to: %s; authenticated principal: %s; secure object: %s; configuration attributes: %s",
|
||||
authEvent.getAccessDeniedException(), authEvent.getAuthentication(), authEvent.getSource(),
|
||||
authEvent.getConfigAttributes()));
|
||||
}
|
||||
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.event;
|
||||
|
||||
import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
|
||||
|
||||
/**
|
||||
* Event that is generated whenever a public secure object is invoked.
|
||||
* <p>
|
||||
* A public secure object is a secure object that has no <code>ConfigAttribute</code>s
|
||||
* defined. A public secure object will not cause the <code>SecurityContextHolder</code>
|
||||
* to be inspected or authenticated, and no authorization will take place.
|
||||
* </p>
|
||||
* <p>
|
||||
* Published just before the secure object attempts to proceed.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated Only used by now-deprecated classes. Consider
|
||||
* {@link AuthorizationGrantedEvent#getSource()} to deduce public invocations.
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("serial")
|
||||
public class PublicInvocationEvent extends AbstractAuthorizationEvent {
|
||||
|
||||
/**
|
||||
* Construct the event, passing in the public secure object.
|
||||
* @param secureObject the public secure object
|
||||
*/
|
||||
public PublicInvocationEvent(Object secureObject) {
|
||||
super(secureObject);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Authorization event and listener classes.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.access.event;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
-82
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.expression.method;
|
||||
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Contains both filtering and authorization expression meta-data for Spring-EL based
|
||||
* access control.
|
||||
* <p>
|
||||
* Base class for pre or post-invocation phases of a method invocation.
|
||||
* <p>
|
||||
* Either filter or authorization expressions may be null, but not both.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
* @deprecated Use {@link org.springframework.security.authorization.AuthorizationManager}
|
||||
* interceptors instead
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
abstract class AbstractExpressionBasedMethodConfigAttribute implements ConfigAttribute {
|
||||
|
||||
private final @Nullable Expression filterExpression;
|
||||
|
||||
private final @Nullable Expression authorizeExpression;
|
||||
|
||||
/**
|
||||
* Parses the supplied expressions as Spring-EL.
|
||||
*/
|
||||
AbstractExpressionBasedMethodConfigAttribute(String filterExpression, String authorizeExpression)
|
||||
throws ParseException {
|
||||
Assert.isTrue(filterExpression != null || authorizeExpression != null,
|
||||
"Filter and authorization Expressions cannot both be null");
|
||||
SpelExpressionParser parser = new SpelExpressionParser();
|
||||
this.filterExpression = (filterExpression != null) ? parser.parseExpression(filterExpression) : null;
|
||||
this.authorizeExpression = (authorizeExpression != null) ? parser.parseExpression(authorizeExpression) : null;
|
||||
}
|
||||
|
||||
AbstractExpressionBasedMethodConfigAttribute(Expression filterExpression, Expression authorizeExpression)
|
||||
throws ParseException {
|
||||
Assert.isTrue(filterExpression != null || authorizeExpression != null,
|
||||
"Filter and authorization Expressions cannot both be null");
|
||||
this.filterExpression = (filterExpression != null) ? filterExpression : null;
|
||||
this.authorizeExpression = (authorizeExpression != null) ? authorizeExpression : null;
|
||||
}
|
||||
|
||||
Expression getFilterExpression() {
|
||||
return this.filterExpression;
|
||||
}
|
||||
|
||||
Expression getAuthorizeExpression() {
|
||||
return this.authorizeExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getAttribute() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
-107
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.expression.method;
|
||||
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.security.access.prepost.PostInvocationAttribute;
|
||||
import org.springframework.security.access.prepost.PreInvocationAttribute;
|
||||
import org.springframework.security.access.prepost.PrePostInvocationAttributeFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link PrePostInvocationAttributeFactory} which interprets the annotation value as an
|
||||
* expression to be evaluated at runtime.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Rob Winch
|
||||
* @since 3.0
|
||||
* @deprecated Use {@link org.springframework.security.authorization.AuthorizationManager}
|
||||
* interceptors instead
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public class ExpressionBasedAnnotationAttributeFactory implements PrePostInvocationAttributeFactory {
|
||||
|
||||
private final Object parserLock = new Object();
|
||||
|
||||
private @Nullable ExpressionParser parser;
|
||||
|
||||
private MethodSecurityExpressionHandler handler;
|
||||
|
||||
public ExpressionBasedAnnotationAttributeFactory(MethodSecurityExpressionHandler handler) {
|
||||
Assert.notNull(handler, "handler cannot be null");
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreInvocationAttribute createPreInvocationAttribute(String preFilterAttribute, String filterObject,
|
||||
String preAuthorizeAttribute) {
|
||||
try {
|
||||
// TODO: Optimization of permitAll
|
||||
ExpressionParser parser = getParser();
|
||||
Expression preAuthorizeExpression = (preAuthorizeAttribute != null)
|
||||
? parser.parseExpression(preAuthorizeAttribute) : parser.parseExpression("permitAll");
|
||||
Expression preFilterExpression = (preFilterAttribute != null) ? parser.parseExpression(preFilterAttribute)
|
||||
: null;
|
||||
return new PreInvocationExpressionAttribute(preFilterExpression, filterObject, preAuthorizeExpression);
|
||||
}
|
||||
catch (ParseException ex) {
|
||||
throw new IllegalArgumentException("Failed to parse expression '" + ex.getExpressionString() + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PostInvocationAttribute createPostInvocationAttribute(String postFilterAttribute,
|
||||
String postAuthorizeAttribute) {
|
||||
try {
|
||||
ExpressionParser parser = getParser();
|
||||
Expression postAuthorizeExpression = (postAuthorizeAttribute != null)
|
||||
? parser.parseExpression(postAuthorizeAttribute) : null;
|
||||
Expression postFilterExpression = (postFilterAttribute != null)
|
||||
? parser.parseExpression(postFilterAttribute) : null;
|
||||
if (postFilterExpression != null || postAuthorizeExpression != null) {
|
||||
return new PostInvocationExpressionAttribute(postFilterExpression, postAuthorizeExpression);
|
||||
}
|
||||
}
|
||||
catch (ParseException ex) {
|
||||
throw new IllegalArgumentException("Failed to parse expression '" + ex.getExpressionString() + "'", ex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delay the lookup of the {@link ExpressionParser} to prevent SEC-2136
|
||||
* @return
|
||||
*/
|
||||
private ExpressionParser getParser() {
|
||||
if (this.parser != null) {
|
||||
return this.parser;
|
||||
}
|
||||
synchronized (this.parserLock) {
|
||||
this.parser = this.handler.getExpressionParser();
|
||||
this.handler = null;
|
||||
}
|
||||
return this.parser;
|
||||
}
|
||||
|
||||
}
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.expression.method;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.expression.ExpressionUtils;
|
||||
import org.springframework.security.access.prepost.PostInvocationAttribute;
|
||||
import org.springframework.security.access.prepost.PostInvocationAuthorizationAdvice;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class ExpressionBasedPostInvocationAdvice implements PostInvocationAuthorizationAdvice {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final MethodSecurityExpressionHandler expressionHandler;
|
||||
|
||||
public ExpressionBasedPostInvocationAdvice(MethodSecurityExpressionHandler expressionHandler) {
|
||||
this.expressionHandler = expressionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object after(Authentication authentication, MethodInvocation mi, PostInvocationAttribute postAttr,
|
||||
Object returnedObject) throws AccessDeniedException {
|
||||
PostInvocationExpressionAttribute pia = (PostInvocationExpressionAttribute) postAttr;
|
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, mi);
|
||||
Expression postFilter = pia.getFilterExpression();
|
||||
Expression postAuthorize = pia.getAuthorizeExpression();
|
||||
if (postFilter != null) {
|
||||
this.logger.debug(LogMessage.format("Applying PostFilter expression %s", postFilter));
|
||||
if (returnedObject != null) {
|
||||
returnedObject = this.expressionHandler.filter(returnedObject, postFilter, ctx);
|
||||
}
|
||||
else {
|
||||
this.logger.debug("Return object is null, filtering will be skipped");
|
||||
}
|
||||
}
|
||||
this.expressionHandler.setReturnObject(returnedObject, ctx);
|
||||
if (postAuthorize != null && !ExpressionUtils.evaluateAsBoolean(postAuthorize, ctx)) {
|
||||
this.logger.debug("PostAuthorize expression rejected access");
|
||||
throw new AccessDeniedException("Access is denied");
|
||||
}
|
||||
return returnedObject;
|
||||
}
|
||||
|
||||
}
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.expression.method;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.security.access.expression.ExpressionUtils;
|
||||
import org.springframework.security.access.prepost.PreInvocationAttribute;
|
||||
import org.springframework.security.access.prepost.PreInvocationAuthorizationAdvice;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Method pre-invocation handling based on expressions.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}
|
||||
* instead
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public class ExpressionBasedPreInvocationAdvice implements PreInvocationAuthorizationAdvice {
|
||||
|
||||
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||
|
||||
@Override
|
||||
public boolean before(Authentication authentication, MethodInvocation mi, PreInvocationAttribute attr) {
|
||||
PreInvocationExpressionAttribute preAttr = (PreInvocationExpressionAttribute) attr;
|
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, mi);
|
||||
Expression preFilter = preAttr.getFilterExpression();
|
||||
Expression preAuthorize = preAttr.getAuthorizeExpression();
|
||||
if (preFilter != null) {
|
||||
Object filterTarget = findFilterTarget(preAttr.getFilterTarget(), ctx, mi);
|
||||
this.expressionHandler.filter(filterTarget, preFilter, ctx);
|
||||
}
|
||||
return (preAuthorize != null) ? ExpressionUtils.evaluateAsBoolean(preAuthorize, ctx) : true;
|
||||
}
|
||||
|
||||
private Object findFilterTarget(String filterTargetName, EvaluationContext ctx, MethodInvocation invocation) {
|
||||
Object filterTarget = null;
|
||||
if (filterTargetName.length() > 0) {
|
||||
filterTarget = ctx.lookupVariable(filterTargetName);
|
||||
Assert.notNull(filterTarget,
|
||||
() -> "Filter target was null, or no argument with name " + filterTargetName + " found in method");
|
||||
}
|
||||
else if (invocation.getArguments().length == 1) {
|
||||
Object arg = invocation.getArguments()[0];
|
||||
if (arg.getClass().isArray() || arg instanceof Collection<?>) {
|
||||
filterTarget = arg;
|
||||
}
|
||||
Assert.notNull(filterTarget, () -> "A PreFilter expression was set but the method argument type"
|
||||
+ arg.getClass() + " is not filterable");
|
||||
}
|
||||
else if (invocation.getArguments().length > 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to determine the method argument for filtering. Specify the filter target.");
|
||||
}
|
||||
Assert.isTrue(!filterTarget.getClass().isArray(),
|
||||
"Pre-filtering on array types is not supported. Using a Collection will solve this problem");
|
||||
return filterTarget;
|
||||
}
|
||||
|
||||
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
|
||||
this.expressionHandler = expressionHandler;
|
||||
}
|
||||
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.expression.method;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.security.access.prepost.PostInvocationAttribute;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("serial")
|
||||
class PostInvocationExpressionAttribute extends AbstractExpressionBasedMethodConfigAttribute
|
||||
implements PostInvocationAttribute {
|
||||
|
||||
PostInvocationExpressionAttribute(String filterExpression, String authorizeExpression) throws ParseException {
|
||||
super(filterExpression, authorizeExpression);
|
||||
}
|
||||
|
||||
PostInvocationExpressionAttribute(@Nullable Expression filterExpression, @Nullable Expression authorizeExpression)
|
||||
throws ParseException {
|
||||
super(filterExpression, authorizeExpression);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Expression authorize = getAuthorizeExpression();
|
||||
Expression filter = getFilterExpression();
|
||||
sb.append("[authorize: '").append((authorize != null) ? authorize.getExpressionString() : "null");
|
||||
sb.append("', filter: '").append((filter != null) ? filter.getExpressionString() : "null").append("']");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.expression.method;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.security.access.prepost.PreInvocationAttribute;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
@SuppressWarnings("serial")
|
||||
class PreInvocationExpressionAttribute extends AbstractExpressionBasedMethodConfigAttribute
|
||||
implements PreInvocationAttribute {
|
||||
|
||||
private final String filterTarget;
|
||||
|
||||
PreInvocationExpressionAttribute(String filterExpression, String filterTarget, String authorizeExpression)
|
||||
throws ParseException {
|
||||
super(filterExpression, authorizeExpression);
|
||||
this.filterTarget = filterTarget;
|
||||
}
|
||||
|
||||
PreInvocationExpressionAttribute(@Nullable Expression filterExpression, String filterTarget,
|
||||
Expression authorizeExpression) throws ParseException {
|
||||
super(filterExpression, authorizeExpression);
|
||||
this.filterTarget = filterTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* The parameter name of the target argument (must be a Collection) to which filtering
|
||||
* will be applied.
|
||||
* @return the method parameter name
|
||||
*/
|
||||
String getFilterTarget() {
|
||||
return this.filterTarget;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
Expression authorize = getAuthorizeExpression();
|
||||
Expression filter = getFilterExpression();
|
||||
sb.append("[authorize: '").append((authorize != null) ? authorize.getExpressionString() : "null");
|
||||
sb.append("', filter: '").append((filter != null) ? filter.getExpressionString() : "null");
|
||||
sb.append("', filterTarget: '").append(this.filterTarget).append("']");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
-496
@@ -1,496 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.intercept;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityMetadataSource;
|
||||
import org.springframework.security.access.event.AuthenticationCredentialsNotFoundEvent;
|
||||
import org.springframework.security.access.event.AuthorizationFailureEvent;
|
||||
import org.springframework.security.access.event.AuthorizedEvent;
|
||||
import org.springframework.security.access.event.PublicInvocationEvent;
|
||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Abstract class that implements security interception for secure objects.
|
||||
* <p>
|
||||
* The <code>AbstractSecurityInterceptor</code> will ensure the proper startup
|
||||
* configuration of the security interceptor. It will also implement the proper handling
|
||||
* of secure object invocations, namely:
|
||||
* <ol>
|
||||
* <li>Obtain the {@link Authentication} object from the
|
||||
* {@link SecurityContextHolder}.</li>
|
||||
* <li>Determine if the request relates to a secured or public invocation by looking up
|
||||
* the secure object request against the {@link SecurityMetadataSource}.</li>
|
||||
* <li>For an invocation that is secured (there is a list of <code>ConfigAttribute</code>s
|
||||
* for the secure object invocation):
|
||||
* <ol type="a">
|
||||
* <li>If either the
|
||||
* {@link org.springframework.security.core.Authentication#isAuthenticated()} returns
|
||||
* <code>false</code>, or the {@link #alwaysReauthenticate} is <code>true</code>,
|
||||
* authenticate the request against the configured {@link AuthenticationManager}. When
|
||||
* authenticated, replace the <code>Authentication</code> object on the
|
||||
* <code>SecurityContextHolder</code> with the returned value.</li>
|
||||
* <li>Authorize the request against the configured {@link AccessDecisionManager}.</li>
|
||||
* <li>Perform any run-as replacement via the configured {@link RunAsManager}.</li>
|
||||
* <li>Pass control back to the concrete subclass, which will actually proceed with
|
||||
* executing the object. A {@link InterceptorStatusToken} is returned so that after the
|
||||
* subclass has finished proceeding with execution of the object, its finally clause can
|
||||
* ensure the <code>AbstractSecurityInterceptor</code> is re-called and tidies up
|
||||
* correctly using {@link #finallyInvocation(InterceptorStatusToken)}.</li>
|
||||
* <li>The concrete subclass will re-call the <code>AbstractSecurityInterceptor</code> via
|
||||
* the {@link #afterInvocation(InterceptorStatusToken, Object)} method.</li>
|
||||
* <li>If the <code>RunAsManager</code> replaced the <code>Authentication</code> object,
|
||||
* return the <code>SecurityContextHolder</code> to the object that existed after the call
|
||||
* to <code>AuthenticationManager</code>.</li>
|
||||
* <li>If an <code>AfterInvocationManager</code> is defined, invoke the invocation manager
|
||||
* and allow it to replace the object due to be returned to the caller.</li>
|
||||
* </ol>
|
||||
* </li>
|
||||
* <li>For an invocation that is public (there are no <code>ConfigAttribute</code>s for
|
||||
* the secure object invocation):
|
||||
* <ol type="a">
|
||||
* <li>As described above, the concrete subclass will be returned an
|
||||
* <code>InterceptorStatusToken</code> which is subsequently re-presented to the
|
||||
* <code>AbstractSecurityInterceptor</code> after the secure object has been executed. The
|
||||
* <code>AbstractSecurityInterceptor</code> will take no further action when its
|
||||
* {@link #afterInvocation(InterceptorStatusToken, Object)} is called.</li>
|
||||
* </ol>
|
||||
* </li>
|
||||
* <li>Control again returns to the concrete subclass, along with the <code>Object</code>
|
||||
* that should be returned to the caller. The subclass will then return that result or
|
||||
* exception to the original caller.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Rob Winch
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.web.access.intercept.AuthorizationFilter} instead
|
||||
* for filter security,
|
||||
* {@link org.springframework.security.messaging.access.intercept.AuthorizationChannelInterceptor}
|
||||
* for messaging security, or
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor}
|
||||
* and
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}
|
||||
* for method security.
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public abstract class AbstractSecurityInterceptor
|
||||
implements InitializingBean, ApplicationEventPublisherAware, MessageSourceAware {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
|
||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
.getContextHolderStrategy();
|
||||
|
||||
private @Nullable ApplicationEventPublisher eventPublisher;
|
||||
|
||||
private @Nullable AccessDecisionManager accessDecisionManager;
|
||||
|
||||
private @Nullable AfterInvocationManager afterInvocationManager;
|
||||
|
||||
private AuthenticationManager authenticationManager = new NoOpAuthenticationManager();
|
||||
|
||||
private RunAsManager runAsManager = new NullRunAsManager();
|
||||
|
||||
private boolean alwaysReauthenticate = false;
|
||||
|
||||
private boolean rejectPublicInvocations = false;
|
||||
|
||||
private boolean validateConfigAttributes = true;
|
||||
|
||||
private boolean publishAuthorizationSuccess = false;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(getSecureObjectClass(), "Subclass must provide a non-null response to getSecureObjectClass()");
|
||||
Assert.notNull(this.messages, "A message source must be set");
|
||||
Assert.notNull(this.authenticationManager, "An AuthenticationManager is required");
|
||||
Assert.notNull(this.accessDecisionManager, "An AccessDecisionManager is required");
|
||||
Assert.notNull(this.runAsManager, "A RunAsManager is required");
|
||||
Assert.notNull(this.obtainSecurityMetadataSource(), "An SecurityMetadataSource is required");
|
||||
Assert.isTrue(this.obtainSecurityMetadataSource().supports(getSecureObjectClass()),
|
||||
() -> "SecurityMetadataSource does not support secure object class: " + getSecureObjectClass());
|
||||
Assert.isTrue(this.runAsManager.supports(getSecureObjectClass()),
|
||||
() -> "RunAsManager does not support secure object class: " + getSecureObjectClass());
|
||||
Assert.isTrue(this.accessDecisionManager.supports(getSecureObjectClass()),
|
||||
() -> "AccessDecisionManager does not support secure object class: " + getSecureObjectClass());
|
||||
if (this.afterInvocationManager != null) {
|
||||
Assert.isTrue(this.afterInvocationManager.supports(getSecureObjectClass()),
|
||||
() -> "AfterInvocationManager does not support secure object class: " + getSecureObjectClass());
|
||||
}
|
||||
if (this.validateConfigAttributes) {
|
||||
Collection<ConfigAttribute> attributeDefs = this.obtainSecurityMetadataSource().getAllConfigAttributes();
|
||||
if (attributeDefs == null) {
|
||||
this.logger.warn("Could not validate configuration attributes as the "
|
||||
+ "SecurityMetadataSource did not return any attributes from getAllConfigAttributes()");
|
||||
return;
|
||||
}
|
||||
validateAttributeDefs(attributeDefs);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateAttributeDefs(Collection<ConfigAttribute> attributeDefs) {
|
||||
Set<ConfigAttribute> unsupportedAttrs = new HashSet<>();
|
||||
for (ConfigAttribute attr : attributeDefs) {
|
||||
if (!this.runAsManager.supports(attr) && !this.accessDecisionManager.supports(attr)
|
||||
&& ((this.afterInvocationManager == null) || !this.afterInvocationManager.supports(attr))) {
|
||||
unsupportedAttrs.add(attr);
|
||||
}
|
||||
}
|
||||
if (unsupportedAttrs.size() != 0) {
|
||||
this.logger
|
||||
.trace("Did not validate configuration attributes since validateConfigurationAttributes is false");
|
||||
throw new IllegalArgumentException("Unsupported configuration attributes: " + unsupportedAttrs);
|
||||
}
|
||||
else {
|
||||
this.logger.trace("Validated configuration attributes");
|
||||
}
|
||||
}
|
||||
|
||||
protected @Nullable InterceptorStatusToken beforeInvocation(Object object) {
|
||||
Assert.notNull(object, "Object was null");
|
||||
if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
|
||||
throw new IllegalArgumentException("Security invocation attempted for object " + object.getClass().getName()
|
||||
+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "
|
||||
+ getSecureObjectClass());
|
||||
}
|
||||
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource().getAttributes(object);
|
||||
if (CollectionUtils.isEmpty(attributes)) {
|
||||
Assert.isTrue(!this.rejectPublicInvocations,
|
||||
() -> "Secure object invocation " + object
|
||||
+ " was denied as public invocations are not allowed via this interceptor. "
|
||||
+ "This indicates a configuration error because the "
|
||||
+ "rejectPublicInvocations property is set to 'true'");
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(LogMessage.format("Authorized public object %s", object));
|
||||
}
|
||||
publishEvent(new PublicInvocationEvent(object));
|
||||
return null; // no further work post-invocation
|
||||
}
|
||||
if (this.securityContextHolderStrategy.getContext().getAuthentication() == null) {
|
||||
credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
|
||||
"An Authentication object was not found in the SecurityContext"), object, attributes);
|
||||
}
|
||||
Authentication authenticated = authenticateIfRequired();
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace(LogMessage.format("Authorizing %s with attributes %s", object, attributes));
|
||||
}
|
||||
// Attempt authorization
|
||||
attemptAuthorization(object, attributes, authenticated);
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(LogMessage.format("Authorized %s with attributes %s", object, attributes));
|
||||
}
|
||||
if (this.publishAuthorizationSuccess) {
|
||||
publishEvent(new AuthorizedEvent(object, attributes, authenticated));
|
||||
}
|
||||
|
||||
// Attempt to run as a different user
|
||||
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
|
||||
if (runAs != null) {
|
||||
SecurityContext origCtx = this.securityContextHolderStrategy.getContext();
|
||||
SecurityContext newCtx = this.securityContextHolderStrategy.createEmptyContext();
|
||||
newCtx.setAuthentication(runAs);
|
||||
this.securityContextHolderStrategy.setContext(newCtx);
|
||||
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(LogMessage.format("Switched to RunAs authentication %s", runAs));
|
||||
}
|
||||
// need to revert to token.Authenticated post-invocation
|
||||
return new InterceptorStatusToken(origCtx, true, attributes, object);
|
||||
}
|
||||
this.logger.trace("Did not switch RunAs authentication since RunAsManager returned null");
|
||||
// no further work post-invocation
|
||||
return new InterceptorStatusToken(this.securityContextHolderStrategy.getContext(), false, attributes, object);
|
||||
|
||||
}
|
||||
|
||||
private void attemptAuthorization(Object object, Collection<ConfigAttribute> attributes,
|
||||
Authentication authenticated) {
|
||||
try {
|
||||
this.accessDecisionManager.decide(authenticated, object, attributes);
|
||||
}
|
||||
catch (AccessDeniedException ex) {
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace(LogMessage.format("Failed to authorize %s with attributes %s using %s", object,
|
||||
attributes, this.accessDecisionManager));
|
||||
}
|
||||
else if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(LogMessage.format("Failed to authorize %s with attributes %s", object, attributes));
|
||||
}
|
||||
publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated, ex));
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the work of the <tt>AbstractSecurityInterceptor</tt> after the secure
|
||||
* object invocation has been completed. This method should be invoked after the
|
||||
* secure object invocation and before afterInvocation regardless of the secure object
|
||||
* invocation returning successfully (i.e. it should be done in a finally block).
|
||||
* @param token as returned by the {@link #beforeInvocation(Object)} method
|
||||
*/
|
||||
protected void finallyInvocation(InterceptorStatusToken token) {
|
||||
if (token != null && token.isContextHolderRefreshRequired()) {
|
||||
this.securityContextHolderStrategy.setContext(token.getSecurityContext());
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(LogMessage
|
||||
.of(() -> "Reverted to original authentication " + token.getSecurityContext().getAuthentication()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes the work of the <tt>AbstractSecurityInterceptor</tt> after the secure
|
||||
* object invocation has been completed.
|
||||
* @param token as returned by the {@link #beforeInvocation(Object)} method
|
||||
* @param returnedObject any object returned from the secure object invocation (may be
|
||||
* <tt>null</tt>)
|
||||
* @return the object the secure object invocation should ultimately return to its
|
||||
* caller (may be <tt>null</tt>)
|
||||
*/
|
||||
protected Object afterInvocation(InterceptorStatusToken token, @Nullable Object returnedObject) {
|
||||
if (token == null) {
|
||||
// public object
|
||||
return returnedObject;
|
||||
}
|
||||
finallyInvocation(token); // continue to clean in this method for passivity
|
||||
if (this.afterInvocationManager != null) {
|
||||
// Attempt after invocation handling
|
||||
try {
|
||||
returnedObject = this.afterInvocationManager.decide(token.getSecurityContext().getAuthentication(),
|
||||
token.getSecureObject(), token.getAttributes(), returnedObject);
|
||||
}
|
||||
catch (AccessDeniedException ex) {
|
||||
publishEvent(new AuthorizationFailureEvent(token.getSecureObject(), token.getAttributes(),
|
||||
token.getSecurityContext().getAuthentication(), ex));
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
return returnedObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the current authentication token and passes it to the AuthenticationManager
|
||||
* if {@link org.springframework.security.core.Authentication#isAuthenticated()}
|
||||
* returns false or the property <tt>alwaysReauthenticate</tt> has been set to true.
|
||||
* @return an authenticated <tt>Authentication</tt> object.
|
||||
*/
|
||||
private Authentication authenticateIfRequired() {
|
||||
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
|
||||
if (authentication.isAuthenticated() && !this.alwaysReauthenticate) {
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace(LogMessage.format("Did not re-authenticate %s before authorizing", authentication));
|
||||
}
|
||||
return authentication;
|
||||
}
|
||||
authentication = this.authenticationManager.authenticate(authentication);
|
||||
// Don't authenticated.setAuthentication(true) because each provider does that
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug(LogMessage.format("Re-authenticated %s before authorizing", authentication));
|
||||
}
|
||||
SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
|
||||
context.setAuthentication(authentication);
|
||||
this.securityContextHolderStrategy.setContext(context);
|
||||
return authentication;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method which generates an exception containing the passed reason, and
|
||||
* publishes an event to the application context.
|
||||
* <p>
|
||||
* Always throws an exception.
|
||||
* @param reason to be provided in the exception detail
|
||||
* @param secureObject that was being called
|
||||
* @param configAttribs that were defined for the secureObject
|
||||
*/
|
||||
private void credentialsNotFound(String reason, Object secureObject, Collection<ConfigAttribute> configAttribs) {
|
||||
AuthenticationCredentialsNotFoundException exception = new AuthenticationCredentialsNotFoundException(reason);
|
||||
AuthenticationCredentialsNotFoundEvent event = new AuthenticationCredentialsNotFoundEvent(secureObject,
|
||||
configAttribs, exception);
|
||||
publishEvent(event);
|
||||
throw exception;
|
||||
}
|
||||
|
||||
public AccessDecisionManager getAccessDecisionManager() {
|
||||
return this.accessDecisionManager;
|
||||
}
|
||||
|
||||
public AfterInvocationManager getAfterInvocationManager() {
|
||||
return this.afterInvocationManager;
|
||||
}
|
||||
|
||||
public AuthenticationManager getAuthenticationManager() {
|
||||
return this.authenticationManager;
|
||||
}
|
||||
|
||||
public RunAsManager getRunAsManager() {
|
||||
return this.runAsManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the type of secure objects the subclass will be presenting to the
|
||||
* abstract parent for processing. This is used to ensure collaborators wired to the
|
||||
* {@code AbstractSecurityInterceptor} all support the indicated secure object class.
|
||||
* @return the type of secure object the subclass provides services for
|
||||
*/
|
||||
public abstract Class<?> getSecureObjectClass();
|
||||
|
||||
public boolean isAlwaysReauthenticate() {
|
||||
return this.alwaysReauthenticate;
|
||||
}
|
||||
|
||||
public boolean isRejectPublicInvocations() {
|
||||
return this.rejectPublicInvocations;
|
||||
}
|
||||
|
||||
public boolean isValidateConfigAttributes() {
|
||||
return this.validateConfigAttributes;
|
||||
}
|
||||
|
||||
public abstract SecurityMetadataSource obtainSecurityMetadataSource();
|
||||
|
||||
/**
|
||||
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
|
||||
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
|
||||
*
|
||||
* @since 5.8
|
||||
*/
|
||||
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
|
||||
Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
|
||||
this.securityContextHolderStrategy = securityContextHolderStrategy;
|
||||
}
|
||||
|
||||
public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {
|
||||
this.accessDecisionManager = accessDecisionManager;
|
||||
}
|
||||
|
||||
public void setAfterInvocationManager(AfterInvocationManager afterInvocationManager) {
|
||||
this.afterInvocationManager = afterInvocationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the <code>AbstractSecurityInterceptor</code> should ignore the
|
||||
* {@link Authentication#isAuthenticated()} property. Defaults to <code>false</code>,
|
||||
* meaning by default the <code>Authentication.isAuthenticated()</code> property is
|
||||
* trusted and re-authentication will not occur if the principal has already been
|
||||
* authenticated.
|
||||
* @param alwaysReauthenticate <code>true</code> to force
|
||||
* <code>AbstractSecurityInterceptor</code> to disregard the value of
|
||||
* <code>Authentication.isAuthenticated()</code> and always re-authenticate the
|
||||
* request (defaults to <code>false</code>).
|
||||
*/
|
||||
public void setAlwaysReauthenticate(boolean alwaysReauthenticate) {
|
||||
this.alwaysReauthenticate = alwaysReauthenticate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
||||
this.eventPublisher = applicationEventPublisher;
|
||||
}
|
||||
|
||||
public void setAuthenticationManager(AuthenticationManager newManager) {
|
||||
this.authenticationManager = newManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only {@code AuthorizationFailureEvent} will be published. If you set this property
|
||||
* to {@code true}, {@code AuthorizedEvent}s will also be published.
|
||||
* @param publishAuthorizationSuccess default value is {@code false}
|
||||
*/
|
||||
public void setPublishAuthorizationSuccess(boolean publishAuthorizationSuccess) {
|
||||
this.publishAuthorizationSuccess = publishAuthorizationSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* By rejecting public invocations (and setting this property to <tt>true</tt>),
|
||||
* essentially you are ensuring that every secure object invocation advised by
|
||||
* <code>AbstractSecurityInterceptor</code> has a configuration attribute defined.
|
||||
* This is useful to ensure a "fail safe" mode where undeclared secure objects will be
|
||||
* rejected and configuration omissions detected early. An
|
||||
* <tt>IllegalArgumentException</tt> will be thrown by the
|
||||
* <tt>AbstractSecurityInterceptor</tt> if you set this property to <tt>true</tt> and
|
||||
* an attempt is made to invoke a secure object that has no configuration attributes.
|
||||
* @param rejectPublicInvocations set to <code>true</code> to reject invocations of
|
||||
* secure objects that have no configuration attributes (by default it is
|
||||
* <code>false</code> which treats undeclared secure objects as "public" or
|
||||
* unauthorized).
|
||||
*/
|
||||
public void setRejectPublicInvocations(boolean rejectPublicInvocations) {
|
||||
this.rejectPublicInvocations = rejectPublicInvocations;
|
||||
}
|
||||
|
||||
public void setRunAsManager(RunAsManager runAsManager) {
|
||||
this.runAsManager = runAsManager;
|
||||
}
|
||||
|
||||
public void setValidateConfigAttributes(boolean validateConfigAttributes) {
|
||||
this.validateConfigAttributes = validateConfigAttributes;
|
||||
}
|
||||
|
||||
private void publishEvent(ApplicationEvent event) {
|
||||
if (this.eventPublisher != null) {
|
||||
this.eventPublisher.publishEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
private static class NoOpAuthenticationManager implements AuthenticationManager {
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
throw new AuthenticationServiceException("Cannot authenticate " + authentication);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.intercept;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* Reviews the <code>Object</code> returned from a secure object invocation, being able to
|
||||
* modify the <code>Object</code> or throw an {@link AccessDeniedException}.
|
||||
* <p>
|
||||
* Typically used to ensure the principal is permitted to access the domain object
|
||||
* instance returned by a service layer bean. Can also be used to mutate the domain object
|
||||
* instance so the principal is only able to access authorised bean properties or
|
||||
* <code>Collection</code> elements.
|
||||
* <p>
|
||||
* Special consideration should be given to using an <code>AfterInvocationManager</code>
|
||||
* on bean methods that modify a database. Typically an
|
||||
* <code>AfterInvocationManager</code> is used with read-only methods, such as
|
||||
* <code>public DomainObject getById(id)</code>. If used with methods that modify a
|
||||
* database, a transaction manager should be used to ensure any
|
||||
* <code>AccessDeniedException</code> will cause a rollback of the changes made by the
|
||||
* transaction.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @see org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor
|
||||
* @see org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor
|
||||
* @deprecated Use delegation with {@link AuthorizationManager}
|
||||
*/
|
||||
@Deprecated
|
||||
public interface AfterInvocationManager {
|
||||
|
||||
/**
|
||||
* Given the details of a secure object invocation including its returned
|
||||
* <code>Object</code>, make an access control decision or optionally modify the
|
||||
* returned <code>Object</code>.
|
||||
* @param authentication the caller that invoked the method
|
||||
* @param object the secured object that was called
|
||||
* @param attributes the configuration attributes associated with the secured object
|
||||
* that was invoked
|
||||
* @param returnedObject the <code>Object</code> that was returned from the secure
|
||||
* object invocation
|
||||
* @return the <code>Object</code> that will ultimately be returned to the caller (if
|
||||
* an implementation does not wish to modify the object to be returned to the caller,
|
||||
* the implementation should simply return the same object it was passed by the
|
||||
* <code>returnedObject</code> method argument)
|
||||
* @throws AccessDeniedException if access is denied
|
||||
*/
|
||||
Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> attributes,
|
||||
Object returnedObject) throws AccessDeniedException;
|
||||
|
||||
/**
|
||||
* Indicates whether this <code>AfterInvocationManager</code> is able to process
|
||||
* "after invocation" requests presented with the passed <code>ConfigAttribute</code>.
|
||||
* <p>
|
||||
* This allows the <code>AbstractSecurityInterceptor</code> to check every
|
||||
* configuration attribute can be consumed by the configured
|
||||
* <code>AccessDecisionManager</code> and/or <code>RunAsManager</code> and/or
|
||||
* <code>AfterInvocationManager</code>.
|
||||
* </p>
|
||||
* @param attribute a configuration attribute that has been configured against the
|
||||
* <code>AbstractSecurityInterceptor</code>
|
||||
* @return true if this <code>AfterInvocationManager</code> can support the passed
|
||||
* configuration attribute
|
||||
*/
|
||||
boolean supports(ConfigAttribute attribute);
|
||||
|
||||
/**
|
||||
* Indicates whether the <code>AfterInvocationManager</code> implementation is able to
|
||||
* provide access control decisions for the indicated secured object type.
|
||||
* @param clazz the class that is being queried
|
||||
* @return <code>true</code> if the implementation can process the indicated class
|
||||
*/
|
||||
boolean supports(Class<?> clazz);
|
||||
|
||||
}
|
||||
-131
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.intercept;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.AfterInvocationProvider;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Provider-based implementation of {@link AfterInvocationManager}.
|
||||
* <p>
|
||||
* Handles configuration of a bean context defined list of {@link AfterInvocationProvider}
|
||||
* s.
|
||||
* <p>
|
||||
* Every <code>AfterInvocationProvider</code> will be polled when the
|
||||
* {@link #decide(Authentication, Object, Collection, Object)} method is called. The
|
||||
* <code>Object</code> returned from each provider will be presented to the successive
|
||||
* provider for processing. This means each provider <b>must</b> ensure they return the
|
||||
* <code>Object</code>, even if they are not interested in the "after invocation" decision
|
||||
* (perhaps as the secure object invocation did not include a configuration attribute a
|
||||
* given provider is configured to respond to).
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @see org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor
|
||||
* @see org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor
|
||||
* @deprecated Use delegation with {@link AuthorizationManager}
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public class AfterInvocationProviderManager implements AfterInvocationManager, InitializingBean {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(AfterInvocationProviderManager.class);
|
||||
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
private @Nullable List<AfterInvocationProvider> providers;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
checkIfValidList(this.providers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> config,
|
||||
Object returnedObject) throws AccessDeniedException {
|
||||
Object result = returnedObject;
|
||||
for (AfterInvocationProvider provider : this.providers) {
|
||||
result = provider.decide(authentication, object, config, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<AfterInvocationProvider> getProviders() {
|
||||
return this.providers;
|
||||
}
|
||||
|
||||
public void setProviders(List<?> newList) {
|
||||
checkIfValidList(newList);
|
||||
this.providers = new ArrayList<>(newList.size());
|
||||
for (Object currentObject : newList) {
|
||||
Assert.isInstanceOf(AfterInvocationProvider.class, currentObject, () -> "AfterInvocationProvider "
|
||||
+ currentObject.getClass().getName() + " must implement AfterInvocationProvider");
|
||||
this.providers.add((AfterInvocationProvider) currentObject);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkIfValidList(List<?> listToCheck) {
|
||||
Assert.isTrue(!CollectionUtils.isEmpty(listToCheck), "A list of AfterInvocationProviders is required");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
for (AfterInvocationProvider provider : this.providers) {
|
||||
logger.debug(LogMessage.format("Evaluating %s against %s", attribute, provider));
|
||||
if (provider.supports(attribute)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through all <code>AfterInvocationProvider</code>s and ensures each can
|
||||
* support the presented class.
|
||||
* <p>
|
||||
* If one or more providers cannot support the presented class, <code>false</code> is
|
||||
* returned.
|
||||
* @param clazz the secure object class being queries
|
||||
* @return if the <code>AfterInvocationProviderManager</code> can support the secure
|
||||
* object class, which requires every one of its <code>AfterInvocationProvider</code>s
|
||||
* to support the secure object class
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
for (AfterInvocationProvider provider : this.providers) {
|
||||
if (!provider.supports(clazz)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.intercept;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
|
||||
/**
|
||||
* A return object received by {@link AbstractSecurityInterceptor} subclasses.
|
||||
* <p>
|
||||
* This class reflects the status of the security interception, so that the final call to
|
||||
* {@link org.springframework.security.access.intercept.AbstractSecurityInterceptor#afterInvocation(InterceptorStatusToken, Object)}
|
||||
* can tidy up correctly.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @see org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor
|
||||
* @see org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor
|
||||
* @deprecated Use delegation with {@link AuthorizationManager}
|
||||
*/
|
||||
@Deprecated
|
||||
public class InterceptorStatusToken {
|
||||
|
||||
private SecurityContext securityContext;
|
||||
|
||||
private Collection<ConfigAttribute> attr;
|
||||
|
||||
private Object secureObject;
|
||||
|
||||
private boolean contextHolderRefreshRequired;
|
||||
|
||||
public InterceptorStatusToken(SecurityContext securityContext, boolean contextHolderRefreshRequired,
|
||||
Collection<ConfigAttribute> attributes, Object secureObject) {
|
||||
this.securityContext = securityContext;
|
||||
this.contextHolderRefreshRequired = contextHolderRefreshRequired;
|
||||
this.attr = attributes;
|
||||
this.secureObject = secureObject;
|
||||
}
|
||||
|
||||
public Collection<ConfigAttribute> getAttributes() {
|
||||
return this.attr;
|
||||
}
|
||||
|
||||
public SecurityContext getSecurityContext() {
|
||||
return this.securityContext;
|
||||
}
|
||||
|
||||
public Object getSecureObject() {
|
||||
return this.secureObject;
|
||||
}
|
||||
|
||||
public boolean isContextHolderRefreshRequired() {
|
||||
return this.contextHolderRefreshRequired;
|
||||
}
|
||||
|
||||
}
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.intercept;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Allows users to determine whether they have "before invocation" privileges for a given
|
||||
* method invocation.
|
||||
* <p>
|
||||
* Of course, if an
|
||||
* {@link org.springframework.security.access.intercept.AfterInvocationManager} is used to
|
||||
* authorize the <em>result</em> of a method invocation, this class cannot assist
|
||||
* determine whether or not the <code>AfterInvocationManager</code> will enable access.
|
||||
* Instead this class aims to allow applications to determine whether or not the current
|
||||
* principal would be allowed to at least attempt to invoke the method, irrespective of
|
||||
* the "after" invocation handling.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated Use {@link org.springframework.security.authorization.AuthorizationManager}
|
||||
* instead
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public class MethodInvocationPrivilegeEvaluator implements InitializingBean {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(MethodInvocationPrivilegeEvaluator.class);
|
||||
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
private @Nullable AbstractSecurityInterceptor securityInterceptor;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(this.securityInterceptor, "SecurityInterceptor required");
|
||||
}
|
||||
|
||||
public boolean isAllowed(MethodInvocation invocation, Authentication authentication) {
|
||||
Assert.notNull(invocation, "MethodInvocation required");
|
||||
Assert.notNull(invocation.getMethod(), "MethodInvocation must provide a non-null getMethod()");
|
||||
Collection<ConfigAttribute> attrs = this.securityInterceptor.obtainSecurityMetadataSource()
|
||||
.getAttributes(invocation);
|
||||
if (attrs == null) {
|
||||
return !this.securityInterceptor.isRejectPublicInvocations();
|
||||
}
|
||||
if (authentication == null || authentication.getAuthorities().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
this.securityInterceptor.getAccessDecisionManager().decide(authentication, invocation, attrs);
|
||||
return true;
|
||||
}
|
||||
catch (AccessDeniedException unauthorized) {
|
||||
logger.debug(LogMessage.format("%s denied for %s", invocation, authentication), unauthorized);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setSecurityInterceptor(AbstractSecurityInterceptor securityInterceptor) {
|
||||
Assert.notNull(securityInterceptor, "AbstractSecurityInterceptor cannot be null");
|
||||
Assert.isTrue(MethodInvocation.class.equals(securityInterceptor.getSecureObjectClass()),
|
||||
"AbstractSecurityInterceptor does not support MethodInvocations");
|
||||
Assert.notNull(securityInterceptor.getAccessDecisionManager(),
|
||||
"AbstractSecurityInterceptor must provide a non-null AccessDecisionManager");
|
||||
this.securityInterceptor = securityInterceptor;
|
||||
}
|
||||
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.intercept;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* Implementation of a {@link RunAsManager} that does nothing.
|
||||
* <p>
|
||||
* This class should be used if you do not require run-as authentication replacement
|
||||
* functionality.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated please see {@link RunAsManager} deprecation notice
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
final class NullRunAsManager implements RunAsManager {
|
||||
|
||||
@Override
|
||||
public @Nullable Authentication buildRunAs(Authentication authentication, Object object,
|
||||
Collection<ConfigAttribute> config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
-92
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.intercept;
|
||||
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} implementation that can authenticate a
|
||||
* {@link RunAsUserToken}.
|
||||
* <P>
|
||||
* Configured in the bean context with a key that should match the key used by adapters to
|
||||
* generate the <code>RunAsUserToken</code>. It treats as valid any
|
||||
* <code>RunAsUserToken</code> instance presenting a hash code that matches the
|
||||
* <code>RunAsImplAuthenticationProvider</code>-configured key.
|
||||
* </p>
|
||||
* <P>
|
||||
* If the key does not match, a <code>BadCredentialsException</code> is thrown.
|
||||
* </p>
|
||||
*
|
||||
* @deprecated Authentication is now separated from authorization in Spring Security. This
|
||||
* class is only used by now-deprecated components. There is not yet an equivalent
|
||||
* replacement in Spring Security.
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public class RunAsImplAuthenticationProvider implements InitializingBean, AuthenticationProvider, MessageSourceAware {
|
||||
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
private @Nullable String key;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(this.key, "A Key is required and should match that configured for the RunAsManagerImpl");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
RunAsUserToken token = (RunAsUserToken) authentication;
|
||||
if (token.getKeyHash() != this.key.hashCode()) {
|
||||
throw new BadCredentialsException(this.messages.getMessage("RunAsImplAuthenticationProvider.incorrectKey",
|
||||
"The presented RunAsUserToken does not contain the expected key"));
|
||||
}
|
||||
return authentication;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return RunAsUserToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.intercept;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* Creates a new temporary {@link Authentication} object for the current secure object
|
||||
* invocation only.
|
||||
*
|
||||
* <p>
|
||||
* This interface permits implementations to replace the <code>Authentication</code>
|
||||
* object that applies to the current secure object invocation only. The
|
||||
* {@link org.springframework.security.access.intercept.AbstractSecurityInterceptor} will
|
||||
* replace the <code>Authentication</code> object held in the
|
||||
* {@link org.springframework.security.core.context.SecurityContext SecurityContext} for
|
||||
* the duration of the secure object callback only, returning it to the original
|
||||
* <code>Authentication</code> object when the callback ends.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This is provided so that systems with two layers of objects can be established. One
|
||||
* layer is public facing and has normal secure methods with the granted authorities
|
||||
* expected to be held by external callers. The other layer is private, and is only
|
||||
* expected to be called by objects within the public facing layer. The objects in this
|
||||
* private layer still need security (otherwise they would be public methods) and they
|
||||
* also need security in such a manner that prevents them being called directly by
|
||||
* external callers. The objects in the private layer would be configured to require
|
||||
* granted authorities never granted to external callers. The <code>RunAsManager</code>
|
||||
* interface provides a mechanism to elevate security in this manner.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* It is expected implementations will provide a corresponding concrete
|
||||
* <code>Authentication</code> and <code>AuthenticationProvider</code> so that the
|
||||
* replacement <code>Authentication</code> object can be authenticated. Some form of
|
||||
* security will need to be implemented to ensure the <code>AuthenticationProvider</code>
|
||||
* only accepts <code>Authentication</code> objects created by an authorized concrete
|
||||
* implementation of <code>RunAsManager</code>.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated Authentication is now separated from authorization in Spring Security. This
|
||||
* class is only used by now-deprecated components. There is not yet an equivalent
|
||||
* replacement in Spring Security.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface RunAsManager {
|
||||
|
||||
/**
|
||||
* Returns a replacement <code>Authentication</code> object for the current secure
|
||||
* object invocation, or <code>null</code> if replacement not required.
|
||||
* @param authentication the caller invoking the secure object
|
||||
* @param object the secured object being called
|
||||
* @param attributes the configuration attributes associated with the secure object
|
||||
* being invoked
|
||||
* @return a replacement object to be used for duration of the secure object
|
||||
* invocation, or <code>null</code> if the <code>Authentication</code> should be left
|
||||
* as is
|
||||
*/
|
||||
Authentication buildRunAs(Authentication authentication, Object object, Collection<ConfigAttribute> attributes);
|
||||
|
||||
/**
|
||||
* Indicates whether this <code>RunAsManager</code> is able to process the passed
|
||||
* <code>ConfigAttribute</code>.
|
||||
* <p>
|
||||
* This allows the <code>AbstractSecurityInterceptor</code> to check every
|
||||
* configuration attribute can be consumed by the configured
|
||||
* <code>AccessDecisionManager</code> and/or <code>RunAsManager</code> and/or
|
||||
* <code>AfterInvocationManager</code>.
|
||||
* </p>
|
||||
* @param attribute a configuration attribute that has been configured against the
|
||||
* <code>AbstractSecurityInterceptor</code>
|
||||
* @return <code>true</code> if this <code>RunAsManager</code> can support the passed
|
||||
* configuration attribute
|
||||
*/
|
||||
boolean supports(ConfigAttribute attribute);
|
||||
|
||||
/**
|
||||
* Indicates whether the <code>RunAsManager</code> implementation is able to provide
|
||||
* run-as replacement for the indicated secure object type.
|
||||
* @param clazz the class that is being queried
|
||||
* @return true if the implementation can process the indicated class
|
||||
*/
|
||||
boolean supports(Class<?> clazz);
|
||||
|
||||
}
|
||||
-134
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.intercept;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Basic concrete implementation of a {@link RunAsManager}.
|
||||
* <p>
|
||||
* Is activated if any {@link ConfigAttribute#getAttribute()} is prefixed with
|
||||
* <Code>RUN_AS_</code>. If found, it generates a new {@link RunAsUserToken} containing
|
||||
* the same principal, credentials and granted authorities as the original
|
||||
* {@link Authentication} object, along with {@link SimpleGrantedAuthority}s for each
|
||||
* <code>RUN_AS_</code> indicated. The created <code>SimpleGrantedAuthority</code>s will
|
||||
* be prefixed with a special prefix indicating that it is a role (default prefix value is
|
||||
* <code>ROLE_</code>), and then the remainder of the <code>RUN_AS_</code> keyword. For
|
||||
* example, <code>RUN_AS_FOO</code> will result in the creation of a granted authority of
|
||||
* <code>ROLE_RUN_AS_FOO</code>.
|
||||
* <p>
|
||||
* The role prefix may be overridden from the default, to match that used elsewhere, for
|
||||
* example when using an existing role database with another prefix. An empty role prefix
|
||||
* may also be specified. Note however that there are potential issues with using an empty
|
||||
* role prefix since different categories of {@link ConfigAttribute} can not be properly
|
||||
* discerned based on the prefix, with possible consequences when performing voting and
|
||||
* other actions. However, this option may be of some use when using pre-existing role
|
||||
* names without a prefix, and no ability exists to prefix them with a role prefix on
|
||||
* reading them in, such as provided for example in
|
||||
* {@link org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author colin sampaleanu
|
||||
* @deprecated Authentication is now separated from authorization in Spring Security. This
|
||||
* class is only used by now-deprecated components. There is not yet an equivalent
|
||||
* replacement in Spring Security.
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public class RunAsManagerImpl implements RunAsManager, InitializingBean {
|
||||
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
private @Nullable String key;
|
||||
|
||||
private String rolePrefix = "ROLE_";
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(this.key,
|
||||
"A Key is required and should match that configured for the RunAsImplAuthenticationProvider");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Authentication buildRunAs(Authentication authentication, Object object,
|
||||
Collection<ConfigAttribute> attributes) {
|
||||
List<GrantedAuthority> newAuthorities = new ArrayList<>();
|
||||
for (ConfigAttribute attribute : attributes) {
|
||||
if (this.supports(attribute)) {
|
||||
GrantedAuthority extraAuthority = new SimpleGrantedAuthority(
|
||||
getRolePrefix() + attribute.getAttribute());
|
||||
newAuthorities.add(extraAuthority);
|
||||
}
|
||||
}
|
||||
if (newAuthorities.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
// Add existing authorities
|
||||
newAuthorities.addAll(authentication.getAuthorities());
|
||||
return new RunAsUserToken(this.key, authentication.getPrincipal(), authentication.getCredentials(),
|
||||
newAuthorities, authentication.getClass());
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public String getRolePrefix() {
|
||||
return this.rolePrefix;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the default role prefix of <code>ROLE_</code> to be overridden. May be set
|
||||
* to an empty value, although this is usually not desirable.
|
||||
* @param rolePrefix the new prefix
|
||||
*/
|
||||
public void setRolePrefix(String rolePrefix) {
|
||||
this.rolePrefix = rolePrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return attribute.getAttribute() != null && attribute.getAttribute().startsWith("RUN_AS_");
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation supports any type of class, because it does not query the
|
||||
* presented secure object.
|
||||
* @param clazz the secure object
|
||||
* @return always <code>true</code>
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
-84
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.intercept;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* An immutable {@link org.springframework.security.core.Authentication} implementation
|
||||
* that supports {@link RunAsManagerImpl}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated Authentication is now separated from authorization in Spring Security. This
|
||||
* class is only used by now-deprecated components. There is not yet an equivalent
|
||||
* replacement in Spring Security.
|
||||
*/
|
||||
@Deprecated
|
||||
public class RunAsUserToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = 620L;
|
||||
|
||||
private final Class<? extends Authentication> originalAuthentication;
|
||||
|
||||
private final Object credentials;
|
||||
|
||||
private final Object principal;
|
||||
|
||||
private final int keyHash;
|
||||
|
||||
public RunAsUserToken(String key, Object principal, Object credentials,
|
||||
Collection<? extends GrantedAuthority> authorities,
|
||||
Class<? extends Authentication> originalAuthentication) {
|
||||
super(authorities);
|
||||
this.keyHash = key.hashCode();
|
||||
this.principal = principal;
|
||||
this.credentials = credentials;
|
||||
this.originalAuthentication = originalAuthentication;
|
||||
setAuthenticated(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return this.credentials;
|
||||
}
|
||||
|
||||
public int getKeyHash() {
|
||||
return this.keyHash;
|
||||
}
|
||||
|
||||
public Class<? extends Authentication> getOriginalAuthentication() {
|
||||
return this.originalAuthentication;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(super.toString());
|
||||
String className = (this.originalAuthentication != null) ? this.originalAuthentication.getName() : null;
|
||||
sb.append("; Original Class: ").append(className);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
-91
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.intercept.aopalliance;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.access.SecurityMetadataSource;
|
||||
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
|
||||
import org.springframework.security.access.intercept.InterceptorStatusToken;
|
||||
import org.springframework.security.access.method.MethodSecurityMetadataSource;
|
||||
|
||||
/**
|
||||
* Provides security interception of AOP Alliance based method invocations.
|
||||
* <p>
|
||||
* The <code>SecurityMetadataSource</code> required by this security interceptor is of
|
||||
* type {@link MethodSecurityMetadataSource}. This is shared with the AspectJ based
|
||||
* security interceptor (<code>AspectJSecurityInterceptor</code>), since both work with
|
||||
* Java <code>Method</code>s.
|
||||
* <p>
|
||||
* Refer to {@link AbstractSecurityInterceptor} for details on the workflow.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Rob Winch
|
||||
* @deprecated Please use
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor}
|
||||
* and
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}
|
||||
* instead
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public class MethodSecurityInterceptor extends AbstractSecurityInterceptor implements MethodInterceptor {
|
||||
|
||||
private @Nullable MethodSecurityMetadataSource securityMetadataSource;
|
||||
|
||||
@Override
|
||||
public Class<?> getSecureObjectClass() {
|
||||
return MethodInvocation.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be used to enforce security on a <code>MethodInvocation</code>.
|
||||
* @param mi The method being invoked which requires a security decision
|
||||
* @return The returned value from the method invocation (possibly modified by the
|
||||
* {@code AfterInvocationManager}).
|
||||
* @throws Throwable if any error occurs
|
||||
*/
|
||||
@Override
|
||||
public Object invoke(MethodInvocation mi) throws Throwable {
|
||||
InterceptorStatusToken token = super.beforeInvocation(mi);
|
||||
Object result;
|
||||
try {
|
||||
result = mi.proceed();
|
||||
}
|
||||
finally {
|
||||
super.finallyInvocation(token);
|
||||
}
|
||||
return super.afterInvocation(token, result);
|
||||
}
|
||||
|
||||
public MethodSecurityMetadataSource getSecurityMetadataSource() {
|
||||
return this.securityMetadataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityMetadataSource obtainSecurityMetadataSource() {
|
||||
return this.securityMetadataSource;
|
||||
}
|
||||
|
||||
public void setSecurityMetadataSource(MethodSecurityMetadataSource newSource) {
|
||||
this.securityMetadataSource = newSource;
|
||||
}
|
||||
|
||||
}
|
||||
-138
@@ -1,138 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.intercept.aopalliance;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.support.AbstractPointcutAdvisor;
|
||||
import org.springframework.aop.support.StaticMethodMatcherPointcut;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.security.access.method.MethodSecurityMetadataSource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Advisor driven by a {@link MethodSecurityMetadataSource}, used to exclude a
|
||||
* {@link MethodInterceptor} from public (non-secure) methods.
|
||||
* <p>
|
||||
* Because the AOP framework caches advice calculations, this is normally faster than just
|
||||
* letting the <code>MethodInterceptor</code> run and find out itself that it has no work
|
||||
* to do.
|
||||
* <p>
|
||||
* This class also allows the use of Spring's {@code DefaultAdvisorAutoProxyCreator},
|
||||
* which makes configuration easier than setup a <code>ProxyFactoryBean</code> for each
|
||||
* object requiring security. Note that autoproxying is not supported for BeanFactory
|
||||
* implementations, as post-processing is automatic only for application contexts.
|
||||
* <p>
|
||||
* Based on Spring's TransactionAttributeSourceAdvisor.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Luke Taylor
|
||||
* @deprecated Use {@link EnableMethodSecurity} or publish interceptors directly
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
@SuppressWarnings("serial")
|
||||
public class MethodSecurityMetadataSourceAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
|
||||
|
||||
private transient MethodSecurityMetadataSource attributeSource;
|
||||
|
||||
private transient @Nullable MethodInterceptor interceptor;
|
||||
|
||||
private final Pointcut pointcut = new MethodSecurityMetadataSourcePointcut();
|
||||
|
||||
private @Nullable BeanFactory beanFactory;
|
||||
|
||||
private final String adviceBeanName;
|
||||
|
||||
private final String metadataSourceBeanName;
|
||||
|
||||
private transient volatile Object adviceMonitor = new Object();
|
||||
|
||||
/**
|
||||
* Alternative constructor for situations where we want the advisor decoupled from the
|
||||
* advice. Instead the advice bean name should be set. This prevents eager
|
||||
* instantiation of the interceptor (and hence the AuthenticationManager). See
|
||||
* SEC-773, for example. The metadataSourceBeanName is used rather than a direct
|
||||
* reference to support serialization via a bean factory lookup.
|
||||
* @param adviceBeanName name of the MethodSecurityInterceptor bean
|
||||
* @param attributeSource the SecurityMetadataSource (should be the same as the one
|
||||
* used on the interceptor)
|
||||
* @param attributeSourceBeanName the bean name of the attributeSource (required for
|
||||
* serialization)
|
||||
*/
|
||||
public MethodSecurityMetadataSourceAdvisor(String adviceBeanName, MethodSecurityMetadataSource attributeSource,
|
||||
String attributeSourceBeanName) {
|
||||
Assert.notNull(adviceBeanName, "The adviceBeanName cannot be null");
|
||||
Assert.notNull(attributeSource, "The attributeSource cannot be null");
|
||||
Assert.notNull(attributeSourceBeanName, "The attributeSourceBeanName cannot be null");
|
||||
this.adviceBeanName = adviceBeanName;
|
||||
this.attributeSource = attributeSource;
|
||||
this.metadataSourceBeanName = attributeSourceBeanName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pointcut getPointcut() {
|
||||
return this.pointcut;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Advice getAdvice() {
|
||||
synchronized (this.adviceMonitor) {
|
||||
if (this.interceptor == null) {
|
||||
Assert.notNull(this.adviceBeanName, "'adviceBeanName' must be set for use with bean factory lookup.");
|
||||
Assert.state(this.beanFactory != null, "BeanFactory must be set to resolve 'adviceBeanName'");
|
||||
this.interceptor = this.beanFactory.getBean(this.adviceBeanName, MethodInterceptor.class);
|
||||
}
|
||||
return this.interceptor;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||
ois.defaultReadObject();
|
||||
this.adviceMonitor = new Object();
|
||||
this.attributeSource = this.beanFactory.getBean(this.metadataSourceBeanName,
|
||||
MethodSecurityMetadataSource.class);
|
||||
}
|
||||
|
||||
class MethodSecurityMetadataSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
|
||||
|
||||
@Override
|
||||
public boolean matches(Method m, Class<?> targetClass) {
|
||||
MethodSecurityMetadataSource source = MethodSecurityMetadataSourceAdvisor.this.attributeSource;
|
||||
return !CollectionUtils.isEmpty(source.getAttributes(m, targetClass));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Enforces security for AOP Alliance <code>MethodInvocation</code>s, such as via Spring
|
||||
* AOP.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.access.intercept.aopalliance;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.intercept.aspectj;
|
||||
|
||||
/**
|
||||
* Called by the {@link AspectJMethodSecurityInterceptor} when it wishes for the AspectJ
|
||||
* processing to continue. Typically implemented in the <code>around()</code> advice as a
|
||||
* simple <code>return proceed();</code> statement.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated This class will be removed from the public API. Please either use
|
||||
* `spring-security-aspects`, Spring Security's method security support or create your own
|
||||
* class that uses Spring AOP annotations.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface AspectJCallback {
|
||||
|
||||
Object proceedWithObject();
|
||||
|
||||
}
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.intercept.aspectj;
|
||||
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
|
||||
import org.springframework.security.access.intercept.InterceptorStatusToken;
|
||||
import org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor;
|
||||
|
||||
/**
|
||||
* AspectJ {@code JoinPoint} security interceptor which wraps the {@code JoinPoint} in a
|
||||
* {@code MethodInvocation} adapter to make it compatible with security infrastructure
|
||||
* classes which only support {@code MethodInvocation}s.
|
||||
* <p>
|
||||
* One of the {@code invoke} methods should be called from the {@code around()} advice in
|
||||
* your aspect. Alternatively you can use one of the pre-defined aspects from the aspects
|
||||
* module.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Rob Winch
|
||||
* @since 3.0.3
|
||||
* @deprecated This class will be removed from the public API. Please either use
|
||||
* `spring-security-aspects`, Spring Security's method security support or create your own
|
||||
* class that uses Spring AOP annotations.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class AspectJMethodSecurityInterceptor extends MethodSecurityInterceptor {
|
||||
|
||||
/**
|
||||
* Method that is suitable for user with @Aspect notation.
|
||||
* @param jp The AspectJ joint point being invoked which requires a security decision
|
||||
* @return The returned value from the method invocation
|
||||
* @throws Throwable if the invocation throws one
|
||||
*/
|
||||
public Object invoke(JoinPoint jp) throws Throwable {
|
||||
return super.invoke(new MethodInvocationAdapter(jp));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method that is suitable for user with traditional AspectJ-code aspects.
|
||||
* @param jp The AspectJ joint point being invoked which requires a security decision
|
||||
* @param advisorProceed the advice-defined anonymous class that implements
|
||||
* {@code AspectJCallback} containing a simple {@code return proceed();} statement
|
||||
* @return The returned value from the method invocation
|
||||
*/
|
||||
public Object invoke(JoinPoint jp, AspectJCallback advisorProceed) {
|
||||
InterceptorStatusToken token = super.beforeInvocation(new MethodInvocationAdapter(jp));
|
||||
Object result;
|
||||
try {
|
||||
result = advisorProceed.proceedWithObject();
|
||||
}
|
||||
finally {
|
||||
super.finallyInvocation(token);
|
||||
}
|
||||
return super.afterInvocation(token, result);
|
||||
}
|
||||
|
||||
}
|
||||
-107
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.intercept.aspectj;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.reflect.CodeSignature;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Decorates a JoinPoint to allow it to be used with method-security infrastructure
|
||||
* classes which support {@code MethodInvocation} instances.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0.3
|
||||
* @deprecated This class will be removed from the public API. See
|
||||
* `JoinPointMethodInvocation` in `spring-security-aspects` for its replacement
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public final class MethodInvocationAdapter implements MethodInvocation {
|
||||
|
||||
private final ProceedingJoinPoint jp;
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Object target;
|
||||
|
||||
MethodInvocationAdapter(JoinPoint jp) {
|
||||
this.jp = (ProceedingJoinPoint) jp;
|
||||
if (jp.getTarget() != null) {
|
||||
this.target = jp.getTarget();
|
||||
}
|
||||
else {
|
||||
// SEC-1295: target may be null if an ITD is in use
|
||||
this.target = jp.getSignature().getDeclaringType();
|
||||
}
|
||||
String targetMethodName = jp.getStaticPart().getSignature().getName();
|
||||
Class<?>[] types = ((CodeSignature) jp.getStaticPart().getSignature()).getParameterTypes();
|
||||
Class<?> declaringType = jp.getStaticPart().getSignature().getDeclaringType();
|
||||
this.method = findMethod(targetMethodName, declaringType, types);
|
||||
Assert.notNull(this.method, () -> "Could not obtain target method from JoinPoint: '" + jp + "'");
|
||||
}
|
||||
|
||||
private Method findMethod(String name, Class<?> declaringType, Class<?>[] params) {
|
||||
Method method = null;
|
||||
try {
|
||||
method = declaringType.getMethod(name, params);
|
||||
}
|
||||
catch (NoSuchMethodException ignored) {
|
||||
}
|
||||
if (method == null) {
|
||||
try {
|
||||
method = declaringType.getDeclaredMethod(name, params);
|
||||
}
|
||||
catch (NoSuchMethodException ignored) {
|
||||
}
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getArguments() {
|
||||
return this.jp.getArgs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibleObject getStaticPart() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getThis() {
|
||||
return this.target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object proceed() throws Throwable {
|
||||
return this.jp.proceed();
|
||||
}
|
||||
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Enforces security for AspectJ <code>JointPoint</code>s, delegating secure object
|
||||
* callbacks to the calling aspect.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.access.intercept.aspectj;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract level security interception classes which are responsible for enforcing the
|
||||
* configured security constraints for a secure object.
|
||||
* <p>
|
||||
* A <i>secure object</i> is a term frequently used throughout the security system. It
|
||||
* does <b>not</b> refer to a business object that is being secured, but instead refers to
|
||||
* some infrastructure object that can have security facilities provided for it by Spring
|
||||
* Security. For example, one secure object would be <code>MethodInvocation</code>, whilst
|
||||
* another would be HTTP {@code org.springframework.security.web.FilterInvocation}. Note
|
||||
* these are infrastructure objects and their design allows them to represent a large
|
||||
* variety of actual resources that might need to be secured, such as business objects or
|
||||
* HTTP request URLs.
|
||||
* <p>
|
||||
* Each secure object typically has its own interceptor package. Each package usually
|
||||
* includes a concrete security interceptor (which subclasses
|
||||
* {@link org.springframework.security.access.intercept.AbstractSecurityInterceptor}) and
|
||||
* an appropriate {@link org.springframework.security.access.SecurityMetadataSource} for
|
||||
* the type of resources the secure object represents.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.access.intercept;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
-112
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.method;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link MethodSecurityMetadataSource} that supports both
|
||||
* Spring AOP and AspectJ and performs attribute resolution from: 1. specific target
|
||||
* method; 2. target class; 3. declaring method; 4. declaring class/interface. Use with
|
||||
* {@link DelegatingMethodSecurityMetadataSource} for caching support.
|
||||
* <p>
|
||||
* This class mimics the behaviour of Spring's
|
||||
* <tt>AbstractFallbackTransactionAttributeSource</tt> class.
|
||||
* <p>
|
||||
* Note that this class cannot extract security metadata where that metadata is expressed
|
||||
* by way of a target method/class (i.e. #1 and #2 above) AND the target method/class is
|
||||
* encapsulated in another proxy object. Spring Security does not walk a proxy chain to
|
||||
* locate the concrete/final target object. Consider making Spring Security your final
|
||||
* advisor (so it advises the final target, as opposed to another proxy), move the
|
||||
* metadata to declared methods or interfaces the proxy implements, or provide your own
|
||||
* replacement <tt>MethodSecurityMetadataSource</tt>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Luke taylor
|
||||
* @since 2.0
|
||||
* @deprecated Use the {@code use-authorization-manager} attribute for
|
||||
* {@code <method-security>} and {@code <intercept-methods>} instead or use
|
||||
* annotation-based or {@link AuthorizationManager}-based authorization
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractFallbackMethodSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {
|
||||
|
||||
@Override
|
||||
public Collection<ConfigAttribute> getAttributes(Method method, @Nullable Class<?> targetClass) {
|
||||
// The method may be on an interface, but we need attributes from the target
|
||||
// class.
|
||||
// If the target class is null, the method will be unchanged.
|
||||
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
|
||||
// First try is the method in the target class.
|
||||
Collection<ConfigAttribute> attr = findAttributes(specificMethod, targetClass);
|
||||
if (attr != null) {
|
||||
return attr;
|
||||
}
|
||||
// Second try is the config attribute on the target class.
|
||||
attr = findAttributes(specificMethod.getDeclaringClass());
|
||||
if (attr != null) {
|
||||
return attr;
|
||||
}
|
||||
if (specificMethod != method || targetClass == null) {
|
||||
// Fallback is to look at the original method.
|
||||
attr = findAttributes(method, method.getDeclaringClass());
|
||||
if (attr != null) {
|
||||
return attr;
|
||||
}
|
||||
// Last fallback is the class of the original method.
|
||||
return findAttributes(method.getDeclaringClass());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the security metadata applicable to the specified method invocation.
|
||||
*
|
||||
* <p>
|
||||
* Note that the {@link Method#getDeclaringClass()} may not equal the
|
||||
* <code>targetClass</code>. Both parameters are provided to assist subclasses which
|
||||
* may wish to provide advanced capabilities related to method metadata being
|
||||
* "registered" against a method even if the target class does not declare the method
|
||||
* (i.e. the subclass may only inherit the method).
|
||||
* @param method the method for the current invocation (never <code>null</code>)
|
||||
* @param targetClass the target class for the invocation (may be <code>null</code>)
|
||||
* @return the security metadata (or null if no metadata applies)
|
||||
*/
|
||||
protected abstract Collection<ConfigAttribute> findAttributes(Method method, @Nullable Class<?> targetClass);
|
||||
|
||||
/**
|
||||
* Obtains the security metadata registered against the specified class.
|
||||
*
|
||||
* <p>
|
||||
* Subclasses should only return metadata expressed at a class level. Subclasses
|
||||
* should NOT aggregate metadata for each method registered against a class, as the
|
||||
* abstract superclass will separate invoke {@link #findAttributes(Method, Class)} for
|
||||
* individual methods as appropriate.
|
||||
* @param clazz the target class for the invocation (never <code>null</code>)
|
||||
* @return the security metadata (or null if no metadata applies)
|
||||
*/
|
||||
protected abstract Collection<ConfigAttribute> findAttributes(Class<?> clazz);
|
||||
|
||||
}
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.method;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
|
||||
import org.springframework.aop.framework.AopProxyUtils;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
|
||||
/**
|
||||
* Abstract implementation of <tt>MethodSecurityMetadataSource</tt> which resolves the
|
||||
* secured object type to a MethodInvocation.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Luke Taylor
|
||||
* @deprecated Use the {@code use-authorization-manager} attribute for
|
||||
* {@code <method-security>} and {@code <intercept-methods>} instead or use
|
||||
* annotation-based or {@link AuthorizationManager}-based authorization
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public abstract class AbstractMethodSecurityMetadataSource implements MethodSecurityMetadataSource {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@Override
|
||||
public final Collection<ConfigAttribute> getAttributes(Object object) {
|
||||
if (object instanceof MethodInvocation mi) {
|
||||
Object target = mi.getThis();
|
||||
Class<?> targetClass = null;
|
||||
if (target != null) {
|
||||
targetClass = (target instanceof Class<?>) ? (Class<?>) target
|
||||
: AopProxyUtils.ultimateTargetClass(target);
|
||||
}
|
||||
Collection<ConfigAttribute> attrs = getAttributes(mi.getMethod(), targetClass);
|
||||
if (attrs != null && !attrs.isEmpty()) {
|
||||
return attrs;
|
||||
}
|
||||
if (target != null && !(target instanceof Class<?>)) {
|
||||
attrs = getAttributes(mi.getMethod(), target.getClass());
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
throw new IllegalArgumentException("Object must be a non-null MethodInvocation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean supports(Class<?> clazz) {
|
||||
return (MethodInvocation.class.isAssignableFrom(clazz));
|
||||
}
|
||||
|
||||
}
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.method;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Automatically tries a series of method definition sources, relying on the first source
|
||||
* of metadata that provides a non-null/non-empty response. Provides automatic caching of
|
||||
* the retrieved metadata.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Luke Taylor
|
||||
* @deprecated Use the {@code use-authorization-manager} attribute for
|
||||
* {@code <method-security>} and {@code <intercept-methods>} instead or use
|
||||
* annotation-based or {@link AuthorizationManager}-based authorization
|
||||
*/
|
||||
@Deprecated
|
||||
public final class DelegatingMethodSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {
|
||||
|
||||
private static final List<ConfigAttribute> NULL_CONFIG_ATTRIBUTE = Collections.emptyList();
|
||||
|
||||
private final List<MethodSecurityMetadataSource> methodSecurityMetadataSources;
|
||||
|
||||
private final Map<DefaultCacheKey, Collection<ConfigAttribute>> attributeCache = new HashMap<>();
|
||||
|
||||
public DelegatingMethodSecurityMetadataSource(List<MethodSecurityMetadataSource> methodSecurityMetadataSources) {
|
||||
Assert.notNull(methodSecurityMetadataSources, "MethodSecurityMetadataSources cannot be null");
|
||||
this.methodSecurityMetadataSources = methodSecurityMetadataSources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigAttribute> getAttributes(Method method, @Nullable Class<?> targetClass) {
|
||||
DefaultCacheKey cacheKey = new DefaultCacheKey(method, targetClass);
|
||||
synchronized (this.attributeCache) {
|
||||
Collection<ConfigAttribute> cached = this.attributeCache.get(cacheKey);
|
||||
// Check for canonical value indicating there is no config attribute,
|
||||
if (cached != null) {
|
||||
return cached;
|
||||
}
|
||||
// No cached value, so query the sources to find a result
|
||||
Collection<ConfigAttribute> attributes = null;
|
||||
for (MethodSecurityMetadataSource s : this.methodSecurityMetadataSources) {
|
||||
attributes = s.getAttributes(method, targetClass);
|
||||
if (attributes != null && !attributes.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Put it in the cache.
|
||||
if (attributes == null || attributes.isEmpty()) {
|
||||
this.attributeCache.put(cacheKey, NULL_CONFIG_ATTRIBUTE);
|
||||
return NULL_CONFIG_ATTRIBUTE;
|
||||
}
|
||||
this.logger.debug(LogMessage.format("Caching method [%s] with attributes %s", cacheKey, attributes));
|
||||
this.attributeCache.put(cacheKey, attributes);
|
||||
return attributes;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||
Set<ConfigAttribute> set = new HashSet<>();
|
||||
for (MethodSecurityMetadataSource s : this.methodSecurityMetadataSources) {
|
||||
Collection<ConfigAttribute> attrs = s.getAllConfigAttributes();
|
||||
if (attrs != null) {
|
||||
set.addAll(attrs);
|
||||
}
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
public List<MethodSecurityMetadataSource> getMethodSecurityMetadataSources() {
|
||||
return this.methodSecurityMetadataSources;
|
||||
}
|
||||
|
||||
private static class DefaultCacheKey {
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final @Nullable Class<?> targetClass;
|
||||
|
||||
DefaultCacheKey(Method method, @Nullable Class<?> targetClass) {
|
||||
this.method = method;
|
||||
this.targetClass = targetClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
DefaultCacheKey otherKey = (DefaultCacheKey) other;
|
||||
return (this.method.equals(otherKey.method)
|
||||
&& ObjectUtils.nullSafeEquals(this.targetClass, otherKey.targetClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.method.hashCode() * 21 + ((this.targetClass != null) ? this.targetClass.hashCode() : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String targetClassName = (this.targetClass != null) ? this.targetClass.getName() : "-";
|
||||
return "CacheKey[" + targetClassName + "; " + this.method + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-290
@@ -1,290 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.method;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Stores a list of <tt>ConfigAttribute</tt>s for a method or class signature.
|
||||
*
|
||||
* <p>
|
||||
* This class is the preferred implementation of {@link MethodSecurityMetadataSource} for
|
||||
* XML-based definition of method security metadata. To assist in XML-based definition,
|
||||
* wildcard support is provided.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @since 2.0
|
||||
* @deprecated Use the {@code use-authorization-manager} attribute for
|
||||
* {@code <method-security>} and {@code <intercept-methods>} instead or use
|
||||
* annotation-based or {@link AuthorizationManager}-based authorization
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public class MapBasedMethodSecurityMetadataSource extends AbstractFallbackMethodSecurityMetadataSource
|
||||
implements BeanClassLoaderAware {
|
||||
|
||||
@SuppressWarnings("NullAway")
|
||||
private @Nullable ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||
|
||||
/**
|
||||
* Map from RegisteredMethod to ConfigAttribute list
|
||||
*/
|
||||
protected final Map<RegisteredMethod, List<ConfigAttribute>> methodMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Map from RegisteredMethod to name pattern used for registration
|
||||
*/
|
||||
private final Map<RegisteredMethod, String> nameMap = new HashMap<>();
|
||||
|
||||
public MapBasedMethodSecurityMetadataSource() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the <tt>MapBasedMethodSecurityMetadataSource</tt> from a
|
||||
* @param methodMap map of method names to <tt>ConfigAttribute</tt>s.
|
||||
*/
|
||||
public MapBasedMethodSecurityMetadataSource(Map<String, List<ConfigAttribute>> methodMap) {
|
||||
for (Map.Entry<String, List<ConfigAttribute>> entry : methodMap.entrySet()) {
|
||||
addSecureMethod(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation does not support class-level attributes.
|
||||
*/
|
||||
@Override
|
||||
protected @Nullable Collection<ConfigAttribute> findAttributes(Class<?> clazz) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will walk the method inheritance tree to find the most specific declaration
|
||||
* applicable.
|
||||
*/
|
||||
@Override
|
||||
protected @Nullable Collection<ConfigAttribute> findAttributes(Method method, Class<?> targetClass) {
|
||||
if (targetClass == null) {
|
||||
return null;
|
||||
}
|
||||
return findAttributesSpecifiedAgainst(method, targetClass);
|
||||
}
|
||||
|
||||
private @Nullable List<ConfigAttribute> findAttributesSpecifiedAgainst(Method method, Class<?> clazz) {
|
||||
RegisteredMethod registeredMethod = new RegisteredMethod(method, clazz);
|
||||
if (this.methodMap.containsKey(registeredMethod)) {
|
||||
return this.methodMap.get(registeredMethod);
|
||||
}
|
||||
// Search superclass
|
||||
if (clazz.getSuperclass() != null) {
|
||||
return findAttributesSpecifiedAgainst(method, clazz.getSuperclass());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add configuration attributes for a secure method. Method names can end or start
|
||||
* with <code>*</code> for matching multiple methods.
|
||||
* @param name type and method name, separated by a dot
|
||||
* @param attr the security attributes associated with the method
|
||||
*/
|
||||
private void addSecureMethod(String name, List<ConfigAttribute> attr) {
|
||||
int lastDotIndex = name.lastIndexOf(".");
|
||||
Assert.isTrue(lastDotIndex != -1, () -> "'" + name + "' is not a valid method name: format is FQN.methodName");
|
||||
String methodName = name.substring(lastDotIndex + 1);
|
||||
Assert.hasText(methodName, () -> "Method not found for '" + name + "'");
|
||||
String typeName = name.substring(0, lastDotIndex);
|
||||
Class<?> type = ClassUtils.resolveClassName(typeName, this.beanClassLoader);
|
||||
addSecureMethod(type, methodName, attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add configuration attributes for a secure method. Mapped method names can end or
|
||||
* start with <code>*</code> for matching multiple methods.
|
||||
* @param javaType target interface or class the security configuration attribute
|
||||
* applies to
|
||||
* @param mappedName mapped method name, which the javaType has declared or inherited
|
||||
* @param attr required authorities associated with the method
|
||||
*/
|
||||
public void addSecureMethod(Class<?> javaType, String mappedName, List<ConfigAttribute> attr) {
|
||||
String name = javaType.getName() + '.' + mappedName;
|
||||
this.logger.debug(LogMessage.format("Request to add secure method [%s] with attributes [%s]", name, attr));
|
||||
Method[] methods = javaType.getMethods();
|
||||
List<Method> matchingMethods = new ArrayList<>();
|
||||
for (Method method : methods) {
|
||||
if (method.getName().equals(mappedName) || isMatch(method.getName(), mappedName)) {
|
||||
matchingMethods.add(method);
|
||||
}
|
||||
}
|
||||
Assert.notEmpty(matchingMethods, () -> "Couldn't find method '" + mappedName + "' on '" + javaType + "'");
|
||||
registerAllMatchingMethods(javaType, attr, name, matchingMethods);
|
||||
}
|
||||
|
||||
private void registerAllMatchingMethods(Class<?> javaType, List<ConfigAttribute> attr, String name,
|
||||
List<Method> matchingMethods) {
|
||||
for (Method method : matchingMethods) {
|
||||
RegisteredMethod registeredMethod = new RegisteredMethod(method, javaType);
|
||||
String regMethodName = this.nameMap.get(registeredMethod);
|
||||
if ((regMethodName == null) || (!regMethodName.equals(name) && (regMethodName.length() <= name.length()))) {
|
||||
// no already registered method name, or more specific
|
||||
// method name specification (now) -> (re-)register method
|
||||
if (regMethodName != null) {
|
||||
this.logger.debug(LogMessage.format(
|
||||
"Replacing attributes for secure method [%s]: current name [%s] is more specific than [%s]",
|
||||
method, name, regMethodName));
|
||||
}
|
||||
this.nameMap.put(registeredMethod, name);
|
||||
addSecureMethod(registeredMethod, attr);
|
||||
}
|
||||
else {
|
||||
this.logger.debug(LogMessage.format(
|
||||
"Keeping attributes for secure method [%s]: current name [%s] is not more specific than [%s]",
|
||||
method, name, regMethodName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds configuration attributes for a specific method, for example where the method
|
||||
* has been matched using a pointcut expression. If a match already exists in the map
|
||||
* for the method, then the existing match will be retained, so that if this method is
|
||||
* called for a more general pointcut it will not override a more specific one which
|
||||
* has already been added.
|
||||
* <p>
|
||||
* This method should only be called during initialization of the {@code BeanFactory}.
|
||||
*/
|
||||
public void addSecureMethod(Class<?> javaType, Method method, List<ConfigAttribute> attr) {
|
||||
RegisteredMethod key = new RegisteredMethod(method, javaType);
|
||||
if (this.methodMap.containsKey(key)) {
|
||||
this.logger.debug(LogMessage.format("Method [%s] is already registered with attributes [%s]", method,
|
||||
this.methodMap.get(key)));
|
||||
return;
|
||||
}
|
||||
this.methodMap.put(key, attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add configuration attributes for a secure method.
|
||||
* @param method the method to be secured
|
||||
* @param attr required authorities associated with the method
|
||||
*/
|
||||
private void addSecureMethod(RegisteredMethod method, List<ConfigAttribute> attr) {
|
||||
Assert.notNull(method, "RegisteredMethod required");
|
||||
Assert.notNull(attr, "Configuration attribute required");
|
||||
this.logger.info(LogMessage.format("Adding secure method [%s] with attributes [%s]", method, attr));
|
||||
this.methodMap.put(method, attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the configuration attributes explicitly defined against this bean.
|
||||
* @return the attributes explicitly defined against this bean
|
||||
*/
|
||||
@Override
|
||||
public Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||
Set<ConfigAttribute> allAttributes = new HashSet<>();
|
||||
this.methodMap.values().forEach(allAttributes::addAll);
|
||||
return allAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the given method name matches the mapped name. The default implementation
|
||||
* checks for "xxx" and "xxx" matches.
|
||||
* @param methodName the method name of the class
|
||||
* @param mappedName the name in the descriptor
|
||||
* @return if the names match
|
||||
*/
|
||||
private boolean isMatch(String methodName, String mappedName) {
|
||||
return (mappedName.endsWith("*") && methodName.startsWith(mappedName.substring(0, mappedName.length() - 1)))
|
||||
|| (mappedName.startsWith("*") && methodName.endsWith(mappedName.substring(1, mappedName.length())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader beanClassLoader) {
|
||||
Assert.notNull(beanClassLoader, "Bean class loader required");
|
||||
this.beanClassLoader = beanClassLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return map size (for unit tests and diagnostics)
|
||||
*/
|
||||
public int getMethodMapSize() {
|
||||
return this.methodMap.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores both the Java Method as well as the Class we obtained the Method from. This
|
||||
* is necessary because Method only provides us access to the declaring class. It
|
||||
* doesn't provide a way for us to introspect which Class the Method was registered
|
||||
* against. If a given Class inherits and redeclares a method (i.e. calls super();)
|
||||
* the registered Class and declaring Class are the same. If a given class merely
|
||||
* inherits but does not redeclare a method, the registered Class will be the Class
|
||||
* we're invoking against and the Method will provide details of the declared class.
|
||||
*/
|
||||
private static class RegisteredMethod {
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Class<?> registeredJavaType;
|
||||
|
||||
RegisteredMethod(Method method, Class<?> registeredJavaType) {
|
||||
Assert.notNull(method, "Method required");
|
||||
Assert.notNull(registeredJavaType, "Registered Java Type required");
|
||||
this.method = method;
|
||||
this.registeredJavaType = registeredJavaType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof RegisteredMethod rhs) {
|
||||
return this.method.equals(rhs.method) && this.registeredJavaType.equals(rhs.registeredJavaType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.method.hashCode() * this.registeredJavaType.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RegisteredMethod[" + this.registeredJavaType.getName() + "; " + this.method + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.method;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.SecurityMetadataSource;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
|
||||
/**
|
||||
* Interface for <code>SecurityMetadataSource</code> implementations that are designed to
|
||||
* perform lookups keyed on <code>Method</code>s.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @see org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager
|
||||
* @see org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager
|
||||
* @deprecated Use the {@code use-authorization-manager} attribute for
|
||||
* {@code <method-security>} and {@code <intercept-methods>} instead or use
|
||||
* annotation-based or {@link AuthorizationManager}-based authorization
|
||||
*/
|
||||
@Deprecated
|
||||
public interface MethodSecurityMetadataSource extends SecurityMetadataSource {
|
||||
|
||||
Collection<ConfigAttribute> getAttributes(Method method, @Nullable Class<?> targetClass);
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.method;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.security.core.parameters.AnnotationParameterNameDiscoverer;
|
||||
|
||||
/**
|
||||
* An annotation that can be used along with {@link AnnotationParameterNameDiscoverer} to
|
||||
* specify parameter names. This is useful for interfaces prior to JDK 8 which cannot
|
||||
* contain the parameter names.
|
||||
*
|
||||
* @see AnnotationParameterNameDiscoverer
|
||||
* @author Rob Winch
|
||||
* @since 3.2
|
||||
* @deprecated use @{code org.springframework.security.core.parameters.P}
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Deprecated
|
||||
public @interface P {
|
||||
|
||||
/**
|
||||
* The parameter name
|
||||
* @return
|
||||
*/
|
||||
String value();
|
||||
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides {@code SecurityMetadataSource} implementations for securing Java method
|
||||
* invocations via different AOP libraries.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.access.method;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
-87
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.prepost;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.AfterInvocationProvider;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* <tt>AfterInvocationProvider</tt> which delegates to a
|
||||
* {@link PostInvocationAuthorizationAdvice} instance passing it the
|
||||
* <tt>PostInvocationAttribute</tt> created from @PostAuthorize and @PostFilter
|
||||
* annotations.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Alexander Furer
|
||||
* @since 3.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}
|
||||
* instead
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public class PostInvocationAdviceProvider implements AfterInvocationProvider {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final PostInvocationAuthorizationAdvice postAdvice;
|
||||
|
||||
public PostInvocationAdviceProvider(PostInvocationAuthorizationAdvice postAdvice) {
|
||||
this.postAdvice = postAdvice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> config,
|
||||
Object returnedObject) throws AccessDeniedException {
|
||||
PostInvocationAttribute postInvocationAttribute = findPostInvocationAttribute(config);
|
||||
if (postInvocationAttribute == null) {
|
||||
return returnedObject;
|
||||
}
|
||||
return this.postAdvice.after(authentication, (MethodInvocation) object, postInvocationAttribute,
|
||||
returnedObject);
|
||||
}
|
||||
|
||||
private @Nullable PostInvocationAttribute findPostInvocationAttribute(Collection<ConfigAttribute> config) {
|
||||
for (ConfigAttribute attribute : config) {
|
||||
if (attribute instanceof PostInvocationAttribute) {
|
||||
return (PostInvocationAttribute) attribute;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return attribute instanceof PostInvocationAttribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return MethodInvocation.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.prepost;
|
||||
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
|
||||
/**
|
||||
* Marker interface for attributes which are created from combined @PostFilter
|
||||
* and @PostAuthorize annotations.
|
||||
* <p>
|
||||
* Consumed by a {@link PostInvocationAuthorizationAdvice}.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public interface PostInvocationAttribute extends ConfigAttribute {
|
||||
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.prepost;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* Performs filtering and authorization logic after a method is invoked.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public interface PostInvocationAuthorizationAdvice extends AopInfrastructureBean {
|
||||
|
||||
Object after(Authentication authentication, MethodInvocation mi, PostInvocationAttribute pia, Object returnedObject)
|
||||
throws AccessDeniedException;
|
||||
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.prepost;
|
||||
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
|
||||
/**
|
||||
* Marker interface for attributes which are created from combined @PreFilter
|
||||
* and @PreAuthorize annotations.
|
||||
* <p>
|
||||
* Consumed by a {@link PreInvocationAuthorizationAdvice}.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public interface PreInvocationAttribute extends ConfigAttribute {
|
||||
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.prepost;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* Performs argument filtering and authorization logic before a method is invoked.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public interface PreInvocationAuthorizationAdvice extends AopInfrastructureBean {
|
||||
|
||||
/**
|
||||
* The "before" advice which should be executed to perform any filtering necessary and
|
||||
* to decide whether the method call is authorised.
|
||||
* @param authentication the information on the principal on whose account the
|
||||
* decision should be made
|
||||
* @param mi the method invocation being attempted
|
||||
* @param preInvocationAttribute the attribute built from the @PreFilter
|
||||
* and @PostFilter annotations.
|
||||
* @return true if authorised, false otherwise
|
||||
*/
|
||||
boolean before(Authentication authentication, MethodInvocation mi, PreInvocationAttribute preInvocationAttribute);
|
||||
|
||||
}
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.prepost;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* Voter which performs the actions using a PreInvocationAuthorizationAdvice
|
||||
* implementation generated from @PreFilter and @PreAuthorize annotations.
|
||||
* <p>
|
||||
* In practice, if these annotations are being used, they will normally contain all the
|
||||
* necessary access control logic, so a voter-based system is not really necessary and a
|
||||
* single <tt>AccessDecisionManager</tt> which contained the same logic would suffice.
|
||||
* However, this class fits in readily with the traditional voter-based
|
||||
* <tt>AccessDecisionManager</tt> implementations used by Spring Security.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor}
|
||||
* instead
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public class PreInvocationAuthorizationAdviceVoter implements AccessDecisionVoter<MethodInvocation> {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private final PreInvocationAuthorizationAdvice preAdvice;
|
||||
|
||||
public PreInvocationAuthorizationAdviceVoter(PreInvocationAuthorizationAdvice pre) {
|
||||
this.preAdvice = pre;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return attribute instanceof PreInvocationAttribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return MethodInvocation.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int vote(Authentication authentication, MethodInvocation method, Collection<ConfigAttribute> attributes) {
|
||||
// Find prefilter and preauth (or combined) attributes
|
||||
// if both null, abstain else call advice with them
|
||||
PreInvocationAttribute preAttr = findPreInvocationAttribute(attributes);
|
||||
if (preAttr == null) {
|
||||
// No expression based metadata, so abstain
|
||||
return ACCESS_ABSTAIN;
|
||||
}
|
||||
return this.preAdvice.before(authentication, method, preAttr) ? ACCESS_GRANTED : ACCESS_DENIED;
|
||||
}
|
||||
|
||||
private @Nullable PreInvocationAttribute findPreInvocationAttribute(Collection<ConfigAttribute> config) {
|
||||
for (ConfigAttribute attribute : config) {
|
||||
if (attribute instanceof PreInvocationAttribute) {
|
||||
return (PreInvocationAttribute) attribute;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
-195
@@ -1,195 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.prepost;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
|
||||
import kotlinx.coroutines.reactive.ReactiveFlowKt;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.Exceptions;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.core.KotlinDetector;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.method.MethodSecurityMetadataSource;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A {@link MethodInterceptor} that supports {@link PreAuthorize} and
|
||||
* {@link PostAuthorize} for methods that return {@link Mono} or {@link Flux} and Kotlin
|
||||
* coroutine functions.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Eleftheria Stein
|
||||
* @since 5.0
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeReactiveMethodInterceptor}
|
||||
* or
|
||||
* {@link org.springframework.security.authorization.method.AuthorizationManagerAfterReactiveMethodInterceptor}
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public class PrePostAdviceReactiveMethodInterceptor implements MethodInterceptor {
|
||||
|
||||
private Authentication anonymous = new AnonymousAuthenticationToken("key", "anonymous",
|
||||
AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
|
||||
|
||||
private final MethodSecurityMetadataSource attributeSource;
|
||||
|
||||
private final PreInvocationAuthorizationAdvice preInvocationAdvice;
|
||||
|
||||
private final PostInvocationAuthorizationAdvice postAdvice;
|
||||
|
||||
private static final String COROUTINES_FLOW_CLASS_NAME = "kotlinx.coroutines.flow.Flow";
|
||||
|
||||
private static final int RETURN_TYPE_METHOD_PARAMETER_INDEX = -1;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
* @param attributeSource the {@link MethodSecurityMetadataSource} to use
|
||||
* @param preInvocationAdvice the {@link PreInvocationAuthorizationAdvice} to use
|
||||
* @param postInvocationAdvice the {@link PostInvocationAuthorizationAdvice} to use
|
||||
*/
|
||||
public PrePostAdviceReactiveMethodInterceptor(MethodSecurityMetadataSource attributeSource,
|
||||
PreInvocationAuthorizationAdvice preInvocationAdvice,
|
||||
PostInvocationAuthorizationAdvice postInvocationAdvice) {
|
||||
Assert.notNull(attributeSource, "attributeSource cannot be null");
|
||||
Assert.notNull(preInvocationAdvice, "preInvocationAdvice cannot be null");
|
||||
Assert.notNull(postInvocationAdvice, "postInvocationAdvice cannot be null");
|
||||
this.attributeSource = attributeSource;
|
||||
this.preInvocationAdvice = preInvocationAdvice;
|
||||
this.postAdvice = postInvocationAdvice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(final MethodInvocation invocation) {
|
||||
Method method = invocation.getMethod();
|
||||
Class<?> returnType = method.getReturnType();
|
||||
|
||||
boolean isSuspendingFunction = KotlinDetector.isSuspendingFunction(method);
|
||||
boolean hasFlowReturnType = COROUTINES_FLOW_CLASS_NAME
|
||||
.equals(new MethodParameter(method, RETURN_TYPE_METHOD_PARAMETER_INDEX).getParameterType().getName());
|
||||
boolean hasReactiveReturnType = Publisher.class.isAssignableFrom(returnType) || isSuspendingFunction
|
||||
|| hasFlowReturnType;
|
||||
|
||||
Assert.state(hasReactiveReturnType,
|
||||
() -> "The returnType " + returnType + " on " + method
|
||||
+ " must return an instance of org.reactivestreams.Publisher "
|
||||
+ "(i.e. Mono / Flux) or the function must be a Kotlin coroutine "
|
||||
+ "function in order to support Reactor Context");
|
||||
Class<?> targetClass = invocation.getThis().getClass();
|
||||
Collection<ConfigAttribute> attributes = this.attributeSource.getAttributes(method, targetClass);
|
||||
PreInvocationAttribute preAttr = findPreInvocationAttribute(attributes);
|
||||
// @formatter:off
|
||||
Mono<Authentication> toInvoke = ReactiveSecurityContextHolder.getContext()
|
||||
.map(SecurityContext::getAuthentication)
|
||||
.defaultIfEmpty(this.anonymous)
|
||||
.filter((auth) -> this.preInvocationAdvice.before(auth, invocation, preAttr))
|
||||
.switchIfEmpty(Mono.defer(() -> Mono.error(new AccessDeniedException("Denied"))));
|
||||
// @formatter:on
|
||||
PostInvocationAttribute attr = findPostInvocationAttribute(attributes);
|
||||
if (Mono.class.isAssignableFrom(returnType)) {
|
||||
return toInvoke.flatMap((auth) -> PrePostAdviceReactiveMethodInterceptor.<Mono<?>>proceed(invocation)
|
||||
.map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r));
|
||||
}
|
||||
if (Flux.class.isAssignableFrom(returnType)) {
|
||||
return toInvoke.flatMapMany((auth) -> PrePostAdviceReactiveMethodInterceptor.<Flux<?>>proceed(invocation)
|
||||
.map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r));
|
||||
}
|
||||
if (hasFlowReturnType) {
|
||||
if (isSuspendingFunction) {
|
||||
return toInvoke
|
||||
.flatMapMany((auth) -> Flux.from(PrePostAdviceReactiveMethodInterceptor.proceed(invocation))
|
||||
.map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r));
|
||||
}
|
||||
else {
|
||||
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(returnType);
|
||||
Assert.state(adapter != null, () -> "The returnType " + returnType + " on " + method
|
||||
+ " must have a org.springframework.core.ReactiveAdapter registered");
|
||||
Flux<?> response = toInvoke.flatMapMany((auth) -> Flux
|
||||
.from(adapter.toPublisher(PrePostAdviceReactiveMethodInterceptor.flowProceed(invocation)))
|
||||
.map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r));
|
||||
return KotlinDelegate.asFlow(response);
|
||||
}
|
||||
}
|
||||
return toInvoke.flatMap((auth) -> Mono.from(PrePostAdviceReactiveMethodInterceptor.proceed(invocation))
|
||||
.map((r) -> (attr != null) ? this.postAdvice.after(auth, invocation, attr, r) : r));
|
||||
}
|
||||
|
||||
private static <T extends Publisher<?>> @Nullable T proceed(final MethodInvocation invocation) {
|
||||
try {
|
||||
return (T) invocation.proceed();
|
||||
}
|
||||
catch (Throwable throwable) {
|
||||
throw Exceptions.propagate(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable Object flowProceed(final MethodInvocation invocation) {
|
||||
try {
|
||||
return invocation.proceed();
|
||||
}
|
||||
catch (Throwable throwable) {
|
||||
throw Exceptions.propagate(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable PostInvocationAttribute findPostInvocationAttribute(Collection<ConfigAttribute> config) {
|
||||
for (ConfigAttribute attribute : config) {
|
||||
if (attribute instanceof PostInvocationAttribute) {
|
||||
return (PostInvocationAttribute) attribute;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static @Nullable PreInvocationAttribute findPreInvocationAttribute(Collection<ConfigAttribute> config) {
|
||||
for (ConfigAttribute attribute : config) {
|
||||
if (attribute instanceof PreInvocationAttribute) {
|
||||
return (PreInvocationAttribute) attribute;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class to avoid a hard dependency on Kotlin at runtime.
|
||||
*/
|
||||
private static class KotlinDelegate {
|
||||
|
||||
private static Object asFlow(Publisher<?> publisher) {
|
||||
return ReactiveFlowKt.asFlow(publisher);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-145
@@ -1,145 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.prepost;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.method.AbstractMethodSecurityMetadataSource;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* <tt>MethodSecurityMetadataSource</tt> which extracts metadata from the @PreFilter
|
||||
* and @PreAuthorize annotations placed on a method. This class is merely responsible for
|
||||
* locating the relevant annotations (if any). It delegates the actual
|
||||
* <tt>ConfigAttribute</tt> creation to its {@link PrePostInvocationAttributeFactory},
|
||||
* thus decoupling itself from the mechanism which will enforce the annotations'
|
||||
* behaviour.
|
||||
* <p>
|
||||
* Annotations may be specified on classes or methods, and method-specific annotations
|
||||
* will take precedence. If you use any annotation and do not specify a pre-authorization
|
||||
* condition, then the method will be allowed as if a @PreAuthorize("permitAll") were
|
||||
* present.
|
||||
* <p>
|
||||
* Since we are handling multiple annotations here, it's possible that we may have to
|
||||
* combine annotations defined in multiple locations for a single method - they may be
|
||||
* defined on the method itself, or at interface or class level.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
* @see PreInvocationAuthorizationAdviceVoter
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager}
|
||||
* and
|
||||
* {@link org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager}
|
||||
* instead
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public class PrePostAnnotationSecurityMetadataSource extends AbstractMethodSecurityMetadataSource {
|
||||
|
||||
private final PrePostInvocationAttributeFactory attributeFactory;
|
||||
|
||||
public PrePostAnnotationSecurityMetadataSource(PrePostInvocationAttributeFactory attributeFactory) {
|
||||
this.attributeFactory = attributeFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ConfigAttribute> getAttributes(Method method, Class<?> targetClass) {
|
||||
if (method.getDeclaringClass() == Object.class) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
PreFilter preFilter = findAnnotation(method, targetClass, PreFilter.class);
|
||||
PreAuthorize preAuthorize = findAnnotation(method, targetClass, PreAuthorize.class);
|
||||
PostFilter postFilter = findAnnotation(method, targetClass, PostFilter.class);
|
||||
// TODO: Can we check for void methods and throw an exception here?
|
||||
PostAuthorize postAuthorize = findAnnotation(method, targetClass, PostAuthorize.class);
|
||||
if (preFilter == null && preAuthorize == null && postFilter == null && postAuthorize == null) {
|
||||
// There is no meta-data so return
|
||||
return Collections.emptyList();
|
||||
}
|
||||
String preFilterAttribute = (preFilter != null) ? preFilter.value() : null;
|
||||
String filterObject = (preFilter != null) ? preFilter.filterTarget() : null;
|
||||
String preAuthorizeAttribute = (preAuthorize != null) ? preAuthorize.value() : null;
|
||||
String postFilterAttribute = (postFilter != null) ? postFilter.value() : null;
|
||||
String postAuthorizeAttribute = (postAuthorize != null) ? postAuthorize.value() : null;
|
||||
ArrayList<ConfigAttribute> attrs = new ArrayList<>(2);
|
||||
PreInvocationAttribute pre = this.attributeFactory.createPreInvocationAttribute(preFilterAttribute,
|
||||
filterObject, preAuthorizeAttribute);
|
||||
if (pre != null) {
|
||||
attrs.add(pre);
|
||||
}
|
||||
PostInvocationAttribute post = this.attributeFactory.createPostInvocationAttribute(postFilterAttribute,
|
||||
postAuthorizeAttribute);
|
||||
if (post != null) {
|
||||
attrs.add(post);
|
||||
}
|
||||
attrs.trimToSize();
|
||||
return attrs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* See
|
||||
* {@link org.springframework.security.access.method.AbstractFallbackMethodSecurityMetadataSource#getAttributes(Method, Class)}
|
||||
* for the logic of this method. The ordering here is slightly different in that we
|
||||
* consider method-specific annotations on an interface before class-level ones.
|
||||
*/
|
||||
private <A extends Annotation> @Nullable A findAnnotation(Method method, Class<?> targetClass,
|
||||
Class<A> annotationClass) {
|
||||
// The method may be on an interface, but we need attributes from the target
|
||||
// class.
|
||||
// If the target class is null, the method will be unchanged.
|
||||
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
|
||||
A annotation = AnnotationUtils.findAnnotation(specificMethod, annotationClass);
|
||||
if (annotation != null) {
|
||||
this.logger.debug(LogMessage.format("%s found on specific method: %s", annotation, specificMethod));
|
||||
return annotation;
|
||||
}
|
||||
// Check the original (e.g. interface) method
|
||||
if (specificMethod != method) {
|
||||
annotation = AnnotationUtils.findAnnotation(method, annotationClass);
|
||||
if (annotation != null) {
|
||||
this.logger.debug(LogMessage.format("%s found on: %s", annotation, method));
|
||||
return annotation;
|
||||
}
|
||||
}
|
||||
// Check the class-level (note declaringClass, not targetClass, which may not
|
||||
// actually implement the method)
|
||||
annotation = AnnotationUtils.findAnnotation(specificMethod.getDeclaringClass(), annotationClass);
|
||||
if (annotation != null) {
|
||||
this.logger
|
||||
.debug(LogMessage.format("%s found on: %s", annotation, specificMethod.getDeclaringClass().getName()));
|
||||
return annotation;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.prepost;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
* @see org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor
|
||||
* @see org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor
|
||||
* @deprecated Use delegation with {@link AuthorizationManager}
|
||||
*/
|
||||
@Deprecated
|
||||
public interface PrePostInvocationAttributeFactory extends AopInfrastructureBean {
|
||||
|
||||
PreInvocationAttribute createPreInvocationAttribute(@Nullable String preFilterAttribute,
|
||||
@Nullable String filterObject, @Nullable String preAuthorizeAttribute);
|
||||
|
||||
PostInvocationAttribute createPostInvocationAttribute(@Nullable String postFilterAttribute,
|
||||
@Nullable String postAuthorizeAttribute);
|
||||
|
||||
}
|
||||
-128
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.vote;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.security.access.AccessDecisionManager;
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract implementation of {@link AccessDecisionManager}.
|
||||
*
|
||||
* <p>
|
||||
* Handles configuration of a bean context defined list of {@link AccessDecisionVoter}s
|
||||
* and the access control behaviour if all voters abstain from voting (defaults to deny
|
||||
* access).
|
||||
*
|
||||
* @deprecated Use {@link AuthorizationManager} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractAccessDecisionManager
|
||||
implements AccessDecisionManager, InitializingBean, MessageSourceAware {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private List<AccessDecisionVoter<?>> decisionVoters;
|
||||
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
|
||||
private boolean allowIfAllAbstainDecisions = false;
|
||||
|
||||
protected AbstractAccessDecisionManager(List<AccessDecisionVoter<?>> decisionVoters) {
|
||||
Assert.notEmpty(decisionVoters, "A list of AccessDecisionVoters is required");
|
||||
this.decisionVoters = decisionVoters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notEmpty(this.decisionVoters, "A list of AccessDecisionVoters is required");
|
||||
Assert.notNull(this.messages, "A message source must be set");
|
||||
}
|
||||
|
||||
protected final void checkAllowIfAllAbstainDecisions() {
|
||||
if (!this.isAllowIfAllAbstainDecisions()) {
|
||||
throw new AccessDeniedException(
|
||||
this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
|
||||
}
|
||||
}
|
||||
|
||||
public List<AccessDecisionVoter<?>> getDecisionVoters() {
|
||||
return this.decisionVoters;
|
||||
}
|
||||
|
||||
public boolean isAllowIfAllAbstainDecisions() {
|
||||
return this.allowIfAllAbstainDecisions;
|
||||
}
|
||||
|
||||
public void setAllowIfAllAbstainDecisions(boolean allowIfAllAbstainDecisions) {
|
||||
this.allowIfAllAbstainDecisions = allowIfAllAbstainDecisions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageSource(MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
for (AccessDecisionVoter<?> voter : this.decisionVoters) {
|
||||
if (voter.supports(attribute)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through all <code>AccessDecisionVoter</code>s and ensures each can support
|
||||
* the presented class.
|
||||
* <p>
|
||||
* If one or more voters cannot support the presented class, <code>false</code> is
|
||||
* returned.
|
||||
* @param clazz the type of secured object being presented
|
||||
* @return true if this type is supported
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
for (AccessDecisionVoter<?> voter : this.decisionVoters) {
|
||||
if (!voter.supports(clazz)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.getClass().getSimpleName() + " [DecisionVoters=" + this.decisionVoters
|
||||
+ ", AllowIfAllAbstainDecisions=" + this.allowIfAllAbstainDecisions + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.vote;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.AuthorizationServiceException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Provides helper methods for writing domain object ACL voters. Not bound to any
|
||||
* particular ACL system.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated Now used by only-deprecated classes. Generally speaking, in-memory ACL is
|
||||
* no longer advised, so no replacement is planned at this point.
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public abstract class AbstractAclVoter implements AccessDecisionVoter<MethodInvocation> {
|
||||
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
private @Nullable Class<?> processDomainObjectClass;
|
||||
|
||||
protected Object getDomainObjectInstance(MethodInvocation invocation) {
|
||||
Object[] args = invocation.getArguments();
|
||||
Class<?>[] params = invocation.getMethod().getParameterTypes();
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
if (this.processDomainObjectClass.isAssignableFrom(params[i])) {
|
||||
return args[i];
|
||||
}
|
||||
}
|
||||
throw new AuthorizationServiceException("MethodInvocation: " + invocation
|
||||
+ " did not provide any argument of type: " + this.processDomainObjectClass);
|
||||
}
|
||||
|
||||
public Class<?> getProcessDomainObjectClass() {
|
||||
return this.processDomainObjectClass;
|
||||
}
|
||||
|
||||
public void setProcessDomainObjectClass(Class<?> processDomainObjectClass) {
|
||||
Assert.notNull(processDomainObjectClass, "processDomainObjectClass cannot be set to null");
|
||||
this.processDomainObjectClass = processDomainObjectClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation supports only <code>MethodSecurityInterceptor</code>, because
|
||||
* it queries the presented <code>MethodInvocation</code>.
|
||||
* @param clazz the secure object
|
||||
* @return <code>true</code> if the secure object is <code>MethodInvocation</code>,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return (MethodInvocation.class.isAssignableFrom(clazz));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.vote;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* Simple concrete implementation of
|
||||
* {@link org.springframework.security.access.AccessDecisionManager} that grants access if
|
||||
* any <code>AccessDecisionVoter</code> returns an affirmative response.
|
||||
*
|
||||
* @deprecated Use {@link AuthorizationManager} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class AffirmativeBased extends AbstractAccessDecisionManager {
|
||||
|
||||
public AffirmativeBased(List<AccessDecisionVoter<?>> decisionVoters) {
|
||||
super(decisionVoters);
|
||||
}
|
||||
|
||||
/**
|
||||
* This concrete implementation simply polls all configured
|
||||
* {@link AccessDecisionVoter}s and grants access if any
|
||||
* <code>AccessDecisionVoter</code> voted affirmatively. Denies access only if there
|
||||
* was a deny vote AND no affirmative votes.
|
||||
* <p>
|
||||
* If every <code>AccessDecisionVoter</code> abstained from voting, the decision will
|
||||
* be based on the {@link #isAllowIfAllAbstainDecisions()} property (defaults to
|
||||
* false).
|
||||
* </p>
|
||||
* @param authentication the caller invoking the method
|
||||
* @param object the secured object
|
||||
* @param configAttributes the configuration attributes associated with the method
|
||||
* being invoked
|
||||
* @throws AccessDeniedException if access is denied
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
|
||||
throws AccessDeniedException {
|
||||
int deny = 0;
|
||||
for (AccessDecisionVoter voter : getDecisionVoters()) {
|
||||
int result = voter.vote(authentication, object, configAttributes);
|
||||
switch (result) {
|
||||
case AccessDecisionVoter.ACCESS_GRANTED:
|
||||
return;
|
||||
case AccessDecisionVoter.ACCESS_DENIED:
|
||||
deny++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (deny > 0) {
|
||||
throw new AccessDeniedException(
|
||||
this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
|
||||
}
|
||||
// To get this far, every AccessDecisionVoter abstained
|
||||
checkAllowIfAllAbstainDecisions();
|
||||
}
|
||||
|
||||
}
|
||||
-119
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.vote;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Votes if a {@link ConfigAttribute#getAttribute()} of
|
||||
* <code>IS_AUTHENTICATED_FULLY</code> or <code>IS_AUTHENTICATED_REMEMBERED</code> or
|
||||
* <code>IS_AUTHENTICATED_ANONYMOUSLY</code> is present. This list is in order of most
|
||||
* strict checking to least strict checking.
|
||||
* <p>
|
||||
* The current <code>Authentication</code> will be inspected to determine if the principal
|
||||
* has a particular level of authentication. The "FULLY" authenticated option means the
|
||||
* user is authenticated fully (i.e.
|
||||
* {@link org.springframework.security.authentication.AuthenticationTrustResolver#isAnonymous(Authentication)}
|
||||
* is false and
|
||||
* {@link org.springframework.security.authentication.AuthenticationTrustResolver#isRememberMe(Authentication)}
|
||||
* is false). The "REMEMBERED" will grant access if the principal was either authenticated
|
||||
* via remember-me OR is fully authenticated. The "ANONYMOUSLY" will grant access if the
|
||||
* principal was authenticated via remember-me, OR anonymously, OR via full
|
||||
* authentication.
|
||||
* <p>
|
||||
* All comparisons and prefixes are case sensitive.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.AuthorityAuthorizationManager}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class AuthenticatedVoter implements AccessDecisionVoter<Object> {
|
||||
|
||||
public static final String IS_AUTHENTICATED_FULLY = "IS_AUTHENTICATED_FULLY";
|
||||
|
||||
public static final String IS_AUTHENTICATED_REMEMBERED = "IS_AUTHENTICATED_REMEMBERED";
|
||||
|
||||
public static final String IS_AUTHENTICATED_ANONYMOUSLY = "IS_AUTHENTICATED_ANONYMOUSLY";
|
||||
|
||||
private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
|
||||
|
||||
private boolean isFullyAuthenticated(Authentication authentication) {
|
||||
return this.authenticationTrustResolver.isFullyAuthenticated(authentication);
|
||||
}
|
||||
|
||||
public void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) {
|
||||
Assert.notNull(authenticationTrustResolver, "AuthenticationTrustResolver cannot be set to null");
|
||||
this.authenticationTrustResolver = authenticationTrustResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return (attribute.getAttribute() != null) && (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute())
|
||||
|| IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute())
|
||||
|| IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation supports any type of class, because it does not query the
|
||||
* presented secure object.
|
||||
* @param clazz the secure object type
|
||||
* @return always {@code true}
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
|
||||
int result = ACCESS_ABSTAIN;
|
||||
for (ConfigAttribute attribute : attributes) {
|
||||
if (this.supports(attribute)) {
|
||||
result = ACCESS_DENIED;
|
||||
if (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute())) {
|
||||
if (isFullyAuthenticated(authentication)) {
|
||||
return ACCESS_GRANTED;
|
||||
}
|
||||
}
|
||||
if (IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute())) {
|
||||
if (this.authenticationTrustResolver.isRememberMe(authentication)
|
||||
|| isFullyAuthenticated(authentication)) {
|
||||
return ACCESS_GRANTED;
|
||||
}
|
||||
}
|
||||
if (IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute())) {
|
||||
if (this.authenticationTrustResolver.isAnonymous(authentication)
|
||||
|| isFullyAuthenticated(authentication)
|
||||
|| this.authenticationTrustResolver.isRememberMe(authentication)) {
|
||||
return ACCESS_GRANTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.vote;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* Simple concrete implementation of
|
||||
* {@link org.springframework.security.access.AccessDecisionManager} that uses a
|
||||
* consensus-based approach.
|
||||
* <p>
|
||||
* "Consensus" here means majority-rule (ignoring abstains) rather than unanimous
|
||||
* agreement (ignoring abstains). If you require unanimity, please see
|
||||
* {@link UnanimousBased}.
|
||||
*
|
||||
* @deprecated Use {@link AuthorizationManager} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class ConsensusBased extends AbstractAccessDecisionManager {
|
||||
|
||||
private boolean allowIfEqualGrantedDeniedDecisions = true;
|
||||
|
||||
public ConsensusBased(List<AccessDecisionVoter<?>> decisionVoters) {
|
||||
super(decisionVoters);
|
||||
}
|
||||
|
||||
/**
|
||||
* This concrete implementation simply polls all configured
|
||||
* {@link AccessDecisionVoter}s and upon completion determines the consensus of
|
||||
* granted against denied responses.
|
||||
* <p>
|
||||
* If there were an equal number of grant and deny votes, the decision will be based
|
||||
* on the {@link #isAllowIfEqualGrantedDeniedDecisions()} property (defaults to true).
|
||||
* <p>
|
||||
* If every <code>AccessDecisionVoter</code> abstained from voting, the decision will
|
||||
* be based on the {@link #isAllowIfAllAbstainDecisions()} property (defaults to
|
||||
* false).
|
||||
* @param authentication the caller invoking the method
|
||||
* @param object the secured object
|
||||
* @param configAttributes the configuration attributes associated with the method
|
||||
* being invoked
|
||||
* @throws AccessDeniedException if access is denied
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
|
||||
throws AccessDeniedException {
|
||||
int grant = 0;
|
||||
int deny = 0;
|
||||
for (AccessDecisionVoter voter : getDecisionVoters()) {
|
||||
int result = voter.vote(authentication, object, configAttributes);
|
||||
switch (result) {
|
||||
case AccessDecisionVoter.ACCESS_GRANTED -> grant++;
|
||||
case AccessDecisionVoter.ACCESS_DENIED -> deny++;
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (grant > deny) {
|
||||
return;
|
||||
}
|
||||
if (deny > grant) {
|
||||
throw new AccessDeniedException(
|
||||
this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
|
||||
}
|
||||
if ((grant == deny) && (grant != 0)) {
|
||||
if (this.allowIfEqualGrantedDeniedDecisions) {
|
||||
return;
|
||||
}
|
||||
throw new AccessDeniedException(
|
||||
this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
|
||||
}
|
||||
// To get this far, every AccessDecisionVoter abstained
|
||||
checkAllowIfAllAbstainDecisions();
|
||||
}
|
||||
|
||||
public boolean isAllowIfEqualGrantedDeniedDecisions() {
|
||||
return this.allowIfEqualGrantedDeniedDecisions;
|
||||
}
|
||||
|
||||
public void setAllowIfEqualGrantedDeniedDecisions(boolean allowIfEqualGrantedDeniedDecisions) {
|
||||
this.allowIfEqualGrantedDeniedDecisions = allowIfEqualGrantedDeniedDecisions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.access.vote;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Extended RoleVoter which uses a {@link RoleHierarchy} definition to determine the roles
|
||||
* allocated to the current user before voting.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 2.0.4
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.AuthorityAuthorizationManager#setRoleHierarchy}
|
||||
* instead
|
||||
*/
|
||||
@NullUnmarked
|
||||
@Deprecated
|
||||
public class RoleHierarchyVoter extends RoleVoter {
|
||||
|
||||
@SuppressWarnings("NullAway")
|
||||
private @Nullable RoleHierarchy roleHierarchy = null;
|
||||
|
||||
public RoleHierarchyVoter(RoleHierarchy roleHierarchy) {
|
||||
Assert.notNull(roleHierarchy, "RoleHierarchy must not be null");
|
||||
this.roleHierarchy = roleHierarchy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the <tt>RoleHierarchy</tt> to obtain the complete set of user authorities.
|
||||
*/
|
||||
@Override
|
||||
Collection<? extends GrantedAuthority> extractAuthorities(Authentication authentication) {
|
||||
return this.roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.vote;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.jspecify.annotations.NullUnmarked;
|
||||
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
/**
|
||||
* Votes if any {@link ConfigAttribute#getAttribute()} starts with a prefix indicating
|
||||
* that it is a role. The default prefix string is <Code>ROLE_</code>, but this may be
|
||||
* overridden to any value. It may also be set to empty, which means that essentially any
|
||||
* attribute will be voted on. As described further below, the effect of an empty prefix
|
||||
* may not be quite desirable.
|
||||
* <p>
|
||||
* Abstains from voting if no configuration attribute commences with the role prefix.
|
||||
* Votes to grant access if there is an exact matching
|
||||
* {@link org.springframework.security.core.GrantedAuthority} to a
|
||||
* <code>ConfigAttribute</code> starting with the role prefix. Votes to deny access if
|
||||
* there is no exact matching <code>GrantedAuthority</code> to a
|
||||
* <code>ConfigAttribute</code> starting with the role prefix.
|
||||
* <p>
|
||||
* An empty role prefix means that the voter will vote for every ConfigAttribute. When
|
||||
* there are different categories of ConfigAttributes used, this will not be optimal since
|
||||
* the voter will be voting for attributes which do not represent roles. However, this
|
||||
* option may be of some use when using pre-existing role names without a prefix, and no
|
||||
* ability exists to prefix them with a role prefix on reading them in, such as provided
|
||||
* for example in {@link org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl}.
|
||||
* <p>
|
||||
* All comparisons and prefixes are case sensitive.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author colin sampaleanu
|
||||
* @deprecated Use
|
||||
* {@link org.springframework.security.authorization.AuthorityAuthorizationManager}
|
||||
* instead
|
||||
*/
|
||||
@Deprecated
|
||||
@NullUnmarked
|
||||
public class RoleVoter implements AccessDecisionVoter<Object> {
|
||||
|
||||
private String rolePrefix = "ROLE_";
|
||||
|
||||
public String getRolePrefix() {
|
||||
return this.rolePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the default role prefix of <code>ROLE_</code> to be overridden. May be set
|
||||
* to an empty value, although this is usually not desirable.
|
||||
* @param rolePrefix the new prefix
|
||||
*/
|
||||
public void setRolePrefix(String rolePrefix) {
|
||||
this.rolePrefix = rolePrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return (attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix());
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation supports any type of class, because it does not query the
|
||||
* presented secure object.
|
||||
* @param clazz the secure object
|
||||
* @return always <code>true</code>
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) {
|
||||
if (authentication == null) {
|
||||
return ACCESS_DENIED;
|
||||
}
|
||||
int result = ACCESS_ABSTAIN;
|
||||
Collection<? extends GrantedAuthority> authorities = extractAuthorities(authentication);
|
||||
for (ConfigAttribute attribute : attributes) {
|
||||
if (this.supports(attribute)) {
|
||||
result = ACCESS_DENIED;
|
||||
// Attempt to find a matching granted authority
|
||||
for (GrantedAuthority authority : authorities) {
|
||||
if (attribute.getAttribute().equals(authority.getAuthority())) {
|
||||
return ACCESS_GRANTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Collection<? extends GrantedAuthority> extractAuthorities(Authentication authentication) {
|
||||
return authentication.getAuthorities();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.access.vote;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.access.AccessDecisionVoter;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* Simple concrete implementation of
|
||||
* {@link org.springframework.security.access.AccessDecisionManager} that requires all
|
||||
* voters to abstain or grant access.
|
||||
*
|
||||
* @deprecated Use {@link AuthorizationManager} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class UnanimousBased extends AbstractAccessDecisionManager {
|
||||
|
||||
public UnanimousBased(List<AccessDecisionVoter<?>> decisionVoters) {
|
||||
super(decisionVoters);
|
||||
}
|
||||
|
||||
/**
|
||||
* This concrete implementation polls all configured {@link AccessDecisionVoter}s for
|
||||
* each {@link ConfigAttribute} and grants access if <b>only</b> grant (or abstain)
|
||||
* votes were received.
|
||||
* <p>
|
||||
* Other voting implementations usually pass the entire list of
|
||||
* <tt>ConfigAttribute</tt>s to the <code>AccessDecisionVoter</code>. This
|
||||
* implementation differs in that each <code>AccessDecisionVoter</code> knows only
|
||||
* about a single <code>ConfigAttribute</code> at a time.
|
||||
* <p>
|
||||
* If every <code>AccessDecisionVoter</code> abstained from voting, the decision will
|
||||
* be based on the {@link #isAllowIfAllAbstainDecisions()} property (defaults to
|
||||
* false).
|
||||
* @param authentication the caller invoking the method
|
||||
* @param object the secured object
|
||||
* @param attributes the configuration attributes associated with the method being
|
||||
* invoked
|
||||
* @throws AccessDeniedException if access is denied
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> attributes)
|
||||
throws AccessDeniedException {
|
||||
int grant = 0;
|
||||
List<ConfigAttribute> singleAttributeList = new ArrayList<>(1);
|
||||
singleAttributeList.add(null);
|
||||
for (ConfigAttribute attribute : attributes) {
|
||||
singleAttributeList.set(0, attribute);
|
||||
for (AccessDecisionVoter voter : getDecisionVoters()) {
|
||||
int result = voter.vote(authentication, object, singleAttributeList);
|
||||
switch (result) {
|
||||
case AccessDecisionVoter.ACCESS_GRANTED:
|
||||
grant++;
|
||||
break;
|
||||
case AccessDecisionVoter.ACCESS_DENIED:
|
||||
throw new AccessDeniedException(this.messages
|
||||
.getMessage("AbstractAccessDecisionManager.accessDenied", "Access is denied"));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// To get this far, there were no deny votes
|
||||
if (grant > 0) {
|
||||
return;
|
||||
}
|
||||
// To get this far, every AccessDecisionVoter abstained
|
||||
checkAllowIfAllAbstainDecisions();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Implements a vote-based approach to authorization decisions.
|
||||
*/
|
||||
@NullMarked
|
||||
package org.springframework.security.access.vote;
|
||||
|
||||
import org.jspecify.annotations.NullMarked;
|
||||
@@ -1,245 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.acls;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.security.access.AuthorizationServiceException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.access.vote.AbstractAclVoter;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
|
||||
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.AclService;
|
||||
import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.acls.model.SidRetrievalStrategy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Given a domain object instance passed as a method argument, ensures the principal has
|
||||
* appropriate permission as indicated by the {@link AclService}.
|
||||
* <p>
|
||||
* The <tt>AclService</tt> is used to retrieve the access control list (ACL) permissions
|
||||
* associated with a domain object instance for the current <tt>Authentication</tt>
|
||||
* object.
|
||||
* <p>
|
||||
* The voter will vote if any {@link ConfigAttribute#getAttribute()} matches the
|
||||
* {@link #processConfigAttribute}. The provider will then locate the first method
|
||||
* argument of type {@link #processDomainObjectClass}. Assuming that method argument is
|
||||
* non-null, the provider will then lookup the ACLs from the <code>AclManager</code> and
|
||||
* ensure the principal is {@link Acl#isGranted(List, List, boolean)} when presenting the
|
||||
* {@link #requirePermission} array to that method.
|
||||
* <p>
|
||||
* If the method argument is <tt>null</tt>, the voter will abstain from voting. If the
|
||||
* method argument could not be found, an {@link AuthorizationServiceException} will be
|
||||
* thrown.
|
||||
* <p>
|
||||
* In practical terms users will typically setup a number of <tt>AclEntryVoter</tt>s. Each
|
||||
* will have a different {@link #setProcessDomainObjectClass processDomainObjectClass},
|
||||
* {@link #processConfigAttribute} and {@link #requirePermission} combination. For
|
||||
* example, a small application might employ the following instances of
|
||||
* <tt>AclEntryVoter</tt>:
|
||||
* <ul>
|
||||
* <li>Process domain object class <code>BankAccount</code>, configuration attribute
|
||||
* <code>VOTE_ACL_BANK_ACCOUNT_READ</code>, require permission
|
||||
* <code>BasePermission.READ</code></li>
|
||||
* <li>Process domain object class <code>BankAccount</code>, configuration attribute
|
||||
* <code>VOTE_ACL_BANK_ACCOUNT_WRITE</code>, require permission list
|
||||
* <code>BasePermission.WRITE</code> and <code>BasePermission.CREATE</code> (allowing the
|
||||
* principal to have <b>either</b> of these two permissions)</li>
|
||||
* <li>Process domain object class <code>Customer</code>, configuration attribute
|
||||
* <code>VOTE_ACL_CUSTOMER_READ</code>, require permission
|
||||
* <code>BasePermission.READ</code></li>
|
||||
* <li>Process domain object class <code>Customer</code>, configuration attribute
|
||||
* <code>VOTE_ACL_CUSTOMER_WRITE</code>, require permission list
|
||||
* <code>BasePermission.WRITE</code> and <code>BasePermission.CREATE</code></li>
|
||||
* </ul>
|
||||
* Alternatively, you could have used a common superclass or interface for the
|
||||
* {@link #processDomainObjectClass} if both <code>BankAccount</code> and
|
||||
* <code>Customer</code> had common parents.
|
||||
*
|
||||
* <p>
|
||||
* If the principal does not have sufficient permissions, the voter will vote to deny
|
||||
* access.
|
||||
*
|
||||
* <p>
|
||||
* All comparisons and prefixes are case sensitive.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
|
||||
* annotations may also prove useful, for example
|
||||
* {@code @PreAuthorize("hasPermission(#id, ObjectsReturnType.class, read)")}
|
||||
*/
|
||||
@Deprecated
|
||||
public class AclEntryVoter extends AbstractAclVoter {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AclEntryVoter.class);
|
||||
|
||||
private final AclService aclService;
|
||||
|
||||
private final String processConfigAttribute;
|
||||
|
||||
private final List<Permission> requirePermission;
|
||||
|
||||
private ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
|
||||
|
||||
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
|
||||
|
||||
private String internalMethod;
|
||||
|
||||
public AclEntryVoter(AclService aclService, String processConfigAttribute, Permission[] requirePermission) {
|
||||
Assert.notNull(processConfigAttribute, "A processConfigAttribute is mandatory");
|
||||
Assert.notNull(aclService, "An AclService is mandatory");
|
||||
Assert.isTrue(!ObjectUtils.isEmpty(requirePermission), "One or more requirePermission entries is mandatory");
|
||||
this.aclService = aclService;
|
||||
this.processConfigAttribute = processConfigAttribute;
|
||||
this.requirePermission = Arrays.asList(requirePermission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionally specifies a method of the domain object that will be used to obtain a
|
||||
* contained domain object. That contained domain object will be used for the ACL
|
||||
* evaluation. This is useful if a domain object contains a parent that an ACL
|
||||
* evaluation should be targeted for, instead of the child domain object (which
|
||||
* perhaps is being created and as such does not yet have any ACL permissions)
|
||||
* @return <code>null</code> to use the domain object, or the name of a method (that
|
||||
* requires no arguments) that should be invoked to obtain an <code>Object</code>
|
||||
* which will be the domain object used for ACL evaluation
|
||||
*/
|
||||
protected String getInternalMethod() {
|
||||
return this.internalMethod;
|
||||
}
|
||||
|
||||
public void setInternalMethod(String internalMethod) {
|
||||
this.internalMethod = internalMethod;
|
||||
}
|
||||
|
||||
protected String getProcessConfigAttribute() {
|
||||
return this.processConfigAttribute;
|
||||
}
|
||||
|
||||
public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
|
||||
Assert.notNull(objectIdentityRetrievalStrategy, "ObjectIdentityRetrievalStrategy required");
|
||||
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
|
||||
}
|
||||
|
||||
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
|
||||
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
|
||||
this.sidRetrievalStrategy = sidRetrievalStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return (attribute.getAttribute() != null) && attribute.getAttribute().equals(getProcessConfigAttribute());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int vote(Authentication authentication, MethodInvocation object, Collection<ConfigAttribute> attributes) {
|
||||
for (ConfigAttribute attr : attributes) {
|
||||
if (!supports(attr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Need to make an access decision on this invocation
|
||||
// Attempt to locate the domain object instance to process
|
||||
Object domainObject = getDomainObjectInstance(object);
|
||||
|
||||
// If domain object is null, vote to abstain
|
||||
if (domainObject == null) {
|
||||
logger.debug("Voting to abstain - domainObject is null");
|
||||
return ACCESS_ABSTAIN;
|
||||
}
|
||||
|
||||
// Evaluate if we are required to use an inner domain object
|
||||
if (StringUtils.hasText(this.internalMethod)) {
|
||||
domainObject = invokeInternalMethod(domainObject);
|
||||
}
|
||||
|
||||
// Obtain the OID applicable to the domain object
|
||||
ObjectIdentity objectIdentity = this.objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);
|
||||
|
||||
// Obtain the SIDs applicable to the principal
|
||||
List<Sid> sids = this.sidRetrievalStrategy.getSids(authentication);
|
||||
|
||||
Acl acl;
|
||||
|
||||
try {
|
||||
// Lookup only ACLs for SIDs we're interested in
|
||||
acl = this.aclService.readAclById(objectIdentity, sids);
|
||||
}
|
||||
catch (NotFoundException ex) {
|
||||
logger.debug("Voting to deny access - no ACLs apply for this principal");
|
||||
return ACCESS_DENIED;
|
||||
}
|
||||
|
||||
try {
|
||||
if (acl.isGranted(this.requirePermission, sids, false)) {
|
||||
logger.debug("Voting to grant access");
|
||||
return ACCESS_GRANTED;
|
||||
}
|
||||
logger.debug("Voting to deny access - ACLs returned, but insufficient permissions for this principal");
|
||||
return ACCESS_DENIED;
|
||||
}
|
||||
catch (NotFoundException ex) {
|
||||
logger.debug("Voting to deny access - no ACLs apply for this principal");
|
||||
return ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
// No configuration attribute matched, so abstain
|
||||
return ACCESS_ABSTAIN;
|
||||
}
|
||||
|
||||
private Object invokeInternalMethod(Object domainObject) {
|
||||
try {
|
||||
Class<?> domainObjectType = domainObject.getClass();
|
||||
Method method = domainObjectType.getMethod(this.internalMethod, new Class[0]);
|
||||
return method.invoke(domainObject);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new AuthorizationServiceException("Object of class '" + domainObject.getClass()
|
||||
+ "' does not provide the requested internalMethod: " + this.internalMethod);
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
logger.debug("IllegalAccessException", ex);
|
||||
throw new AuthorizationServiceException(
|
||||
"Problem invoking internalMethod: " + this.internalMethod + " for object: " + domainObject);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
logger.debug("InvocationTargetException", ex);
|
||||
throw new AuthorizationServiceException(
|
||||
"Problem invoking internalMethod: " + this.internalMethod + " for object: " + domainObject);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-129
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.acls.afterinvocation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.access.AfterInvocationProvider;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.acls.AclPermissionEvaluator;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
|
||||
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.AclService;
|
||||
import org.springframework.security.acls.model.NotFoundException;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.ObjectIdentityRetrievalStrategy;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.acls.model.SidRetrievalStrategy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Abstract {@link AfterInvocationProvider} which provides commonly-used ACL-related
|
||||
* services.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
|
||||
* annotations may also prove useful, for example
|
||||
* {@code @PostAuthorize("hasPermission(filterObject, read)")}
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractAclProvider implements AfterInvocationProvider {
|
||||
|
||||
protected final AclService aclService;
|
||||
|
||||
protected String processConfigAttribute;
|
||||
|
||||
protected Class<?> processDomainObjectClass = Object.class;
|
||||
|
||||
protected ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy = new ObjectIdentityRetrievalStrategyImpl();
|
||||
|
||||
protected SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
|
||||
|
||||
protected final List<Permission> requirePermission;
|
||||
|
||||
public AbstractAclProvider(AclService aclService, String processConfigAttribute,
|
||||
List<Permission> requirePermission) {
|
||||
Assert.hasText(processConfigAttribute, "A processConfigAttribute is mandatory");
|
||||
Assert.notNull(aclService, "An AclService is mandatory");
|
||||
Assert.isTrue(!ObjectUtils.isEmpty(requirePermission), "One or more requirePermission entries is mandatory");
|
||||
this.aclService = aclService;
|
||||
this.processConfigAttribute = processConfigAttribute;
|
||||
this.requirePermission = requirePermission;
|
||||
}
|
||||
|
||||
protected Class<?> getProcessDomainObjectClass() {
|
||||
return this.processDomainObjectClass;
|
||||
}
|
||||
|
||||
protected boolean hasPermission(Authentication authentication, Object domainObject) {
|
||||
// Obtain the OID applicable to the domain object
|
||||
ObjectIdentity objectIdentity = this.objectIdentityRetrievalStrategy.getObjectIdentity(domainObject);
|
||||
|
||||
// Obtain the SIDs applicable to the principal
|
||||
List<Sid> sids = this.sidRetrievalStrategy.getSids(authentication);
|
||||
|
||||
try {
|
||||
// Lookup only ACLs for SIDs we're interested in
|
||||
Acl acl = this.aclService.readAclById(objectIdentity, sids);
|
||||
return acl.isGranted(this.requirePermission, sids, false);
|
||||
}
|
||||
catch (NotFoundException ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setObjectIdentityRetrievalStrategy(ObjectIdentityRetrievalStrategy objectIdentityRetrievalStrategy) {
|
||||
Assert.notNull(objectIdentityRetrievalStrategy, "ObjectIdentityRetrievalStrategy required");
|
||||
this.objectIdentityRetrievalStrategy = objectIdentityRetrievalStrategy;
|
||||
}
|
||||
|
||||
protected void setProcessConfigAttribute(String processConfigAttribute) {
|
||||
Assert.hasText(processConfigAttribute, "A processConfigAttribute is mandatory");
|
||||
this.processConfigAttribute = processConfigAttribute;
|
||||
}
|
||||
|
||||
public void setProcessDomainObjectClass(Class<?> processDomainObjectClass) {
|
||||
Assert.notNull(processDomainObjectClass, "processDomainObjectClass cannot be set to null");
|
||||
this.processDomainObjectClass = processDomainObjectClass;
|
||||
}
|
||||
|
||||
public void setSidRetrievalStrategy(SidRetrievalStrategy sidRetrievalStrategy) {
|
||||
Assert.notNull(sidRetrievalStrategy, "SidRetrievalStrategy required");
|
||||
this.sidRetrievalStrategy = sidRetrievalStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(ConfigAttribute attribute) {
|
||||
return this.processConfigAttribute.equals(attribute.getAttribute());
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation supports any type of class, because it does not query the
|
||||
* presented secure object.
|
||||
* @param clazz the secure object
|
||||
* @return always <code>true</code>
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> clazz) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
-125
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
|
||||
*
|
||||
* 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.acls.afterinvocation;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.AuthorizationServiceException;
|
||||
import org.springframework.security.access.ConfigAttribute;
|
||||
import org.springframework.security.acls.AclPermissionEvaluator;
|
||||
import org.springframework.security.acls.model.AclService;
|
||||
import org.springframework.security.acls.model.Permission;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Given a <code>Collection</code> of domain object instances returned from a secure
|
||||
* object invocation, remove any <code>Collection</code> elements the principal does not
|
||||
* have appropriate permission to access as defined by the {@link AclService}.
|
||||
* <p>
|
||||
* The <code>AclService</code> is used to retrieve the access control list (ACL)
|
||||
* permissions associated with each <code>Collection</code> domain object instance element
|
||||
* for the current <code>Authentication</code> object.
|
||||
* <p>
|
||||
* This after invocation provider will fire if any {@link ConfigAttribute#getAttribute()}
|
||||
* matches the {@link #processConfigAttribute}. The provider will then lookup the ACLs
|
||||
* from the <code>AclService</code> and ensure the principal is
|
||||
* {@link org.springframework.security.acls.model.Acl#isGranted(List, List, boolean)
|
||||
* Acl.isGranted()} when presenting the {@link #requirePermission} array to that method.
|
||||
* <p>
|
||||
* If the principal does not have permission, that element will not be included in the
|
||||
* returned <code>Collection</code>.
|
||||
* <p>
|
||||
* Often users will setup a <code>BasicAclEntryAfterInvocationProvider</code> with a
|
||||
* {@link #processConfigAttribute} of <code>AFTER_ACL_COLLECTION_READ</code> and a
|
||||
* {@link #requirePermission} of <code>BasePermission.READ</code>. These are also the
|
||||
* defaults.
|
||||
* <p>
|
||||
* If the provided <code>returnObject</code> is <code>null</code>, a <code>null</code>
|
||||
* <code>Collection</code> will be returned. If the provided <code>returnObject</code> is
|
||||
* not a <code>Collection</code>, an {@link AuthorizationServiceException} will be thrown.
|
||||
* <p>
|
||||
* All comparisons and prefixes are case sensitive.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Paulo Neves
|
||||
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
|
||||
* annotations may also prove useful, for example
|
||||
* {@code @PostFilter("hasPermission(filterObject, read)")}
|
||||
*/
|
||||
@Deprecated
|
||||
public class AclEntryAfterInvocationCollectionFilteringProvider extends AbstractAclProvider {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationCollectionFilteringProvider.class);
|
||||
|
||||
public AclEntryAfterInvocationCollectionFilteringProvider(AclService aclService,
|
||||
List<Permission> requirePermission) {
|
||||
super(aclService, "AFTER_ACL_COLLECTION_READ", requirePermission);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Object decide(Authentication authentication, Object object, Collection<ConfigAttribute> config,
|
||||
Object returnedObject) throws AccessDeniedException {
|
||||
if (returnedObject == null) {
|
||||
logger.debug("Return object is null, skipping");
|
||||
return null;
|
||||
}
|
||||
|
||||
for (ConfigAttribute attr : config) {
|
||||
if (!this.supports(attr)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Need to process the Collection for this invocation
|
||||
Filterer filterer = getFilterer(returnedObject);
|
||||
|
||||
// Locate unauthorised Collection elements
|
||||
for (Object domainObject : filterer) {
|
||||
// Ignore nulls or entries which aren't instances of the configured domain
|
||||
// object class
|
||||
if (domainObject == null || !getProcessDomainObjectClass().isAssignableFrom(domainObject.getClass())) {
|
||||
continue;
|
||||
}
|
||||
if (!hasPermission(authentication, domainObject)) {
|
||||
filterer.remove(domainObject);
|
||||
logger.debug(LogMessage.of(() -> "Principal is NOT authorised for element: " + domainObject));
|
||||
}
|
||||
}
|
||||
return filterer.getFilteredObject();
|
||||
}
|
||||
return returnedObject;
|
||||
}
|
||||
|
||||
private Filterer getFilterer(Object returnedObject) {
|
||||
if (returnedObject instanceof Collection) {
|
||||
return new CollectionFilterer((Collection) returnedObject);
|
||||
}
|
||||
if (returnedObject.getClass().isArray()) {
|
||||
return new ArrayFilterer((Object[]) returnedObject);
|
||||
}
|
||||
throw new AuthorizationServiceException("A Collection or an array (or null) was required as the "
|
||||
+ "returnedObject, but the returnedObject was: " + returnedObject);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user