Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3c2d810394 |
@@ -15,4 +15,3 @@
|
||||
*.otg binary
|
||||
*.png binary
|
||||
*.hsx binary
|
||||
*.serialized binary
|
||||
|
||||
@@ -7,10 +7,6 @@ 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.
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
require:
|
||||
members: false
|
||||
@@ -1,121 +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'
|
||||
- package-ecosystem: npm
|
||||
target-branch: 6.3.x
|
||||
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@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@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 +0,0 @@
|
||||
name: "CodeQL Advanced"
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule
|
||||
- cron: '0 5 * * *'
|
||||
permissions: read-all
|
||||
jobs:
|
||||
codeql-analysis-call:
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
uses: spring-io/github-actions/.github/workflows/codeql-analysis.yml@1
|
||||
@@ -1,73 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- "dependabot/**"
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch: # Manual trigger
|
||||
|
||||
env:
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/build.yml@v1
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest ]
|
||||
jdk: [ 17 ]
|
||||
with:
|
||||
runs-on: ${{ matrix.os }}
|
||||
java-version: ${{ matrix.jdk }}
|
||||
distribution: temurin
|
||||
secrets: inherit
|
||||
deploy-artifacts:
|
||||
name: Deploy Artifacts
|
||||
needs: [ build]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@v1
|
||||
with:
|
||||
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
default-publish-milestones-central: true
|
||||
secrets: inherit
|
||||
deploy-docs:
|
||||
name: Deploy Docs
|
||||
needs: [ build ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-docs.yml@v1
|
||||
with:
|
||||
should-deploy-docs: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
secrets: inherit
|
||||
deploy-schema:
|
||||
name: Deploy Schema
|
||||
needs: [ build ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@v1
|
||||
with:
|
||||
should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
secrets: inherit
|
||||
perform-release:
|
||||
name: Perform Release
|
||||
needs: [ deploy-artifacts, deploy-docs, deploy-schema ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@v1
|
||||
with:
|
||||
should-perform-release: ${{ needs.deploy-artifacts.outputs.artifacts-deployed }}
|
||||
project-version: ${{ needs.deploy-artifacts.outputs.project-version }}
|
||||
milestone-repo-url: https://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@v1
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
@@ -1,33 +0,0 @@
|
||||
name: Deploy Docs
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- "gh-pages"
|
||||
- "dependabot/**"
|
||||
tags: '**'
|
||||
repository_dispatch:
|
||||
types: request-build-reference # legacy
|
||||
#schedule:
|
||||
#- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch:
|
||||
permissions: read-all
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: docs-build
|
||||
fetch-depth: 1
|
||||
- name: Dispatch (partial build)
|
||||
if: github.ref_type == 'branch'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }}
|
||||
- name: Dispatch (full build)
|
||||
if: github.ref_type == 'tag'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD)
|
||||
@@ -1,41 +0,0 @@
|
||||
name: Finalize Release
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Manual trigger
|
||||
|
||||
env:
|
||||
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
project-version:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.project-version.outputs.version }}
|
||||
steps:
|
||||
- id: project-version
|
||||
run: echo "version=$(grep '^version=' gradle.properties | cut -d'=' -f2)" >> $GITHUB_OUTPUT
|
||||
perform-release:
|
||||
name: Perform Release
|
||||
needs: [ project-version ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@v1
|
||||
with:
|
||||
should-perform-release: true
|
||||
project-version: ${{ needs.project-version.outputs.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@v1
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
@@ -1,33 +0,0 @@
|
||||
name: Execute Gradle Wrapper Upgrade
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # 2am UTC
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
pull-requests: write
|
||||
jobs:
|
||||
upgrade_wrapper:
|
||||
name: Execution
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Git configuration
|
||||
env:
|
||||
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
git config --global url."https://unused-username:${TOKEN}@github.com/".insteadOf "https://github.com/"
|
||||
git config --global user.name 'github-actions[bot]'
|
||||
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Set up Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
- name: Upgrade Wrappers
|
||||
run: ./gradlew clean upgradeGradleWrapperAll --continue -Porg.gradle.java.installations.auto-download=false
|
||||
env:
|
||||
WRAPPER_UPGRADE_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,35 +0,0 @@
|
||||
name: Check Milestone
|
||||
on:
|
||||
milestone:
|
||||
types: [created, opened, edited]
|
||||
env:
|
||||
DUE_ON: ${{ github.event.milestone.due_on }}
|
||||
TITLE: ${{ github.event.milestone.title }}
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
spring-releasetrain-checks:
|
||||
name: Check DueOn is on a Release Date
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
permissions:
|
||||
contents: none
|
||||
steps:
|
||||
- name: Print Milestone Being Checked
|
||||
run: echo "Validating DueOn '$DUE_ON' for milestone '$TITLE'"
|
||||
- name: Validate DueOn
|
||||
if: env.DUE_ON != ''
|
||||
run: |
|
||||
export TOOL_VERSION=0.1.1
|
||||
wget "https://repo.maven.apache.org/maven2/io/spring/releasetrain/spring-release-train-tools/$TOOL_VERSION/spring-release-train-tools-$TOOL_VERSION.jar"
|
||||
java -cp "spring-release-train-tools-$TOOL_VERSION.jar" io.spring.releasetrain.CheckMilestoneDueOnMain --dueOn "$DUE_ON" --expectedDayOfWeek MONDAY --expectedMondayCount 3
|
||||
send-notification:
|
||||
name: Send Notification
|
||||
needs: [ spring-releasetrain-checks ]
|
||||
if: ${{ failure() || cancelled() }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Notification
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
@@ -1,51 +0,0 @@
|
||||
name: PR Build
|
||||
|
||||
on: pull_request
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v2
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew clean build -PskipCheckExpectedBranchVersion --continue --scan
|
||||
generate-docs:
|
||||
name: Generate Docs
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v2
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Run Antora
|
||||
run: ./gradlew -PbuildSrc.skipTests=true :spring-security-docs:antora
|
||||
- name: Upload Docs
|
||||
id: upload
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: docs
|
||||
path: docs/build/site
|
||||
overwrite: true
|
||||
send-notification:
|
||||
name: Send Notification
|
||||
needs: [ build, generate-docs ]
|
||||
if: ${{ failure() && github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'spring-projects/spring-security' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Notification
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
@@ -1,24 +0,0 @@
|
||||
name: Release Scheduler
|
||||
on:
|
||||
schedule:
|
||||
- cron: '15 15 * * MON' # Every Monday at 3:15pm UTC
|
||||
workflow_dispatch:
|
||||
permissions: read-all
|
||||
jobs:
|
||||
dispatch_scheduled_releases:
|
||||
name: Dispatch scheduled releases
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
strategy:
|
||||
matrix:
|
||||
# List of active maintenance branches.
|
||||
branch: [ main, 6.5.x, 6.4.x, 6.3.x ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Dispatch
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
run: gh workflow run update-scheduled-release-version.yml -r ${{ matrix.branch }}
|
||||
@@ -1,35 +0,0 @@
|
||||
name: Update Antora UI Spring
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-antora-ui-spring:
|
||||
runs-on: ubuntu-latest
|
||||
name: Update on Supported Branches
|
||||
strategy:
|
||||
matrix:
|
||||
branch: [ '5.8.x', '6.2.x', '6.3.x', 'main' ]
|
||||
steps:
|
||||
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@e28269199d1d27975cf7f65e16d6095c555b3cd0
|
||||
name: Update
|
||||
with:
|
||||
docs-branch: ${{ matrix.branch }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
antora-file-path: 'docs/antora-playbook.yml'
|
||||
update-antora-ui-spring-docs-build:
|
||||
runs-on: ubuntu-latest
|
||||
name: Update on docs-build
|
||||
steps:
|
||||
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@e28269199d1d27975cf7f65e16d6095c555b3cd0
|
||||
name: Update
|
||||
with:
|
||||
docs-branch: 'docs-build'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -1,23 +0,0 @@
|
||||
name: Update Scheduled Release Version
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Manual trigger only. Triggered by release-scheduler.yml on main.
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update-scheduled-release-version:
|
||||
name: Update Scheduled Release Version
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml@v1
|
||||
secrets: inherit
|
||||
send-notification:
|
||||
name: Send Notification
|
||||
needs: [ update-scheduled-release-version ]
|
||||
if: ${{ failure() || cancelled() }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Notification
|
||||
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
|
||||
with:
|
||||
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
|
||||
@@ -25,7 +25,6 @@ atlassian-ide-plugin.xml
|
||||
s101plugin.state
|
||||
.attach_pid*
|
||||
.~lock.*#
|
||||
.kotlin/
|
||||
|
||||
!.idea/checkstyle-idea.xml
|
||||
!.idea/externalDependencies.xml
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# See https://sdkman.io/usage#config
|
||||
# A summary is to add the following to ~/.sdkman/etc/config
|
||||
# sdkman_auto_env=true
|
||||
java=17.0.3-tem
|
||||
java=11.0.14-tem
|
||||
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"java.import.gradle.enabled": false
|
||||
}
|
||||
+195
-109
@@ -1,146 +1,232 @@
|
||||
= Contributing to Spring Security
|
||||
_Have something you'd like to contribute to the framework? We welcome pull requests, but ask that you carefully read this document first to understand how best to submit them; what kind of changes are likely to be accepted; and what to expect from the Spring Security team when evaluating your submission._
|
||||
|
||||
First off, thank you for taking the time to contribute! :+1: :tada:
|
||||
_Please refer back to this document as a checklist before issuing any pull request; this will save time for everyone!_
|
||||
|
||||
== Table of Contents
|
||||
= Code of Conduct
|
||||
|
||||
* <<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>>
|
||||
Please see our https://github.com/spring-projects/.github/blob/main/CODE_OF_CONDUCT.md[code of conduct].
|
||||
|
||||
[[code-of-conduct]]
|
||||
== Code of Conduct
|
||||
= Similar but different
|
||||
|
||||
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.
|
||||
Each Spring module is slightly different from one another in terms of team size, number of issues, etc. Therefore, each project is managed slightly different. You will notice that this document is very similar to the https://github.com/spring-projects/spring-framework/wiki/Contributor-guidelines[Spring Framework Contributor guidelines]. However, there are some subtle differences between the two documents, so please be sure to read this document thoroughly.
|
||||
|
||||
[[how-to-contribute]]
|
||||
== How to Contribute
|
||||
= Importing into IDE
|
||||
|
||||
[[ask-questions]]
|
||||
=== Ask Questions
|
||||
The following provides information on setting up a development environment that can run the sample in https://www.springsource.org/sts[Spring Tool Suite 3.6.0+]. Other IDE's should work using Gradle's IDE support, but have not been tested.
|
||||
|
||||
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.
|
||||
* IDE Setup
|
||||
** Install Spring Tool Suite 3.6.0+
|
||||
** You will need the following plugins installed (can be found on the Extensions Page)
|
||||
*** Gradle Eclipse
|
||||
*** Groovy Eclipse
|
||||
* Importing the project into Spring Tool Suite
|
||||
** File -> Import… -> Gradle Project
|
||||
|
||||
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.
|
||||
As of new versions of Spring Tool Suite, you might need to install Groovy Eclipse pointing directly to the updated plugin location. To install Groovy Eclipse on Spring Tool Suite based on Eclipse Oxigen you must do the following steps:
|
||||
|
||||
[[find-an-issue]]
|
||||
=== Find an Existing Issue
|
||||
Help -> Install New Software… -> Add the following URL into _Work with_ field:
|
||||
https://dist.springsource.org/snapshot/GRECLIPSE/e4.7/[https://dist.springsource.org/snapshot/GRECLIPSE/e4.7/]
|
||||
|
||||
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.
|
||||
= Understand the basics
|
||||
|
||||
[[create-an-issue]]
|
||||
=== Create an Issue
|
||||
Not sure what a pull request is, or how to submit one? Take a look at GitHub's excellent https://help.github.com/articles/using-pull-requests[help documentation first].
|
||||
|
||||
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.
|
||||
= Search GitHub issues; create an issue if necessary
|
||||
|
||||
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.
|
||||
Is there already an issue that addresses your concern? Do a bit of searching in our https://github.com/spring-projects/spring-security/issues[GitHub issues] to see if you can find something similar. If not, please create a new issue before submitting a pull request unless the change is not a user facing issue.
|
||||
|
||||
Once you're ready, create an issue on https://github.com/spring-projects/spring-security/issues[GitHub].
|
||||
= Discuss non-trivial contribution ideas with committers
|
||||
|
||||
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.
|
||||
If you're considering anything more than correcting a typo or fixing a minor bug, please discuss it on the https://gitter.im/spring-projects/spring-security[Spring Security Gitter] before submitting a pull request. We're happy to provide guidance but please spend an hour or two researching the subject on your own including searching the forums for prior discussions.
|
||||
|
||||
We love contributors, and we may ask you to <<submit-a-pull-request,submit a PR with a fix>>.
|
||||
= Sign the Contributor License Agreement
|
||||
|
||||
[[issue-lifecycle]]
|
||||
=== Issue Lifecycle
|
||||
If you have not previously done so, please fill out and submit the https://cla.pivotal.io/sign/spring[Contributor License Agreement].
|
||||
|
||||
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>>.
|
||||
= Create your branch from oldest maintenance branch
|
||||
|
||||
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.
|
||||
Create your topic branch to be submitted as a pull request from the oldest impacted and supported maintenance branch.
|
||||
You can find the supported versions by looking at the https://github.com/spring-projects/spring-security/milestones[milestones page].
|
||||
Switch to a branch named `<major>.<minor>.x` from the smallest milestone in the format of `<major>.<minor>.<patch>(-<prerelease>)`.
|
||||
The spring team will ensure the code gets merged forward into additional branches.
|
||||
|
||||
[[build-from-source]]
|
||||
=== Build from Source
|
||||
= Use short branch names
|
||||
|
||||
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.
|
||||
Branches used when submitting pull requests should preferably be named according to GitHub issues, e.g. `gh-1234` or `gh-1234-fix-npe`. Otherwise, use succinct, lower-case, dash (`-`) delimited names, such as `fix-warnings` or `fix-typo`. This is important, because branch names show up in the merge commits that result from accepting pull requests, and should be as expressive and concise as possible.
|
||||
|
||||
[[code-style]]
|
||||
=== Source Code Style
|
||||
= Keep commits focused
|
||||
|
||||
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.
|
||||
Remember each ticket should be focused on a single item of interest since the tickets are used to produce the changelog. Since each commit should be tied to a single GitHub issue, ensure that your commits are focused. For example, do not include an update to a transitive library in your commit unless the GitHub is to update the library. Reviewing your commits is essential before sending a pull request.
|
||||
|
||||
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.
|
||||
= Mind the whitespace
|
||||
|
||||
To format the code as well as check the style, run `./gradlew format && ./gradlew check`.
|
||||
Please carefully follow the whitespace and formatting conventions already present in the framework.
|
||||
|
||||
[[submit-a-pull-request]]
|
||||
=== Submit a Pull Request
|
||||
. Tabs, not spaces
|
||||
. Unix (LF), not dos (CRLF) line endings
|
||||
. Eliminate all trailing whitespace
|
||||
. Aim to wrap code at 120 characters, but favor readability over wrapping
|
||||
. Preserve existing formatting; i.e. do not reformat code for its own sake
|
||||
. Search the codebase using `git grep` and other tools to discover common naming conventions, etc.
|
||||
. UTF-8 encoding for Java sources and XML files
|
||||
|
||||
We are excited for your pull request! :heart:
|
||||
Whitespace management tips
|
||||
|
||||
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.
|
||||
. You can use the https://marketplace.eclipse.org/content/anyedit-tools[AnyEdit Eclipse plugin] to ensure spaces are used and to clean up trailing whitespaces.
|
||||
. Use Git's `pre-commit.sample` hook to prevent invalid whitespace from being pushed out. You can enable it by moving `.git/hooks/pre-commit.sample` to `.git/hooks/pre-commit` and ensuring it is executable. For more information on hooks refer to https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks[https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks].
|
||||
|
||||
= Add Apache license header to all new classes
|
||||
|
||||
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
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
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
|
||||
package ...;
|
||||
----
|
||||
|
||||
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.
|
||||
= Update Apache license header to modified files as necessary
|
||||
|
||||
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.
|
||||
Always check the date range in the license header. For example, if you've modified a file in 2020 whose header still reads
|
||||
|
||||
==== Participate in Reviews
|
||||
----
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
----
|
||||
|
||||
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.
|
||||
then be sure to update it to the current year appropriately (e.g. 2020)
|
||||
|
||||
----
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
----
|
||||
|
||||
= Use @since tags for newly-added public API types and methods
|
||||
|
||||
Example:
|
||||
|
||||
----
|
||||
/**
|
||||
* …
|
||||
*
|
||||
* @author First Last
|
||||
* @since 5.4
|
||||
* @see …
|
||||
*/
|
||||
----
|
||||
|
||||
= Submit JUnit test cases for all behavior changes
|
||||
|
||||
Search the codebase to find related unit tests and add additional `@Test` methods within.
|
||||
|
||||
. Any new tests should end in the name `Tests` (note this is plural). For example, a valid name would be `FilterChainProxyTests`. An invalid name would be `FilterChainProxyTest`.
|
||||
. New test methods should not start with test. This is an old JUnit3 convention and is not necessary since the method is annotated with `@Test`.
|
||||
|
||||
= Update spring-security-x.y.rnc for schema changes
|
||||
|
||||
Update the https://www.relaxng.org[RELAX NG] schema `spring-security-x.y.rnc` instead of `spring-security-x.y.xsd` if you contribute changes to supported XML configuration. The XML schema file can be generated the following Gradle task:
|
||||
|
||||
----
|
||||
./gradlew :spring-security-config:rncToXsd
|
||||
----
|
||||
|
||||
Changes to the XML schema will be overwritten by the Gradle build task.
|
||||
|
||||
= Squash commits
|
||||
|
||||
Use `git rebase --interactive`, `git add --patch` and other tools to "squash" multiple commits into atomic changes. In addition to the man pages for `git`, there are https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History[many resources online] to help you understand how these tools work.
|
||||
|
||||
= Use real name in git commits
|
||||
|
||||
Please configure Git to use your real first and last name for any commits you intend to submit as pull requests. Make sure the name is properly capitalized as submitted to the https://cla.pivotal.io[Pivotal Contributor License Agreement]:
|
||||
|
||||
----
|
||||
First Last <user@mail.com>
|
||||
----
|
||||
|
||||
This helps ensure traceability against the CLA, and also goes a long way to ensuring useful output from tools like Git shortlog and others.
|
||||
|
||||
You can configure this globally:
|
||||
|
||||
----
|
||||
git config --global user.name "First Last"
|
||||
git config --global user.email user@example.com
|
||||
----
|
||||
|
||||
or locally for the current repository by omitting the `--global` flag:
|
||||
|
||||
----
|
||||
git config user.name "First Last"
|
||||
git config user.email user@example.com
|
||||
----
|
||||
|
||||
= Format commit messages
|
||||
|
||||
. Keep the subject line to 50 characters or less if possible
|
||||
. Do not end the subject line with a period
|
||||
. In the body of the commit message, explain how things worked before this commit, what has changed, and how things work now
|
||||
. Include `Closes gh-<issue-number>` at the end if this fixes a GitHub issue
|
||||
. Avoid markdown, including back-ticks identifying code
|
||||
|
||||
Example:
|
||||
|
||||
----
|
||||
Short (50 chars or less) summary of changes
|
||||
|
||||
More detailed explanatory text, if necessary. Wrap it to about 72
|
||||
characters or so. In some contexts, the first line is treated as the
|
||||
subject of an email and the rest of the text as the body. The blank
|
||||
line separating the summary from the body is critical (unless you omit
|
||||
the body entirely); tools like rebase can get confused if you run the
|
||||
two together.
|
||||
|
||||
Further paragraphs come after blank lines.
|
||||
|
||||
- Bullet points are okay, too
|
||||
|
||||
- Typically a hyphen or asterisk is used for the bullet, preceded by a
|
||||
single space, with blank lines in between, but conventions vary here
|
||||
|
||||
Closes gh-123
|
||||
----
|
||||
|
||||
|
||||
= Run all tests prior to submission
|
||||
|
||||
----
|
||||
./gradlew clean build integrationTest
|
||||
----
|
||||
|
||||
= Submit your pull request
|
||||
|
||||
*Subject line:*
|
||||
|
||||
Follow the same conventions for pull request subject lines as mentioned above for commit message subject lines.
|
||||
|
||||
*In the body:*
|
||||
|
||||
. Explain your use case. What led you to submit this change? Why were existing mechanisms in the framework insufficient? Make a case that this is a general-purpose problem and that yours is a general-purpose solution, etc
|
||||
. Add any additional information and ask questions; start a conversation, or continue one from GitHub Issues
|
||||
. Mention any GitHub Issues
|
||||
. Also mention that you have submitted the CLA as described above
|
||||
Note that for pull requests containing a single commit, GitHub will default the subject line and body of the pull request to match the subject line and body of the commit message. This is fine, but please also include the items above in the body of the request.
|
||||
|
||||
= Mention your pull request on the associated GitHub issue
|
||||
|
||||
Add a comment to the associated GitHub issue(s) linking to your new pull request.
|
||||
|
||||
= Expect discussion and rework
|
||||
|
||||
The Spring team takes a very conservative approach to accepting contributions to the framework. This is to keep code quality and stability as high as possible, and to keep complexity at a minimum. Your changes, if accepted, may be heavily modified prior to merging. You will retain "Author:" attribution for your Git commits granted that the bulk of your changes remain intact. You may be asked to rework the submission for style (as explained above) and/or substance. Again, we strongly recommend discussing any serious submissions with the Spring Framework team prior to engaging in serious development work.
|
||||
|
||||
Note that you can always force push (`git push -f`) reworked / rebased commits against the branch used to submit your pull request. i.e. you do not need to issue a new pull request when asked to make changes.
|
||||
|
||||
+13
-26
@@ -1,13 +1,13 @@
|
||||
image::https://badges.gitter.im/Join%20Chat.svg[Gitter,link=https://gitter.im/spring-projects/spring-security?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge]
|
||||
|
||||
image:https://github.com/spring-projects/spring-security/actions/workflows/continuous-integration-workflow.yml/badge.svg?branch=main["Build Status", link="https://github.com/spring-projects/spring-security/actions/workflows/continuous-integration-workflow.yml"]
|
||||
image:https://github.com/spring-projects/spring-security/workflows/CI/badge.svg?branch=main["Build Status", link="https://github.com/spring-projects/spring-security/actions?query=workflow%3ACI"]
|
||||
|
||||
image:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Develocity", link="https://ge.spring.io/scans?search.rootProjectNames=spring-security"]
|
||||
image:https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?logo=Gradle&labelColor=02303A["Revved up by Gradle Enterprise", link="https://ge.spring.io/scans?search.rootProjectNames=spring-security"]
|
||||
|
||||
= Spring Security
|
||||
|
||||
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.
|
||||
Spring Security provides security services for the https://docs.spring.io[Spring IO Platform]. Spring Security 5.0 requires Spring 5.0 as
|
||||
a minimum and also requires Java 8.
|
||||
|
||||
For a detailed list of features and access to the latest release, please visit https://spring.io/projects[Spring projects].
|
||||
|
||||
@@ -15,16 +15,14 @@ For a detailed list of features and access to the latest release, please visit h
|
||||
Please see our https://github.com/spring-projects/.github/blob/main/CODE_OF_CONDUCT.md[code of conduct]
|
||||
|
||||
== Downloading Artifacts
|
||||
See https://docs.spring.io/spring-security/reference/getting-spring-security.html[Getting Spring Security] for how to obtain Spring Security.
|
||||
See https://docs.spring.io/spring-security/site/docs/current/reference/html5/#getting[Getting Spring Security] for how to obtain Spring Security.
|
||||
|
||||
== Documentation
|
||||
Be sure to read the https://docs.spring.io/spring-security/reference/[Spring Security Reference].
|
||||
Be sure to read the https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/[Spring Security Reference].
|
||||
Extensive JavaDoc for the Spring Security code is also available in the https://docs.spring.io/spring-security/site/docs/current/api/[Spring Security API Documentation].
|
||||
|
||||
You may also want to check out https://docs.spring.io/spring-security/reference/whats-new.html[what's new in the latest release].
|
||||
|
||||
== Quick Start
|
||||
See https://docs.spring.io/spring-security/reference/servlet/getting-started.html[Hello Spring Security] to get started with a "Hello, World" application.
|
||||
See https://docs.spring.io/spring-security/site/docs/current/reference/html5/#servlet-hello[Hello Spring Security] to get started with a "Hello, World" application.
|
||||
|
||||
== Building from Source
|
||||
Spring Security uses a https://gradle.org[Gradle]-based build system.
|
||||
@@ -32,9 +30,9 @@ In the instructions below, https://vimeo.com/34436402[`./gradlew`] is invoked fr
|
||||
a cross-platform, self-contained bootstrap mechanism for the build.
|
||||
|
||||
=== 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].
|
||||
https://help.github.com/set-up-git-redirect[Git] and the https://www.oracle.com/technetwork/java/javase/downloads[JDK11 build].
|
||||
|
||||
Be sure that your `JAVA_HOME` environment variable points to the `jdk-17` folder extracted from the JDK download.
|
||||
Be sure that your `JAVA_HOME` environment variable points to the `jdk-11` folder extracted from the JDK download.
|
||||
|
||||
=== Check out sources
|
||||
[indent=0]
|
||||
@@ -42,38 +40,27 @@ Be sure that your `JAVA_HOME` environment variable points to the `jdk-17` folder
|
||||
git clone git@github.com:spring-projects/spring-security.git
|
||||
----
|
||||
|
||||
=== Install all `spring-*.jar` into your local Maven repository.
|
||||
|
||||
=== Install all spring-\* jars into your local Maven cache
|
||||
[indent=0]
|
||||
----
|
||||
./gradlew publishToMavenLocal
|
||||
----
|
||||
|
||||
=== Compile and test; build all JARs, distribution zips, and docs
|
||||
|
||||
=== Compile and test; build all jars, distribution zips, and docs
|
||||
[indent=0]
|
||||
----
|
||||
./gradlew build
|
||||
----
|
||||
|
||||
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:
|
||||
|
||||
----
|
||||
./gradlew :spring-security-docs:antora
|
||||
----
|
||||
|
||||
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.
|
||||
|
||||
Discover more commands with `./gradlew tasks`.
|
||||
See also the https://github.com/spring-projects/spring-framework/wiki/Gradle-build-and-release-FAQ[Gradle build and release FAQ].
|
||||
|
||||
== 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.
|
||||
|
||||
== 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.
|
||||
https://help.github.com/articles/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.
|
||||
|
||||
== License
|
||||
Spring Security is Open Source software released under the
|
||||
|
||||
+106
-188
@@ -1,266 +1,184 @@
|
||||
= Release Process
|
||||
= Update Dependencies
|
||||
|
||||
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.
|
||||
Ensure you have no changes in your local repository.
|
||||
Change to a new branch.
|
||||
For example:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew checkMilestoneHasOpenIssues -PnextVersion=<next-version> -PgitHubAccessToken=<github-personal-access-token>
|
||||
$ git checkout -b 5.5.0-RC1-dependencies
|
||||
----
|
||||
|
||||
Alternatively, you can manually check using the https://github.com/spring-projects/spring-security/milestones[milestones] page.
|
||||
Review the rules in build.gradle to ensure the rules make sense.
|
||||
For example, we should not allow major version updates in a patch release.
|
||||
Also ensure that all of the exclusions still make sense.
|
||||
|
||||
[#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:
|
||||
The following Gradle command will update your dependencies creating a commit for each dependency update.
|
||||
The first invocation of the command will take quite a while (~20 minutes depending on internet speed) to run because it is indexing all the versions of all the dependencies.
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
git tag 5.5.0
|
||||
$ ./gradlew updateDependencies
|
||||
----
|
||||
|
||||
[#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:
|
||||
Review the commits to ensure that the updated dependency versions make sense for this release. For example, we should not perform a major version update for a patch release.
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
git push --atomic origin main 5.5.0
|
||||
$ git log
|
||||
----
|
||||
|
||||
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:
|
||||
If any of the versions don’t make sense, update `build.gradle` to ensure that the version is excluded.
|
||||
|
||||
Run all the checks:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./scripts/release/wait-for-done.sh 5.5.0
|
||||
$ ./gradlew check
|
||||
----
|
||||
|
||||
[#build-locally]
|
||||
== Build
|
||||
If they don’t work, you can run a git bisect to discover what broke the build.
|
||||
Fix any commits that broke the 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:
|
||||
Check out the original brach:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew check
|
||||
$ git checkout -
|
||||
----
|
||||
|
||||
[#update-release-notes-on-github]
|
||||
== Update release notes on GitHub
|
||||
The following command will update the dependencies again but this time creating a ticket for each update and placing `Closes gh-<number>` in the commit. Replacing the following values:
|
||||
|
||||
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)
|
||||
* <github-personal-access-token> - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `public_repo`
|
||||
* <next-version> - Replace with the title of the milestone you are releasing now (i.e. 5.5.0-RC1)
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew generateChangelog -PnextVersion=<next-version>
|
||||
$ ./gradlew updateDependencies -PupdateMode=GITHUB_ISSUE -PgitHubAccessToken=<github-personal-access-token> -PnextVersion=<next-version>
|
||||
----
|
||||
|
||||
Then copy the release notes to your clipboard (your mileage may vary with the following command):
|
||||
Apply any fixes from your previous branch that were necessary.
|
||||
|
||||
= Check All Issues are Closed
|
||||
|
||||
The following command will check if there are any open issues for the ticket.
|
||||
Before running the command, replace the following values:
|
||||
|
||||
* <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.
|
||||
* <next-version> - Replace with the title of the milestone you are releasing now (i.e. 5.5.0-RC1)
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
cat build/changelog/release-notes.md | xclip -selection clipboard
|
||||
$ ./gradlew gitHubCheckMilestoneHasNoOpenIssues -PgitHubAccessToken=<github-personal-access-token> -PnextVersion=<next-version>
|
||||
----
|
||||
|
||||
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 manually check using https://github.com/spring-projects/spring-security/milestones
|
||||
|
||||
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:
|
||||
= Update Release Version
|
||||
|
||||
* `<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`
|
||||
Update the version number in `gradle.properties` for the release, for example `5.5.0-M1`, `5.5.0-RC1`, `5.5.0`
|
||||
|
||||
= Update Antora Version
|
||||
|
||||
You will need to update the antora.yml version.
|
||||
If you are unsure of what the values should be, the following task will instruct you what the expected values are:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew createGitHubRelease -PnextVersion=<next-version> -Pbranch=<branch> -PcreateRelease=true -PgitHubAccessToken=<github-personal-access-token>
|
||||
./gradlew :spring-security-docs:antoraCheckVersion
|
||||
----
|
||||
|
||||
[#update-version-on-project-page]
|
||||
== Update version on project page
|
||||
= Build Locally
|
||||
|
||||
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]
|
||||
Run the build using
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew createSaganRelease deleteSaganRelease -PnextVersion=<next-version> -PpreviousVersion=<previous-version> -PgitHubAccessToken=<github-personal-access-token>
|
||||
$ ./gradlew check
|
||||
----
|
||||
|
||||
Alternatively, you can log into Contentful and update the versions manually on the Spring Security project page.
|
||||
= Push the Release Commit
|
||||
|
||||
[#close-create-milestone]
|
||||
== Close / Create milestone
|
||||
Push the commit and GitHub actions will build and deploy the artifacts
|
||||
If you are pushing to Maven Central, then you can get notified when it’s uploaded by running the following:
|
||||
|
||||
The release milestone will be automatically closed once the release is complete.
|
||||
To proceed manually, perform the following steps:
|
||||
[source,bash]
|
||||
----
|
||||
$ ./scripts/release/wait-for-done.sh 5.5.0
|
||||
----
|
||||
|
||||
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
|
||||
= Announce the release on Slack
|
||||
|
||||
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.
|
||||
* Announce via Slack on
|
||||
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
|
||||
spring-security-announcing 5.5.0 is available.
|
||||
....
|
||||
|
||||
[#update-to-next-development-version]
|
||||
== Update to next development version
|
||||
= Tag the release
|
||||
|
||||
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.
|
||||
* Tag the release and then push the tag
|
||||
|
||||
[#announce-release-on-other-channels]
|
||||
== Announce release on other channels
|
||||
....
|
||||
git tag 5.4.0-RC1
|
||||
git push origin 5.4.0-RC1
|
||||
....
|
||||
|
||||
* Create a blog post on Contentful
|
||||
* Tweet from https://twitter.com/springsecurity[@SpringSecurity]
|
||||
== 7. Update to Next Development Version
|
||||
|
||||
[[frequently-asked-questions]]
|
||||
== Frequently Asked Questions
|
||||
* Update `gradle.properties` version to next `+SNAPSHOT+` version, update antora.yml, and then push
|
||||
|
||||
*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.
|
||||
== 8. Update version on project page
|
||||
|
||||
*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).
|
||||
The following command will update https://spring.io/projects/spring-security#learn with the new release version using the following parameters
|
||||
|
||||
[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.
|
||||
<github-personal-access-token> - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `read:org` as https://spring.io/restdocs/index.html#authentication[documented for spring.io api]
|
||||
<next-version> - Replace with the milestone you are releasing now (i.e. 5.5.0-RC1)
|
||||
<previous-version> - Replace with the previous release which will be removed from the listed versions (i.e. 5.5.0-M3)
|
||||
|
||||
The automated release process occurs on the following branches:
|
||||
[source,bash]
|
||||
----
|
||||
$ ./gradlew saganCreateRelease saganDeleteRelease -PgitHubAccessToken=<github-personal-access-token> -PnextVersion=<next-version> -PpreviousVersion=<previous-version>
|
||||
----
|
||||
|
||||
* `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.
|
||||
== 9. Update Release Notes on GitHub
|
||||
|
||||
[IMPORTANT]
|
||||
You should ensure all issues are closed or moved to another milestone prior to a scheduled release.
|
||||
Generate the Release Notes replacing:
|
||||
|
||||
If the above checks pass, the version number is updated (in `gradle.properties`) and a commit is pushed to trigger the CI process.
|
||||
* <next-version> - Replace with the milestone you are releasing now (i.e. 5.5.0-RC1)
|
||||
|
||||
*How do I trigger a release manually?* You can trigger a release manually in two ways:
|
||||
----
|
||||
$ ./gradlew generateChangelog -PnextVersion=<next-version>
|
||||
----
|
||||
|
||||
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.
|
||||
* Copy the release notes to your clipboard (your mileage may vary with
|
||||
the following command)
|
||||
|
||||
....
|
||||
cat build/changelog/release-notes.md | xclip -selection clipboard
|
||||
....
|
||||
|
||||
* Create the
|
||||
https://github.com/spring-projects/spring-security/releases[release on
|
||||
GitHub], associate it with the tag, and paste the generated notes
|
||||
|
||||
== 10. Close / Create Milestone
|
||||
|
||||
* In
|
||||
https://github.com/spring-projects/spring-security/milestones[GitHub
|
||||
Milestones], create a new milestone for the next release version
|
||||
* Move any open issues from the existing milestone you just released to
|
||||
the new milestone
|
||||
* Close the milestone for the release.
|
||||
|
||||
== 11. Announce the release on other channels
|
||||
|
||||
* Create a https://spring.io/admin/blog[Blog]
|
||||
* Tweet from [@SpringSecurity](https://twitter.com/springsecurity)
|
||||
|
||||
*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,53 +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;
|
||||
|
||||
/**
|
||||
* Simply extends {@link TargetObject} so we have a different object to put configuration
|
||||
* attributes against.
|
||||
* <P>
|
||||
* There is no different behaviour. We have to define each method so that
|
||||
* <code>Class.getMethod(methodName, args)</code> returns a <code>Method</code>
|
||||
* referencing this class rather than the parent class.
|
||||
* </p>
|
||||
* <P>
|
||||
* We need to implement <code>ITargetObject</code> again because the
|
||||
* <code>MethodDefinitionAttributes</code> only locates attributes on interfaces
|
||||
* explicitly defined by the intercepted class (not the interfaces defined by its parent
|
||||
* class or classes).
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class OtherTargetObject extends TargetObject implements ITargetObject {
|
||||
|
||||
@Override
|
||||
public String makeLowerCase(String input) {
|
||||
return super.makeLowerCase(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String makeUpperCase(String input) {
|
||||
return super.makeUpperCase(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String publicMakeLowerCase(String input) {
|
||||
return super.publicMakeLowerCase(input);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
/**
|
||||
* Represents a secured object.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class TargetObject implements ITargetObject {
|
||||
|
||||
@Override
|
||||
public Integer computeHashCode(String input) {
|
||||
return input.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int countLength(String input) {
|
||||
return input.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lowercase string, followed by security environment information.
|
||||
* @param input the message to make lowercase
|
||||
* @return the lowercase message, a space, the <code>Authentication</code> class that
|
||||
* was on the <code>SecurityContext</code> at the time of method invocation, and a
|
||||
* boolean indicating if the <code>Authentication</code> object is authenticated or
|
||||
* not
|
||||
*/
|
||||
@Override
|
||||
public String makeLowerCase(String input) {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (auth == null) {
|
||||
return input.toLowerCase() + " Authentication empty";
|
||||
}
|
||||
else {
|
||||
return input.toLowerCase() + " " + auth.getClass().getName() + " " + auth.isAuthenticated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the uppercase string, followed by security environment information.
|
||||
* @param input the message to make uppercase
|
||||
* @return the uppercase message, a space, the <code>Authentication</code> class that
|
||||
* was on the <code>SecurityContext</code> at the time of method invocation, and a
|
||||
* boolean indicating if the <code>Authentication</code> object is authenticated or
|
||||
* not
|
||||
*/
|
||||
@Override
|
||||
public String makeUpperCase(String input) {
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
return input.toUpperCase() + " " + auth.getClass().getName() + " " + auth.isAuthenticated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegates through to the {@link #makeLowerCase(String)} method.
|
||||
* @param input the message to be made lower-case
|
||||
*/
|
||||
@Override
|
||||
public String publicMakeLowerCase(String input) {
|
||||
return this.makeLowerCase(input);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,8 @@ dependencies {
|
||||
api 'org.springframework:spring-jdbc'
|
||||
api 'org.springframework:spring-tx'
|
||||
|
||||
optional 'net.sf.ehcache:ehcache'
|
||||
|
||||
testImplementation "org.assertj:assertj-core"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params"
|
||||
@@ -20,5 +22,4 @@ dependencies {
|
||||
testImplementation "org.springframework:spring-test"
|
||||
|
||||
testRuntimeOnly 'org.hsqldb:hsqldb'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
+1
-5
@@ -71,7 +71,7 @@ import org.springframework.util.StringUtils;
|
||||
* <tt>AclEntryVoter</tt>:
|
||||
* <ul>
|
||||
* <li>Process domain object class <code>BankAccount</code>, configuration attribute
|
||||
* <code>VOTE_ACL_BANK_ACCOUNT_READ</code>, require permission
|
||||
* <code>VOTE_ACL_BANK_ACCONT_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
|
||||
@@ -96,11 +96,7 @@ import org.springframework.util.StringUtils;
|
||||
* 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);
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -118,7 +118,8 @@ public class AclPermissionEvaluator implements PermissionEvaluator {
|
||||
if (permission instanceof Permission[]) {
|
||||
return Arrays.asList((Permission[]) permission);
|
||||
}
|
||||
if (permission instanceof String permString) {
|
||||
if (permission instanceof String) {
|
||||
String permString = (String) permission;
|
||||
Permission p = buildPermission(permString);
|
||||
if (p != null) {
|
||||
return Arrays.asList(p);
|
||||
|
||||
-5
@@ -20,7 +20,6 @@ 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;
|
||||
@@ -40,11 +39,7 @@ import org.springframework.util.ObjectUtils;
|
||||
* 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;
|
||||
-5
@@ -26,7 +26,6 @@ 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;
|
||||
@@ -63,11 +62,7 @@ import org.springframework.security.core.Authentication;
|
||||
*
|
||||
* @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);
|
||||
-6
@@ -27,7 +27,6 @@ import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
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;
|
||||
@@ -60,12 +59,7 @@ import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
* granted and <code>null</code> will be returned.
|
||||
* <p>
|
||||
* All comparisons and prefixes are case sensitive.
|
||||
*
|
||||
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
|
||||
* annotations may also prove useful, for example
|
||||
* {@code @PostAuthorize("hasPermission(filterObject, read)")}
|
||||
*/
|
||||
@Deprecated
|
||||
public class AclEntryAfterInvocationProvider extends AbstractAclProvider implements MessageSourceAware {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationProvider.class);
|
||||
-2
@@ -32,9 +32,7 @@ import org.springframework.core.log.LogMessage;
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Paulo Neves
|
||||
* @deprecated please see {@code PostFilter}
|
||||
*/
|
||||
@Deprecated
|
||||
class ArrayFilterer<T> implements Filterer<T> {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(ArrayFilterer.class);
|
||||
-2
@@ -31,9 +31,7 @@ import org.springframework.core.log.LogMessage;
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Paulo Neves
|
||||
* @deprecated please see {@code PostFilter}
|
||||
*/
|
||||
@Deprecated
|
||||
class CollectionFilterer<T> implements Filterer<T> {
|
||||
|
||||
protected static final Log logger = LogFactory.getLog(CollectionFilterer.class);
|
||||
-2
@@ -23,9 +23,7 @@ import java.util.Iterator;
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Paulo Neves
|
||||
* @deprecated please use {@code PreFilter} and {@code @PostFilter} instead
|
||||
*/
|
||||
@Deprecated
|
||||
interface Filterer<T> extends Iterable<T> {
|
||||
|
||||
/**
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -56,9 +56,10 @@ public abstract class AbstractPermission implements Permission {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof Permission other)) {
|
||||
if (!(obj instanceof Permission)) {
|
||||
return false;
|
||||
}
|
||||
Permission other = (Permission) obj;
|
||||
return (this.mask == other.getMask());
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
||||
+5
-38
@@ -17,22 +17,17 @@
|
||||
package org.springframework.security.acls.domain;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.acls.model.SidRetrievalStrategy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -51,9 +46,6 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
|
||||
|
||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
.getContextHolderStrategy();
|
||||
|
||||
private final GrantedAuthority gaGeneralChanges;
|
||||
|
||||
private final GrantedAuthority gaModifyAuditing;
|
||||
@@ -62,8 +54,6 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
|
||||
|
||||
private SidRetrievalStrategy sidRetrievalStrategy = new SidRetrievalStrategyImpl();
|
||||
|
||||
private RoleHierarchy roleHierarchy = new NullRoleHierarchy();
|
||||
|
||||
/**
|
||||
* Constructor. The only mandatory parameter relates to the system-wide
|
||||
* {@link GrantedAuthority} instances that can be held to always permit ACL changes.
|
||||
@@ -91,12 +81,12 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
|
||||
|
||||
@Override
|
||||
public void securityCheck(Acl acl, int changeType) {
|
||||
SecurityContext context = this.securityContextHolderStrategy.getContext();
|
||||
if ((context == null) || (context.getAuthentication() == null)
|
||||
|| !context.getAuthentication().isAuthenticated()) {
|
||||
if ((SecurityContextHolder.getContext() == null)
|
||||
|| (SecurityContextHolder.getContext().getAuthentication() == null)
|
||||
|| !SecurityContextHolder.getContext().getAuthentication().isAuthenticated()) {
|
||||
throw new AccessDeniedException("Authenticated principal required to operate with ACLs");
|
||||
}
|
||||
Authentication authentication = context.getAuthentication();
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
// Check if authorized by virtue of ACL ownership
|
||||
Sid currentUser = createCurrentUser(authentication);
|
||||
if (currentUser.equals(acl.getOwner())
|
||||
@@ -105,9 +95,7 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
|
||||
}
|
||||
|
||||
// Iterate this principal's authorities to determine right
|
||||
Collection<? extends GrantedAuthority> reachableGrantedAuthorities = this.roleHierarchy
|
||||
.getReachableGrantedAuthorities(authentication.getAuthorities());
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(reachableGrantedAuthorities);
|
||||
Set<String> authorities = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
|
||||
if (acl.getOwner() instanceof GrantedAuthoritySid
|
||||
&& authorities.contains(((GrantedAuthoritySid) acl.getOwner()).getGrantedAuthority())) {
|
||||
return;
|
||||
@@ -158,25 +146,4 @@ public class AclAuthorizationStrategyImpl implements AclAuthorizationStrategy {
|
||||
this.sidRetrievalStrategy = sidRetrievalStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link RoleHierarchy} to use. The default is to use a
|
||||
* {@link NullRoleHierarchy}
|
||||
* @since 6.4
|
||||
*/
|
||||
public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
|
||||
Assert.notNull(roleHierarchy, "roleHierarchy cannot be null");
|
||||
this.roleHierarchy = roleHierarchy;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ public class AclImpl implements Acl, MutableAcl, AuditableAcl, OwnershipAcl {
|
||||
public boolean isSidLoaded(List<Sid> sids) {
|
||||
// If loadedSides is null, this indicates all SIDs were loaded
|
||||
// Also return true if the caller didn't specify a SID to find
|
||||
if ((this.loadedSids == null) || (sids == null) || sids.isEmpty()) {
|
||||
if ((this.loadedSids == null) || (sids == null) || (sids.size() == 0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -140,7 +140,7 @@ public class DefaultPermissionFactory implements PermissionFactory {
|
||||
|
||||
@Override
|
||||
public List<Permission> buildFromNames(List<String> names) {
|
||||
if ((names == null) || names.isEmpty()) {
|
||||
if ((names == null) || (names.size() == 0)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<Permission> permissions = new ArrayList<>(names.size());
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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.domain;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import net.sf.ehcache.CacheException;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
import net.sf.ehcache.Element;
|
||||
|
||||
import org.springframework.security.acls.model.AclCache;
|
||||
import org.springframework.security.acls.model.MutableAcl;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.PermissionGrantingStrategy;
|
||||
import org.springframework.security.util.FieldUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link AclCache} that delegates to EH-CACHE.
|
||||
* <p>
|
||||
* Designed to handle the transient fields in {@link AclImpl}. Note that this
|
||||
* implementation assumes all {@link AclImpl} instances share the same
|
||||
* {@link PermissionGrantingStrategy} and {@link AclAuthorizationStrategy} instances.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated since 5.6. In favor of JCache based implementations
|
||||
*/
|
||||
@Deprecated
|
||||
public class EhCacheBasedAclCache implements AclCache {
|
||||
|
||||
private final Ehcache cache;
|
||||
|
||||
private PermissionGrantingStrategy permissionGrantingStrategy;
|
||||
|
||||
private AclAuthorizationStrategy aclAuthorizationStrategy;
|
||||
|
||||
public EhCacheBasedAclCache(Ehcache cache, PermissionGrantingStrategy permissionGrantingStrategy,
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy) {
|
||||
Assert.notNull(cache, "Cache required");
|
||||
Assert.notNull(permissionGrantingStrategy, "PermissionGrantingStrategy required");
|
||||
Assert.notNull(aclAuthorizationStrategy, "AclAuthorizationStrategy required");
|
||||
this.cache = cache;
|
||||
this.permissionGrantingStrategy = permissionGrantingStrategy;
|
||||
this.aclAuthorizationStrategy = aclAuthorizationStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictFromCache(Serializable pk) {
|
||||
Assert.notNull(pk, "Primary key (identifier) required");
|
||||
MutableAcl acl = getFromCache(pk);
|
||||
if (acl != null) {
|
||||
this.cache.remove(acl.getId());
|
||||
this.cache.remove(acl.getObjectIdentity());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictFromCache(ObjectIdentity objectIdentity) {
|
||||
Assert.notNull(objectIdentity, "ObjectIdentity required");
|
||||
MutableAcl acl = getFromCache(objectIdentity);
|
||||
if (acl != null) {
|
||||
this.cache.remove(acl.getId());
|
||||
this.cache.remove(acl.getObjectIdentity());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableAcl getFromCache(ObjectIdentity objectIdentity) {
|
||||
Assert.notNull(objectIdentity, "ObjectIdentity required");
|
||||
try {
|
||||
Element element = this.cache.get(objectIdentity);
|
||||
return (element != null) ? initializeTransientFields((MutableAcl) element.getValue()) : null;
|
||||
}
|
||||
catch (CacheException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableAcl getFromCache(Serializable pk) {
|
||||
Assert.notNull(pk, "Primary key (identifier) required");
|
||||
try {
|
||||
Element element = this.cache.get(pk);
|
||||
return (element != null) ? initializeTransientFields((MutableAcl) element.getValue()) : null;
|
||||
}
|
||||
catch (CacheException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putInCache(MutableAcl acl) {
|
||||
Assert.notNull(acl, "Acl required");
|
||||
Assert.notNull(acl.getObjectIdentity(), "ObjectIdentity required");
|
||||
Assert.notNull(acl.getId(), "ID required");
|
||||
if (this.aclAuthorizationStrategy == null) {
|
||||
if (acl instanceof AclImpl) {
|
||||
this.aclAuthorizationStrategy = (AclAuthorizationStrategy) FieldUtils
|
||||
.getProtectedFieldValue("aclAuthorizationStrategy", acl);
|
||||
this.permissionGrantingStrategy = (PermissionGrantingStrategy) FieldUtils
|
||||
.getProtectedFieldValue("permissionGrantingStrategy", acl);
|
||||
}
|
||||
}
|
||||
if ((acl.getParentAcl() != null) && (acl.getParentAcl() instanceof MutableAcl)) {
|
||||
putInCache((MutableAcl) acl.getParentAcl());
|
||||
}
|
||||
this.cache.put(new Element(acl.getObjectIdentity(), acl));
|
||||
this.cache.put(new Element(acl.getId(), acl));
|
||||
}
|
||||
|
||||
private MutableAcl initializeTransientFields(MutableAcl value) {
|
||||
if (value instanceof AclImpl) {
|
||||
FieldUtils.setProtectedFieldValue("aclAuthorizationStrategy", value, this.aclAuthorizationStrategy);
|
||||
FieldUtils.setProtectedFieldValue("permissionGrantingStrategy", value, this.permissionGrantingStrategy);
|
||||
}
|
||||
if (value.getParentAcl() != null) {
|
||||
initializeTransientFields((MutableAcl) value.getParentAcl());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCache() {
|
||||
this.cache.removeAll();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ public class SidRetrievalStrategyImpl implements SidRetrievalStrategy {
|
||||
@Override
|
||||
public List<Sid> getSids(Authentication authentication) {
|
||||
Collection<? extends GrantedAuthority> authorities = this.roleHierarchy
|
||||
.getReachableGrantedAuthorities(authentication.getAuthorities());
|
||||
.getReachableGrantedAuthorities(authentication.getAuthorities());
|
||||
List<Sid> sids = new ArrayList<>(authorities.size() + 1);
|
||||
sids.add(new PrincipalSid(authentication));
|
||||
for (GrantedAuthority authority : authorities) {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -65,11 +65,10 @@ import org.springframework.util.Assert;
|
||||
* NB: This implementation does attempt to provide reasonably optimised lookups - within
|
||||
* the constraints of a normalised database and standard ANSI SQL features. If you are
|
||||
* willing to sacrifice either of these constraints (e.g. use a particular database
|
||||
* feature such as hierarchical queries or materialized views, or reduce normalisation)
|
||||
* you are likely to achieve better performance. In such situations you will need to
|
||||
* provide your own custom <code>LookupStrategy</code>. This class does not support
|
||||
* subclassing, as it is likely to change in future releases and therefore subclassing is
|
||||
* unsupported.
|
||||
* feature such as hierarchical queries or materalized views, or reduce normalisation) you
|
||||
* are likely to achieve better performance. In such situations you will need to provide
|
||||
* your own custom <code>LookupStrategy</code>. This class does not support subclassing,
|
||||
* as it is likely to change in future releases and therefore subclassing is unsupported.
|
||||
* <p>
|
||||
* There are two SQL queries executed, one in the <tt>lookupPrimaryKeys</tt> method and
|
||||
* one in <tt>lookupObjectIdentities</tt>. These are built from the same select and "order
|
||||
@@ -580,7 +579,7 @@ public class BasicLookupStrategy implements LookupStrategy {
|
||||
Serializable identifier = (Serializable) rs.getObject("object_id_identity");
|
||||
identifier = BasicLookupStrategy.this.aclClassIdUtils.identifierFrom(identifier, rs);
|
||||
ObjectIdentity objectIdentity = BasicLookupStrategy.this.objectIdentityGenerator
|
||||
.createObjectIdentity(identifier, rs.getString("class"));
|
||||
.createObjectIdentity(identifier, rs.getString("class"));
|
||||
|
||||
Acl parentAcl = null;
|
||||
long parentAclId = rs.getLong("parent_object");
|
||||
|
||||
@@ -100,8 +100,8 @@ public class JdbcAclService implements AclService {
|
||||
@Override
|
||||
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
|
||||
Object[] args = { parentIdentity.getIdentifier().toString(), parentIdentity.getType() };
|
||||
List<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql,
|
||||
(rs, rowNum) -> mapObjectIdentityRow(rs), args);
|
||||
List<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql, args,
|
||||
(rs, rowNum) -> mapObjectIdentityRow(rs));
|
||||
return (!objects.isEmpty()) ? objects : null;
|
||||
}
|
||||
|
||||
|
||||
+5
-19
@@ -40,7 +40,6 @@ import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -65,9 +64,6 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
|
||||
private static final String DEFAULT_INSERT_INTO_ACL_CLASS_WITH_ID = "insert into acl_class (class, class_id_type) values (?, ?)";
|
||||
|
||||
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
|
||||
.getContextHolderStrategy();
|
||||
|
||||
private boolean foreignKeysInDatabase = true;
|
||||
|
||||
private final AclCache aclCache;
|
||||
@@ -119,7 +115,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
|
||||
// Need to retrieve the current principal, in order to know who "owns" this ACL
|
||||
// (can be changed later on)
|
||||
Authentication auth = this.securityContextHolderStrategy.getContext().getAuthentication();
|
||||
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||
PrincipalSid sid = new PrincipalSid(auth);
|
||||
|
||||
// Create the acl_object_identity row
|
||||
@@ -190,7 +186,8 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
* @return the primary key or null if not found
|
||||
*/
|
||||
protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate, Class idType) {
|
||||
List<Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, Long.class, type);
|
||||
List<Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, new Object[] { type },
|
||||
Long.class);
|
||||
|
||||
if (!classIds.isEmpty()) {
|
||||
return classIds.get(0);
|
||||
@@ -241,8 +238,8 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
* @return the primary key or null if not found
|
||||
*/
|
||||
protected Long createOrRetrieveSidPrimaryKey(String sidName, boolean sidIsPrincipal, boolean allowCreate) {
|
||||
List<Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey, Long.class, sidIsPrincipal,
|
||||
sidName);
|
||||
List<Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey,
|
||||
new Object[] { sidIsPrincipal, sidName }, Long.class);
|
||||
if (!sidIds.isEmpty()) {
|
||||
return sidIds.get(0);
|
||||
}
|
||||
@@ -476,15 +473,4 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -37,7 +37,7 @@ public class AclFormattingUtilsTests {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> AclFormattingUtils.demergePatterns(null, "SOME STRING"));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> AclFormattingUtils.demergePatterns("SOME STRING", null));
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> AclFormattingUtils.demergePatterns("SOME STRING", "LONGER SOME STRING"));
|
||||
.isThrownBy(() -> AclFormattingUtils.demergePatterns("SOME STRING", "LONGER SOME STRING"));
|
||||
assertThatNoException().isThrownBy(() -> AclFormattingUtils.demergePatterns("SOME STRING", "SAME LENGTH"));
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ public class AclFormattingUtilsTests {
|
||||
String original = "...........................A...R";
|
||||
String removeBits = "...............................R";
|
||||
assertThat(AclFormattingUtils.demergePatterns(original, removeBits))
|
||||
.isEqualTo("...........................A....");
|
||||
.isEqualTo("...........................A....");
|
||||
assertThat(AclFormattingUtils.demergePatterns("ABCDEF", "......")).isEqualTo("ABCDEF");
|
||||
assertThat(AclFormattingUtils.demergePatterns("ABCDEF", "GHIJKL")).isEqualTo("......");
|
||||
}
|
||||
@@ -56,7 +56,7 @@ public class AclFormattingUtilsTests {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> AclFormattingUtils.mergePatterns(null, "SOME STRING"));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> AclFormattingUtils.mergePatterns("SOME STRING", null));
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> AclFormattingUtils.mergePatterns("SOME STRING", "LONGER SOME STRING"));
|
||||
.isThrownBy(() -> AclFormattingUtils.mergePatterns("SOME STRING", "LONGER SOME STRING"));
|
||||
assertThatNoException().isThrownBy(() -> AclFormattingUtils.mergePatterns("SOME STRING", "SAME LENGTH"));
|
||||
}
|
||||
|
||||
@@ -73,9 +73,9 @@ public class AclFormattingUtilsTests {
|
||||
public final void testBinaryPrints() {
|
||||
assertThat(AclFormattingUtils.printBinary(15)).isEqualTo("............................****");
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> AclFormattingUtils.printBinary(15, Permission.RESERVED_ON));
|
||||
.isThrownBy(() -> AclFormattingUtils.printBinary(15, Permission.RESERVED_ON));
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> AclFormattingUtils.printBinary(15, Permission.RESERVED_OFF));
|
||||
.isThrownBy(() -> AclFormattingUtils.printBinary(15, Permission.RESERVED_OFF));
|
||||
assertThat(AclFormattingUtils.printBinary(15, 'x')).isEqualTo("............................xxxx");
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -34,7 +34,7 @@ import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
@@ -68,7 +68,7 @@ public class AclPermissionCacheOptimizerTests {
|
||||
pco.setObjectIdentityRetrievalStrategy(oids);
|
||||
pco.setSidRetrievalStrategy(sids);
|
||||
pco.cachePermissionsFor(mock(Authentication.class), Collections.emptyList());
|
||||
verifyNoMoreInteractions(service, sids, oids);
|
||||
verifyZeroInteractions(service, sids, oids);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -85,8 +85,7 @@ public class AclEntryAfterInvocationCollectionFilteringProviderTests {
|
||||
AclEntryAfterInvocationCollectionFilteringProvider provider = new AclEntryAfterInvocationCollectionFilteringProvider(
|
||||
service, Arrays.asList(mock(Permission.class)));
|
||||
assertThat(provider.decide(mock(Authentication.class), new Object(),
|
||||
SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), null))
|
||||
.isNull();
|
||||
SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), null)).isNull();
|
||||
verify(service, never()).readAclById(any(ObjectIdentity.class), any(List.class));
|
||||
}
|
||||
|
||||
+7
-8
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -54,7 +54,7 @@ public class AclEntryAfterInvocationProviderTests {
|
||||
@Test
|
||||
public void rejectsMissingPermissions() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new AclEntryAfterInvocationProvider(mock(AclService.class), null));
|
||||
.isThrownBy(() -> new AclEntryAfterInvocationProvider(mock(AclService.class), null));
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> new AclEntryAfterInvocationProvider(mock(AclService.class), Collections.<Permission>emptyList()));
|
||||
}
|
||||
@@ -112,12 +112,12 @@ public class AclEntryAfterInvocationProviderTests {
|
||||
provider.setProcessDomainObjectClass(Object.class);
|
||||
provider.setSidRetrievalStrategy(mock(SidRetrievalStrategy.class));
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(() -> provider.decide(mock(Authentication.class), new Object(),
|
||||
SecurityConfig.createList("UNSUPPORTED", "MY_ATTRIBUTE"), new Object()));
|
||||
.isThrownBy(() -> provider.decide(mock(Authentication.class), new Object(),
|
||||
SecurityConfig.createList("UNSUPPORTED", "MY_ATTRIBUTE"), new Object()));
|
||||
// Second scenario with no acls found
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(() -> provider.decide(mock(Authentication.class), new Object(),
|
||||
SecurityConfig.createList("UNSUPPORTED", "MY_ATTRIBUTE"), new Object()));
|
||||
.isThrownBy(() -> provider.decide(mock(Authentication.class), new Object(),
|
||||
SecurityConfig.createList("UNSUPPORTED", "MY_ATTRIBUTE"), new Object()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -126,8 +126,7 @@ public class AclEntryAfterInvocationProviderTests {
|
||||
AclEntryAfterInvocationProvider provider = new AclEntryAfterInvocationProvider(service,
|
||||
Arrays.asList(mock(Permission.class)));
|
||||
assertThat(provider.decide(mock(Authentication.class), new Object(),
|
||||
SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), null))
|
||||
.isNull();
|
||||
SecurityConfig.createList("AFTER_ACL_COLLECTION_READ"), null)).isNull();
|
||||
verify(service, never()).readAclById(any(ObjectIdentity.class), any(List.class));
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -77,14 +77,14 @@ public class AccessControlImplEntryTests {
|
||||
assertThat(ace).isNotNull();
|
||||
assertThat(ace).isNotEqualTo(100L);
|
||||
assertThat(ace).isEqualTo(ace);
|
||||
assertThat(ace)
|
||||
.isEqualTo(new AccessControlEntryImpl(1L, mockAcl, sid, BasePermission.ADMINISTRATION, true, true, true));
|
||||
assertThat(ace).isEqualTo(
|
||||
new AccessControlEntryImpl(1L, mockAcl, sid, BasePermission.ADMINISTRATION, true, true, true));
|
||||
assertThat(ace).isNotEqualTo(
|
||||
new AccessControlEntryImpl(2L, mockAcl, sid, BasePermission.ADMINISTRATION, true, true, true));
|
||||
assertThat(ace).isNotEqualTo(new AccessControlEntryImpl(1L, mockAcl, new PrincipalSid("scott"),
|
||||
BasePermission.ADMINISTRATION, true, true, true));
|
||||
assertThat(ace)
|
||||
.isNotEqualTo(new AccessControlEntryImpl(1L, mockAcl, sid, BasePermission.WRITE, true, true, true));
|
||||
.isNotEqualTo(new AccessControlEntryImpl(1L, mockAcl, sid, BasePermission.WRITE, true, true, true));
|
||||
assertThat(ace).isNotEqualTo(
|
||||
new AccessControlEntryImpl(1L, mockAcl, sid, BasePermission.ADMINISTRATION, false, true, true));
|
||||
assertThat(ace).isNotEqualTo(
|
||||
|
||||
+2
-33
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,19 +25,13 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
@@ -46,14 +40,9 @@ import static org.mockito.Mockito.verify;
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class AclAuthorizationStrategyImplTests {
|
||||
|
||||
SecurityContext context;
|
||||
|
||||
@Mock
|
||||
Acl acl;
|
||||
|
||||
@Mock
|
||||
SecurityContextHolderStrategy securityContextHolderStrategy;
|
||||
|
||||
GrantedAuthority authority;
|
||||
|
||||
AclAuthorizationStrategyImpl strategy;
|
||||
@@ -64,8 +53,7 @@ public class AclAuthorizationStrategyImplTests {
|
||||
TestingAuthenticationToken authentication = new TestingAuthenticationToken("foo", "bar",
|
||||
Arrays.asList(this.authority));
|
||||
authentication.setAuthenticated(true);
|
||||
this.context = new SecurityContextImpl(authentication);
|
||||
SecurityContextHolder.setContext(this.context);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
@@ -88,25 +76,6 @@ public class AclAuthorizationStrategyImplTests {
|
||||
this.strategy.securityCheck(this.acl, AclAuthorizationStrategy.CHANGE_GENERAL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securityCheckWhenRoleReachableByHierarchyThenAuthorized() {
|
||||
given(this.acl.getOwner()).willReturn(new GrantedAuthoritySid("ROLE_AUTH_B"));
|
||||
this.strategy = new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_SYSTEM_ADMIN"));
|
||||
this.strategy.setRoleHierarchy(RoleHierarchyImpl.fromHierarchy("ROLE_AUTH > ROLE_AUTH_B"));
|
||||
assertThatNoException()
|
||||
.isThrownBy(() -> this.strategy.securityCheck(this.acl, AclAuthorizationStrategy.CHANGE_GENERAL));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securityCheckWhenCustomSecurityContextHolderStrategyThenUses() {
|
||||
given(this.securityContextHolderStrategy.getContext()).willReturn(this.context);
|
||||
given(this.acl.getOwner()).willReturn(new GrantedAuthoritySid("ROLE_AUTH"));
|
||||
this.strategy = new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_SYSTEM_ADMIN"));
|
||||
this.strategy.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
|
||||
this.strategy.securityCheck(this.acl, AclAuthorizationStrategy.CHANGE_GENERAL);
|
||||
verify(this.securityContextHolderStrategy).getContext();
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
class CustomAuthority implements GrantedAuthority {
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -103,7 +103,7 @@ public class AclImplTests {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> new AclImpl(null, 1, this.authzStrategy, this.pgs, null, null, true, new PrincipalSid("joe")));
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new AclImpl(null, 1, this.authzStrategy, this.mockAuditLogger));
|
||||
.isThrownBy(() -> new AclImpl(null, 1, this.authzStrategy, this.mockAuditLogger));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -111,7 +111,7 @@ public class AclImplTests {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new AclImpl(this.objectIdentity, null, this.authzStrategy,
|
||||
this.pgs, null, null, true, new PrincipalSid("joe")));
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new AclImpl(this.objectIdentity, null, this.authzStrategy, this.mockAuditLogger));
|
||||
.isThrownBy(() -> new AclImpl(this.objectIdentity, null, this.authzStrategy, this.mockAuditLogger));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -120,7 +120,7 @@ public class AclImplTests {
|
||||
new DefaultPermissionGrantingStrategy(this.mockAuditLogger), null, null, true,
|
||||
new PrincipalSid("joe")));
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new AclImpl(this.objectIdentity, 1, null, this.mockAuditLogger));
|
||||
.isThrownBy(() -> new AclImpl(this.objectIdentity, 1, null, this.mockAuditLogger));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -128,7 +128,7 @@ public class AclImplTests {
|
||||
MutableAcl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, this.pgs, null, null, true,
|
||||
new PrincipalSid("joe"));
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> acl.insertAce(0, null, new GrantedAuthoritySid("ROLE_IGNORED"), true));
|
||||
.isThrownBy(() -> acl.insertAce(0, null, new GrantedAuthoritySid("ROLE_IGNORED"), true));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> acl.insertAce(0, BasePermission.READ, null, true));
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ public class AclImplTests {
|
||||
acl.insertAce(0, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST1"), true);
|
||||
service.updateAcl(acl);
|
||||
assertThatExceptionOfType(NotFoundException.class)
|
||||
.isThrownBy(() -> acl.insertAce(55, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST2"), true));
|
||||
.isThrownBy(() -> acl.insertAce(55, BasePermission.READ, new GrantedAuthoritySid("ROLE_TEST2"), true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -223,7 +223,7 @@ public class AclImplTests {
|
||||
new PrincipalSid("joe"));
|
||||
Sid ben = new PrincipalSid("ben");
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> acl.isGranted(new ArrayList<>(0), Arrays.asList(ben), false));
|
||||
.isThrownBy(() -> acl.isGranted(new ArrayList<>(0), Arrays.asList(ben), false));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> acl.isGranted(READ, new ArrayList<>(0), false));
|
||||
}
|
||||
|
||||
@@ -246,14 +246,12 @@ public class AclImplTests {
|
||||
List<Sid> sids = Arrays.asList(new PrincipalSid("ben"), new GrantedAuthoritySid("ROLE_GUEST"));
|
||||
assertThat(rootAcl.isGranted(permissions, sids, false)).isFalse();
|
||||
assertThatExceptionOfType(NotFoundException.class)
|
||||
.isThrownBy(() -> rootAcl.isGranted(permissions, SCOTT, false));
|
||||
.isThrownBy(() -> rootAcl.isGranted(permissions, SCOTT, false));
|
||||
assertThat(rootAcl.isGranted(WRITE, SCOTT, false)).isTrue();
|
||||
assertThat(rootAcl.isGranted(WRITE,
|
||||
Arrays.asList(new PrincipalSid("rod"), new GrantedAuthoritySid("WRITE_ACCESS_ROLE")), false))
|
||||
.isFalse();
|
||||
Arrays.asList(new PrincipalSid("rod"), new GrantedAuthoritySid("WRITE_ACCESS_ROLE")), false)).isFalse();
|
||||
assertThat(rootAcl.isGranted(WRITE,
|
||||
Arrays.asList(new GrantedAuthoritySid("WRITE_ACCESS_ROLE"), new PrincipalSid("rod")), false))
|
||||
.isTrue();
|
||||
Arrays.asList(new GrantedAuthoritySid("WRITE_ACCESS_ROLE"), new PrincipalSid("rod")), false)).isTrue();
|
||||
// Change the type of the Sid and check the granting process
|
||||
assertThatExceptionOfType(NotFoundException.class).isThrownBy(() -> rootAcl.isGranted(WRITE,
|
||||
Arrays.asList(new GrantedAuthoritySid("rod"), new PrincipalSid("WRITE_ACCESS_ROLE")), false));
|
||||
@@ -294,7 +292,7 @@ public class AclImplTests {
|
||||
// Check granting process for parent1
|
||||
assertThat(parentAcl1.isGranted(READ, SCOTT, false)).isTrue();
|
||||
assertThat(parentAcl1.isGranted(READ, Arrays.asList((Sid) new GrantedAuthoritySid("ROLE_USER_READ")), false))
|
||||
.isTrue();
|
||||
.isTrue();
|
||||
assertThat(parentAcl1.isGranted(WRITE, BEN, false)).isTrue();
|
||||
assertThat(parentAcl1.isGranted(DELETE, BEN, false)).isFalse();
|
||||
assertThat(parentAcl1.isGranted(DELETE, SCOTT, false)).isFalse();
|
||||
@@ -305,13 +303,13 @@ public class AclImplTests {
|
||||
// Check granting process for child1
|
||||
assertThat(childAcl1.isGranted(CREATE, SCOTT, false)).isTrue();
|
||||
assertThat(childAcl1.isGranted(READ, Arrays.asList((Sid) new GrantedAuthoritySid("ROLE_USER_READ")), false))
|
||||
.isTrue();
|
||||
.isTrue();
|
||||
assertThat(childAcl1.isGranted(DELETE, BEN, false)).isFalse();
|
||||
// Check granting process for child2 (doesn't inherit the permissions from its
|
||||
// parent)
|
||||
assertThatExceptionOfType(NotFoundException.class).isThrownBy(() -> childAcl2.isGranted(CREATE, SCOTT, false));
|
||||
assertThatExceptionOfType(NotFoundException.class)
|
||||
.isThrownBy(() -> childAcl2.isGranted(CREATE, Arrays.asList((Sid) new PrincipalSid("joe")), false));
|
||||
.isThrownBy(() -> childAcl2.isGranted(CREATE, Arrays.asList((Sid) new PrincipalSid("joe")), false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -398,20 +396,20 @@ public class AclImplTests {
|
||||
new PrincipalSid("joe"));
|
||||
assertThat(acl.isSidLoaded(loadedSids)).isTrue();
|
||||
assertThat(acl.isSidLoaded(Arrays.asList(new GrantedAuthoritySid("ROLE_IGNORED"), new PrincipalSid("ben"))))
|
||||
.isTrue();
|
||||
.isTrue();
|
||||
assertThat(acl.isSidLoaded(Arrays.asList((Sid) new GrantedAuthoritySid("ROLE_IGNORED")))).isTrue();
|
||||
assertThat(acl.isSidLoaded(BEN)).isTrue();
|
||||
assertThat(acl.isSidLoaded(null)).isTrue();
|
||||
assertThat(acl.isSidLoaded(new ArrayList<>(0))).isTrue();
|
||||
assertThat(acl.isSidLoaded(
|
||||
Arrays.asList(new GrantedAuthoritySid("ROLE_IGNORED"), new GrantedAuthoritySid("ROLE_IGNORED"))))
|
||||
.isTrue();
|
||||
.isTrue();
|
||||
assertThat(acl.isSidLoaded(
|
||||
Arrays.asList(new GrantedAuthoritySid("ROLE_GENERAL"), new GrantedAuthoritySid("ROLE_IGNORED"))))
|
||||
.isFalse();
|
||||
.isFalse();
|
||||
assertThat(acl.isSidLoaded(
|
||||
Arrays.asList(new GrantedAuthoritySid("ROLE_IGNORED"), new GrantedAuthoritySid("ROLE_GENERAL"))))
|
||||
.isFalse();
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -419,7 +417,7 @@ public class AclImplTests {
|
||||
AclImpl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, this.pgs, null, null, true,
|
||||
new PrincipalSid("joe"));
|
||||
assertThatExceptionOfType(NotFoundException.class)
|
||||
.isThrownBy(() -> acl.insertAce(-1, mock(Permission.class), mock(Sid.class), true));
|
||||
.isThrownBy(() -> acl.insertAce(-1, mock(Permission.class), mock(Sid.class), true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -437,7 +435,7 @@ public class AclImplTests {
|
||||
acl.insertAce(0, mock(Permission.class), mock(Sid.class), true);
|
||||
// Size is now 1
|
||||
assertThatExceptionOfType(NotFoundException.class)
|
||||
.isThrownBy(() -> acl.insertAce(2, mock(Permission.class), mock(Sid.class), true));
|
||||
.isThrownBy(() -> acl.insertAce(2, mock(Permission.class), mock(Sid.class), true));
|
||||
}
|
||||
|
||||
// SEC-1151
|
||||
@@ -468,7 +466,7 @@ public class AclImplTests {
|
||||
AclImpl acl = new AclImpl(this.objectIdentity, 1, this.authzStrategy, maskPgs, null, null, true,
|
||||
new PrincipalSid("joe"));
|
||||
Permission permission = this.permissionFactory
|
||||
.buildFromMask(BasePermission.READ.getMask() | BasePermission.WRITE.getMask());
|
||||
.buildFromMask(BasePermission.READ.getMask() | BasePermission.WRITE.getMask());
|
||||
Sid sid = new PrincipalSid("ben");
|
||||
acl.insertAce(0, permission, sid, true);
|
||||
service.updateAcl(acl);
|
||||
|
||||
+12
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -73,12 +73,12 @@ public class AclImplementationSecurityCheckTests {
|
||||
new SimpleGrantedAuthority("ROLE_THREE"));
|
||||
Acl acl2 = new AclImpl(identity, 1L, aclAuthorizationStrategy2, new ConsoleAuditLogger());
|
||||
// Check access in case the principal has no authorization rights
|
||||
assertThatExceptionOfType(NotFoundException.class)
|
||||
.isThrownBy(() -> aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_GENERAL));
|
||||
assertThatExceptionOfType(NotFoundException.class)
|
||||
.isThrownBy(() -> aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_AUDITING));
|
||||
assertThatExceptionOfType(NotFoundException.class)
|
||||
.isThrownBy(() -> aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_OWNERSHIP));
|
||||
assertThatExceptionOfType(NotFoundException.class).isThrownBy(
|
||||
() -> aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_GENERAL));
|
||||
assertThatExceptionOfType(NotFoundException.class).isThrownBy(
|
||||
() -> aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_AUDITING));
|
||||
assertThatExceptionOfType(NotFoundException.class).isThrownBy(
|
||||
() -> aclAuthorizationStrategy2.securityCheck(acl2, AclAuthorizationStrategy.CHANGE_OWNERSHIP));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -181,11 +181,11 @@ public class AclImplementationSecurityCheckTests {
|
||||
new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()), null, null, false,
|
||||
new PrincipalSid(auth));
|
||||
assertThatNoException()
|
||||
.isThrownBy(() -> aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_GENERAL));
|
||||
assertThatExceptionOfType(NotFoundException.class)
|
||||
.isThrownBy(() -> aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_AUDITING));
|
||||
assertThatNoException()
|
||||
.isThrownBy(() -> aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_OWNERSHIP));
|
||||
.isThrownBy(() -> aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_GENERAL));
|
||||
assertThatExceptionOfType(NotFoundException.class).isThrownBy(
|
||||
() -> aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_AUDITING));
|
||||
assertThatNoException().isThrownBy(
|
||||
() -> aclAuthorizationStrategy.securityCheck(acl, AclAuthorizationStrategy.CHANGE_OWNERSHIP));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -60,7 +60,7 @@ public class ObjectIdentityImplTests {
|
||||
public void testGetIdMethodConstraints() {
|
||||
// Check the getId() method is present
|
||||
assertThatExceptionOfType(IdentityUnavailableException.class)
|
||||
.isThrownBy(() -> new ObjectIdentityImpl("A_STRING_OBJECT"));
|
||||
.isThrownBy(() -> new ObjectIdentityImpl("A_STRING_OBJECT"));
|
||||
// getId() should return a non-null value
|
||||
MockIdDomainObject mockId = new MockIdDomainObject();
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new ObjectIdentityImpl(mockId));
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -47,12 +47,10 @@ public class PermissionTests {
|
||||
public void expectedIntegerValues() {
|
||||
assertThat(BasePermission.READ.getMask()).isEqualTo(1);
|
||||
assertThat(BasePermission.ADMINISTRATION.getMask()).isEqualTo(16);
|
||||
assertThat(new CumulativePermission().set(BasePermission.READ)
|
||||
.set(BasePermission.WRITE)
|
||||
.set(BasePermission.CREATE)
|
||||
.getMask()).isEqualTo(7);
|
||||
assertThat(new CumulativePermission().set(BasePermission.READ).set(BasePermission.WRITE)
|
||||
.set(BasePermission.CREATE).getMask()).isEqualTo(7);
|
||||
assertThat(new CumulativePermission().set(BasePermission.READ).set(BasePermission.ADMINISTRATION).getMask())
|
||||
.isEqualTo(17);
|
||||
.isEqualTo(17);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -66,23 +64,20 @@ public class PermissionTests {
|
||||
this.permissionFactory.registerPublicPermissions(SpecialPermission.class);
|
||||
assertThat(BasePermission.READ.toString()).isEqualTo("BasePermission[...............................R=1]");
|
||||
assertThat(BasePermission.ADMINISTRATION.toString())
|
||||
.isEqualTo("BasePermission[...........................A....=16]");
|
||||
.isEqualTo("BasePermission[...........................A....=16]");
|
||||
assertThat(new CumulativePermission().set(BasePermission.READ).toString())
|
||||
.isEqualTo("CumulativePermission[...............................R=1]");
|
||||
.isEqualTo("CumulativePermission[...............................R=1]");
|
||||
assertThat(
|
||||
new CumulativePermission().set(SpecialPermission.ENTER).set(BasePermission.ADMINISTRATION).toString())
|
||||
.isEqualTo("CumulativePermission[..........................EA....=48]");
|
||||
.isEqualTo("CumulativePermission[..........................EA....=48]");
|
||||
assertThat(new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ).toString())
|
||||
.isEqualTo("CumulativePermission[...........................A...R=17]");
|
||||
assertThat(new CumulativePermission().set(BasePermission.ADMINISTRATION)
|
||||
.set(BasePermission.READ)
|
||||
.clear(BasePermission.ADMINISTRATION)
|
||||
.toString()).isEqualTo("CumulativePermission[...............................R=1]");
|
||||
assertThat(new CumulativePermission().set(BasePermission.ADMINISTRATION)
|
||||
.set(BasePermission.READ)
|
||||
.clear(BasePermission.ADMINISTRATION)
|
||||
.clear(BasePermission.READ)
|
||||
.toString()).isEqualTo("CumulativePermission[................................=0]");
|
||||
.isEqualTo("CumulativePermission[...........................A...R=17]");
|
||||
assertThat(new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ)
|
||||
.clear(BasePermission.ADMINISTRATION).toString())
|
||||
.isEqualTo("CumulativePermission[...............................R=1]");
|
||||
assertThat(new CumulativePermission().set(BasePermission.ADMINISTRATION).set(BasePermission.READ)
|
||||
.clear(BasePermission.ADMINISTRATION).clear(BasePermission.READ).toString())
|
||||
.isEqualTo("CumulativePermission[................................=0]");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+22
-52
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.security.acls.jdbc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -24,15 +23,15 @@ import java.util.UUID;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import net.sf.ehcache.Ehcache;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.security.acls.TargetObject;
|
||||
import org.springframework.security.acls.TargetObjectWithUUID;
|
||||
@@ -42,10 +41,10 @@ import org.springframework.security.acls.domain.BasePermission;
|
||||
import org.springframework.security.acls.domain.ConsoleAuditLogger;
|
||||
import org.springframework.security.acls.domain.DefaultPermissionFactory;
|
||||
import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;
|
||||
import org.springframework.security.acls.domain.EhCacheBasedAclCache;
|
||||
import org.springframework.security.acls.domain.GrantedAuthoritySid;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityImpl;
|
||||
import org.springframework.security.acls.domain.PrincipalSid;
|
||||
import org.springframework.security.acls.domain.SpringCacheBasedAclCache;
|
||||
import org.springframework.security.acls.model.Acl;
|
||||
import org.springframework.security.acls.model.AuditableAccessControlEntry;
|
||||
import org.springframework.security.acls.model.MutableAcl;
|
||||
@@ -56,8 +55,6 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests {@link BasicLookupStrategy}
|
||||
@@ -78,7 +75,7 @@ public abstract class AbstractBasicLookupStrategyTests {
|
||||
|
||||
private BasicLookupStrategy strategy;
|
||||
|
||||
private static CacheManagerMock cacheManager;
|
||||
private static CacheManager cacheManager;
|
||||
|
||||
public abstract JdbcTemplate getJdbcTemplate();
|
||||
|
||||
@@ -86,13 +83,14 @@ public abstract class AbstractBasicLookupStrategyTests {
|
||||
|
||||
@BeforeAll
|
||||
public static void initCacheManaer() {
|
||||
cacheManager = new CacheManagerMock();
|
||||
cacheManager.addCache("basiclookuptestcache");
|
||||
cacheManager = CacheManager.create();
|
||||
cacheManager.addCache(new Cache("basiclookuptestcache", 500, false, false, 30, 30));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void shutdownCacheManager() {
|
||||
cacheManager.clear();
|
||||
cacheManager.removalAll();
|
||||
cacheManager.shutdown();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@@ -120,17 +118,11 @@ public abstract class AbstractBasicLookupStrategyTests {
|
||||
return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ADMINISTRATOR"));
|
||||
}
|
||||
|
||||
protected SpringCacheBasedAclCache aclCache() {
|
||||
return new SpringCacheBasedAclCache(getCache(), new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()),
|
||||
protected EhCacheBasedAclCache aclCache() {
|
||||
return new EhCacheBasedAclCache(getCache(), new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()),
|
||||
new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_USER")));
|
||||
}
|
||||
|
||||
protected Cache getCache() {
|
||||
Cache cache = cacheManager.getCacheManager().getCache("basiclookuptestcache");
|
||||
cache.clear();
|
||||
return cache;
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void emptyDatabase() {
|
||||
String query = "DELETE FROM acl_entry;" + "DELETE FROM acl_object_identity WHERE ID = 9;"
|
||||
@@ -142,6 +134,12 @@ public abstract class AbstractBasicLookupStrategyTests {
|
||||
getJdbcTemplate().execute(query);
|
||||
}
|
||||
|
||||
protected Ehcache getCache() {
|
||||
Ehcache cache = cacheManager.getCache("basiclookuptestcache");
|
||||
cache.removeAll();
|
||||
return cache;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAclsRetrievalWithDefaultBatchSize() throws Exception {
|
||||
ObjectIdentity topParentOid = new ObjectIdentityImpl(TARGET_CLASS, 100L);
|
||||
@@ -149,7 +147,7 @@ public abstract class AbstractBasicLookupStrategyTests {
|
||||
// Deliberately use an integer for the child, to reproduce bug report in SEC-819
|
||||
ObjectIdentity childOid = new ObjectIdentityImpl(TARGET_CLASS, 102);
|
||||
Map<ObjectIdentity, Acl> map = this.strategy
|
||||
.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
|
||||
.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
|
||||
checkEntries(topParentOid, middleParentOid, childOid, map);
|
||||
}
|
||||
|
||||
@@ -163,7 +161,7 @@ public abstract class AbstractBasicLookupStrategyTests {
|
||||
// Let's empty the database to force acls retrieval from cache
|
||||
emptyDatabase();
|
||||
Map<ObjectIdentity, Acl> map = this.strategy
|
||||
.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
|
||||
.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
|
||||
checkEntries(topParentOid, middleParentOid, childOid, map);
|
||||
}
|
||||
|
||||
@@ -176,7 +174,7 @@ public abstract class AbstractBasicLookupStrategyTests {
|
||||
// acls
|
||||
this.strategy.setBatchSize(1);
|
||||
Map<ObjectIdentity, Acl> map = this.strategy
|
||||
.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
|
||||
.readAclsById(Arrays.asList(topParentOid, middleParentOid, childOid), null);
|
||||
checkEntries(topParentOid, middleParentOid, childOid, map);
|
||||
}
|
||||
|
||||
@@ -303,7 +301,7 @@ public abstract class AbstractBasicLookupStrategyTests {
|
||||
getJdbcTemplate().execute(query);
|
||||
ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, 104L);
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.strategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID)));
|
||||
.isThrownBy(() -> this.strategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -329,32 +327,4 @@ public abstract class AbstractBasicLookupStrategyTests {
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
private static final class CacheManagerMock {
|
||||
|
||||
private final List<String> cacheNames;
|
||||
|
||||
private final CacheManager cacheManager;
|
||||
|
||||
private CacheManagerMock() {
|
||||
this.cacheNames = new ArrayList<>();
|
||||
this.cacheManager = mock(CacheManager.class);
|
||||
given(this.cacheManager.getCacheNames()).willReturn(this.cacheNames);
|
||||
}
|
||||
|
||||
private CacheManager getCacheManager() {
|
||||
return this.cacheManager;
|
||||
}
|
||||
|
||||
private void addCache(String name) {
|
||||
this.cacheNames.add(name);
|
||||
Cache cache = new ConcurrentMapCache(name);
|
||||
given(this.cacheManager.getCache(name)).willReturn(cache);
|
||||
}
|
||||
|
||||
private void clear() {
|
||||
this.cacheNames.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -116,7 +116,7 @@ public class BasicLookupStrategyWithAclClassTypeTests extends AbstractBasicLooku
|
||||
public void testReadObjectIdentityUsingNonUuidInDatabase() {
|
||||
ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, OBJECT_IDENTITY_LONG_AS_UUID);
|
||||
assertThatExceptionOfType(ConversionFailedException.class)
|
||||
.isThrownBy(() -> this.uuidEnabledStrategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID)));
|
||||
.isThrownBy(() -> this.uuidEnabledStrategy.readAclsById(Arrays.asList(oid), Arrays.asList(BEN_SID)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.acls.jdbc;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import net.sf.ehcache.Ehcache;
|
||||
import net.sf.ehcache.Element;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import org.springframework.security.acls.domain.AclAuthorizationStrategy;
|
||||
import org.springframework.security.acls.domain.AclAuthorizationStrategyImpl;
|
||||
import org.springframework.security.acls.domain.AclImpl;
|
||||
import org.springframework.security.acls.domain.ConsoleAuditLogger;
|
||||
import org.springframework.security.acls.domain.DefaultPermissionGrantingStrategy;
|
||||
import org.springframework.security.acls.domain.EhCacheBasedAclCache;
|
||||
import org.springframework.security.acls.domain.ObjectIdentityImpl;
|
||||
import org.springframework.security.acls.model.MutableAcl;
|
||||
import org.springframework.security.acls.model.ObjectIdentity;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.util.FieldUtils;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests {@link EhCacheBasedAclCache}
|
||||
*
|
||||
* @author Andrei Stefan
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class EhCacheBasedAclCacheTests {
|
||||
|
||||
private static final String TARGET_CLASS = "org.springframework.security.acls.TargetObject";
|
||||
|
||||
@Mock
|
||||
private Ehcache cache;
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<Element> element;
|
||||
|
||||
private EhCacheBasedAclCache myCache;
|
||||
|
||||
private MutableAcl acl;
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
this.myCache = new EhCacheBasedAclCache(this.cache,
|
||||
new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()),
|
||||
new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_USER")));
|
||||
ObjectIdentity identity = new ObjectIdentityImpl(TARGET_CLASS, 100L);
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
|
||||
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
|
||||
new SimpleGrantedAuthority("ROLE_GENERAL"));
|
||||
this.acl = new AclImpl(identity, 1L, aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanup() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorRejectsNullParameters() {
|
||||
assertThatIllegalArgumentException().isThrownBy(
|
||||
() -> new EhCacheBasedAclCache(null, new DefaultPermissionGrantingStrategy(new ConsoleAuditLogger()),
|
||||
new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_USER"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodsRejectNullParameters() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.myCache.evictFromCache((Serializable) null));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.myCache.evictFromCache((ObjectIdentity) null));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.myCache.getFromCache((Serializable) null));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.myCache.getFromCache((ObjectIdentity) null));
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> this.myCache.putInCache(null));
|
||||
}
|
||||
|
||||
// SEC-527
|
||||
@Test
|
||||
public void testDiskSerializationOfMutableAclObjectInstance() throws Exception {
|
||||
// Serialization test
|
||||
File file = File.createTempFile("SEC_TEST", ".object");
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
ObjectOutputStream oos = new ObjectOutputStream(fos);
|
||||
oos.writeObject(this.acl);
|
||||
oos.close();
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
ObjectInputStream ois = new ObjectInputStream(fis);
|
||||
MutableAcl retrieved = (MutableAcl) ois.readObject();
|
||||
ois.close();
|
||||
assertThat(retrieved).isEqualTo(this.acl);
|
||||
Object retrieved1 = FieldUtils.getProtectedFieldValue("aclAuthorizationStrategy", retrieved);
|
||||
assertThat(retrieved1).isNull();
|
||||
Object retrieved2 = FieldUtils.getProtectedFieldValue("permissionGrantingStrategy", retrieved);
|
||||
assertThat(retrieved2).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearCache() {
|
||||
this.myCache.clearCache();
|
||||
verify(this.cache).removeAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putInCache() {
|
||||
this.myCache.putInCache(this.acl);
|
||||
verify(this.cache, times(2)).put(this.element.capture());
|
||||
assertThat(this.element.getValue().getKey()).isEqualTo(this.acl.getId());
|
||||
assertThat(this.element.getValue().getObjectValue()).isEqualTo(this.acl);
|
||||
assertThat(this.element.getAllValues().get(0).getKey()).isEqualTo(this.acl.getObjectIdentity());
|
||||
assertThat(this.element.getAllValues().get(0).getObjectValue()).isEqualTo(this.acl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putInCacheAclWithParent() {
|
||||
Authentication auth = new TestingAuthenticationToken("user", "password", "ROLE_GENERAL");
|
||||
auth.setAuthenticated(true);
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
ObjectIdentity identityParent = new ObjectIdentityImpl(TARGET_CLASS, 2L);
|
||||
AclAuthorizationStrategy aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
|
||||
new SimpleGrantedAuthority("ROLE_OWNERSHIP"), new SimpleGrantedAuthority("ROLE_AUDITING"),
|
||||
new SimpleGrantedAuthority("ROLE_GENERAL"));
|
||||
MutableAcl parentAcl = new AclImpl(identityParent, 2L, aclAuthorizationStrategy, new ConsoleAuditLogger());
|
||||
this.acl.setParent(parentAcl);
|
||||
this.myCache.putInCache(this.acl);
|
||||
verify(this.cache, times(4)).put(this.element.capture());
|
||||
List<Element> allValues = this.element.getAllValues();
|
||||
assertThat(allValues.get(0).getKey()).isEqualTo(parentAcl.getObjectIdentity());
|
||||
assertThat(allValues.get(0).getObjectValue()).isEqualTo(parentAcl);
|
||||
assertThat(allValues.get(1).getKey()).isEqualTo(parentAcl.getId());
|
||||
assertThat(allValues.get(1).getObjectValue()).isEqualTo(parentAcl);
|
||||
assertThat(allValues.get(2).getKey()).isEqualTo(this.acl.getObjectIdentity());
|
||||
assertThat(allValues.get(2).getObjectValue()).isEqualTo(this.acl);
|
||||
assertThat(allValues.get(3).getKey()).isEqualTo(this.acl.getId());
|
||||
assertThat(allValues.get(3).getObjectValue()).isEqualTo(this.acl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFromCacheSerializable() {
|
||||
given(this.cache.get(this.acl.getId())).willReturn(new Element(this.acl.getId(), this.acl));
|
||||
assertThat(this.myCache.getFromCache(this.acl.getId())).isEqualTo(this.acl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFromCacheSerializablePopulatesTransient() {
|
||||
given(this.cache.get(this.acl.getId())).willReturn(new Element(this.acl.getId(), this.acl));
|
||||
this.myCache.putInCache(this.acl);
|
||||
ReflectionTestUtils.setField(this.acl, "permissionGrantingStrategy", null);
|
||||
ReflectionTestUtils.setField(this.acl, "aclAuthorizationStrategy", null);
|
||||
MutableAcl fromCache = this.myCache.getFromCache(this.acl.getId());
|
||||
assertThat(ReflectionTestUtils.getField(fromCache, "aclAuthorizationStrategy")).isNotNull();
|
||||
assertThat(ReflectionTestUtils.getField(fromCache, "permissionGrantingStrategy")).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFromCacheObjectIdentity() {
|
||||
given(this.cache.get(this.acl.getId())).willReturn(new Element(this.acl.getId(), this.acl));
|
||||
assertThat(this.myCache.getFromCache(this.acl.getId())).isEqualTo(this.acl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFromCacheObjectIdentityPopulatesTransient() {
|
||||
given(this.cache.get(this.acl.getObjectIdentity())).willReturn(new Element(this.acl.getId(), this.acl));
|
||||
this.myCache.putInCache(this.acl);
|
||||
ReflectionTestUtils.setField(this.acl, "permissionGrantingStrategy", null);
|
||||
ReflectionTestUtils.setField(this.acl, "aclAuthorizationStrategy", null);
|
||||
MutableAcl fromCache = this.myCache.getFromCache(this.acl.getObjectIdentity());
|
||||
assertThat(ReflectionTestUtils.getField(fromCache, "aclAuthorizationStrategy")).isNotNull();
|
||||
assertThat(ReflectionTestUtils.getField(fromCache, "permissionGrantingStrategy")).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void evictCacheSerializable() {
|
||||
given(this.cache.get(this.acl.getObjectIdentity())).willReturn(new Element(this.acl.getId(), this.acl));
|
||||
this.myCache.evictFromCache(this.acl.getObjectIdentity());
|
||||
verify(this.cache).remove(this.acl.getId());
|
||||
verify(this.cache).remove(this.acl.getObjectIdentity());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void evictCacheObjectIdentity() {
|
||||
given(this.cache.get(this.acl.getId())).willReturn(new Element(this.acl.getId(), this.acl));
|
||||
this.myCache.evictFromCache(this.acl.getId());
|
||||
verify(this.cache).remove(this.acl.getId());
|
||||
verify(this.cache).remove(this.acl.getObjectIdentity());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -101,7 +101,7 @@ public class JdbcAclServiceTests {
|
||||
ObjectIdentity objectIdentity = new ObjectIdentityImpl(Object.class, 1);
|
||||
List<Sid> sids = Arrays.<Sid>asList(new PrincipalSid("user"));
|
||||
assertThatExceptionOfType(NotFoundException.class)
|
||||
.isThrownBy(() -> this.aclService.readAclById(objectIdentity, sids));
|
||||
.isThrownBy(() -> this.aclService.readAclById(objectIdentity, sids));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -109,10 +109,10 @@ public class JdbcAclServiceTests {
|
||||
List<ObjectIdentity> result = new ArrayList<>();
|
||||
result.add(new ObjectIdentityImpl(Object.class, "5577"));
|
||||
Object[] args = { "1", "org.springframework.security.acls.jdbc.JdbcAclServiceTests$MockLongIdDomainObject" };
|
||||
given(this.jdbcOperations.query(anyString(), any(RowMapper.class), eq(args))).willReturn(result);
|
||||
given(this.jdbcOperations.query(anyString(), eq(args), any(RowMapper.class))).willReturn(result);
|
||||
ObjectIdentity objectIdentity = new ObjectIdentityImpl(MockLongIdDomainObject.class, 1L);
|
||||
List<ObjectIdentity> objectIdentities = this.aclService.findChildren(objectIdentity);
|
||||
assertThat(objectIdentities).hasSize(1);
|
||||
assertThat(objectIdentities.size()).isEqualTo(1);
|
||||
assertThat(objectIdentities.get(0).getIdentifier()).isEqualTo("5577");
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ public class JdbcAclServiceTests {
|
||||
public void findChildrenWithoutIdType() {
|
||||
ObjectIdentity objectIdentity = new ObjectIdentityImpl(MockLongIdDomainObject.class, 4711L);
|
||||
List<ObjectIdentity> objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity);
|
||||
assertThat(objectIdentities).hasSize(1);
|
||||
assertThat(objectIdentities.size()).isEqualTo(1);
|
||||
assertThat(objectIdentities.get(0).getType()).isEqualTo(MockUntypedIdDomainObject.class.getName());
|
||||
assertThat(objectIdentities.get(0).getIdentifier()).isEqualTo(5000L);
|
||||
}
|
||||
@@ -143,7 +143,7 @@ public class JdbcAclServiceTests {
|
||||
public void findChildrenOfIdTypeLong() {
|
||||
ObjectIdentity objectIdentity = new ObjectIdentityImpl("location", "US-PAL");
|
||||
List<ObjectIdentity> objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity);
|
||||
assertThat(objectIdentities).hasSize(2);
|
||||
assertThat(objectIdentities.size()).isEqualTo(2);
|
||||
assertThat(objectIdentities.get(0).getType()).isEqualTo(MockLongIdDomainObject.class.getName());
|
||||
assertThat(objectIdentities.get(0).getIdentifier()).isEqualTo(4711L);
|
||||
assertThat(objectIdentities.get(1).getType()).isEqualTo(MockLongIdDomainObject.class.getName());
|
||||
@@ -155,7 +155,7 @@ public class JdbcAclServiceTests {
|
||||
ObjectIdentity objectIdentity = new ObjectIdentityImpl("location", "US");
|
||||
this.aclServiceIntegration.setAclClassIdSupported(true);
|
||||
List<ObjectIdentity> objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity);
|
||||
assertThat(objectIdentities).hasSize(1);
|
||||
assertThat(objectIdentities.size()).isEqualTo(1);
|
||||
assertThat(objectIdentities.get(0).getType()).isEqualTo("location");
|
||||
assertThat(objectIdentities.get(0).getIdentifier()).isEqualTo("US-PAL");
|
||||
}
|
||||
@@ -165,28 +165,28 @@ public class JdbcAclServiceTests {
|
||||
ObjectIdentity objectIdentity = new ObjectIdentityImpl(MockUntypedIdDomainObject.class, 5000L);
|
||||
this.aclServiceIntegration.setAclClassIdSupported(true);
|
||||
List<ObjectIdentity> objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity);
|
||||
assertThat(objectIdentities).hasSize(1);
|
||||
assertThat(objectIdentities.size()).isEqualTo(1);
|
||||
assertThat(objectIdentities.get(0).getType()).isEqualTo("costcenter");
|
||||
assertThat(objectIdentities.get(0).getIdentifier())
|
||||
.isEqualTo(UUID.fromString("25d93b3f-c3aa-4814-9d5e-c7c96ced7762"));
|
||||
.isEqualTo(UUID.fromString("25d93b3f-c3aa-4814-9d5e-c7c96ced7762"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setObjectIdentityGeneratorWhenNullThenThrowsIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.aclServiceIntegration.setObjectIdentityGenerator(null))
|
||||
.withMessage("objectIdentityGenerator cannot be null");
|
||||
.isThrownBy(() -> this.aclServiceIntegration.setObjectIdentityGenerator(null))
|
||||
.withMessage("objectIdentityGenerator cannot be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findChildrenWhenObjectIdentityGeneratorSetThenUsed() {
|
||||
this.aclServiceIntegration
|
||||
.setObjectIdentityGenerator((id, type) -> new ObjectIdentityImpl(type, "prefix:" + id));
|
||||
.setObjectIdentityGenerator((id, type) -> new ObjectIdentityImpl(type, "prefix:" + id));
|
||||
|
||||
ObjectIdentity objectIdentity = new ObjectIdentityImpl("location", "US");
|
||||
this.aclServiceIntegration.setAclClassIdSupported(true);
|
||||
List<ObjectIdentity> objectIdentities = this.aclServiceIntegration.findChildren(objectIdentity);
|
||||
assertThat(objectIdentities).hasSize(1);
|
||||
assertThat(objectIdentities.size()).isEqualTo(1);
|
||||
assertThat(objectIdentities.get(0).getType()).isEqualTo("location");
|
||||
assertThat(objectIdentities.get(0).getIdentifier()).isEqualTo("prefix:US-PAL");
|
||||
}
|
||||
|
||||
+13
-31
@@ -48,10 +48,7 @@ import org.springframework.security.acls.model.Sid;
|
||||
import org.springframework.security.acls.sid.CustomSid;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.core.context.SecurityContextImpl;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.context.transaction.AfterTransaction;
|
||||
@@ -62,9 +59,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Integration tests the ACL system using an in-memory database.
|
||||
@@ -168,7 +163,7 @@ public class JdbcMutableAclServiceTests {
|
||||
this.jdbcMutableAclService.updateAcl(child);
|
||||
// Let's check if we can read them back correctly
|
||||
Map<ObjectIdentity, Acl> map = this.jdbcMutableAclService
|
||||
.readAclsById(Arrays.asList(getTopParentOid(), getMiddleParentOid(), getChildOid()));
|
||||
.readAclsById(Arrays.asList(getTopParentOid(), getMiddleParentOid(), getChildOid()));
|
||||
assertThat(map).hasSize(3);
|
||||
// Get the retrieved versions
|
||||
MutableAcl retrievedTopParent = (MutableAcl) map.get(getTopParentOid());
|
||||
@@ -196,7 +191,7 @@ public class JdbcMutableAclServiceTests {
|
||||
assertThat(retrievedMiddleParent.isGranted(delete, pSid, false)).isTrue();
|
||||
assertThat(retrievedChild.isGranted(delete, pSid, false)).isFalse();
|
||||
assertThatExceptionOfType(NotFoundException.class)
|
||||
.isThrownBy(() -> retrievedChild.isGranted(Arrays.asList(BasePermission.ADMINISTRATION), pSid, false));
|
||||
.isThrownBy(() -> retrievedChild.isGranted(Arrays.asList(BasePermission.ADMINISTRATION), pSid, false));
|
||||
// Now check the inherited rights (when not explicitly overridden) also look OK
|
||||
assertThat(retrievedChild.isGranted(read, pSid, false)).isTrue();
|
||||
assertThat(retrievedChild.isGranted(write, pSid, false)).isFalse();
|
||||
@@ -209,9 +204,9 @@ public class JdbcMutableAclServiceTests {
|
||||
// Check the child permissions no longer inherit
|
||||
assertThat(nonInheritingChild.isGranted(delete, pSid, true)).isFalse();
|
||||
assertThatExceptionOfType(NotFoundException.class)
|
||||
.isThrownBy(() -> nonInheritingChild.isGranted(read, pSid, true));
|
||||
.isThrownBy(() -> nonInheritingChild.isGranted(read, pSid, true));
|
||||
assertThatExceptionOfType(NotFoundException.class)
|
||||
.isThrownBy(() -> nonInheritingChild.isGranted(write, pSid, true));
|
||||
.isThrownBy(() -> nonInheritingChild.isGranted(write, pSid, true));
|
||||
// Let's add an identical permission to the child, but it'll appear AFTER the
|
||||
// current permission, so has no impact
|
||||
nonInheritingChild.insertAce(1, BasePermission.DELETE, new PrincipalSid(this.auth), true);
|
||||
@@ -266,9 +261,9 @@ public class JdbcMutableAclServiceTests {
|
||||
// Delete the mid-parent and test if the child was deleted, as well
|
||||
this.jdbcMutableAclService.deleteAcl(getMiddleParentOid(), true);
|
||||
assertThatExceptionOfType(NotFoundException.class)
|
||||
.isThrownBy(() -> this.jdbcMutableAclService.readAclById(getMiddleParentOid()));
|
||||
.isThrownBy(() -> this.jdbcMutableAclService.readAclById(getMiddleParentOid()));
|
||||
assertThatExceptionOfType(NotFoundException.class)
|
||||
.isThrownBy(() -> this.jdbcMutableAclService.readAclById(getChildOid()));
|
||||
.isThrownBy(() -> this.jdbcMutableAclService.readAclById(getChildOid()));
|
||||
Acl acl = this.jdbcMutableAclService.readAclById(getTopParentOid());
|
||||
assertThat(acl).isNotNull();
|
||||
assertThat(getTopParentOid()).isEqualTo(acl.getObjectIdentity());
|
||||
@@ -277,11 +272,11 @@ public class JdbcMutableAclServiceTests {
|
||||
@Test
|
||||
public void constructorRejectsNullParameters() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new JdbcMutableAclService(null, this.lookupStrategy, this.aclCache));
|
||||
.isThrownBy(() -> new JdbcMutableAclService(null, this.lookupStrategy, this.aclCache));
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new JdbcMutableAclService(this.dataSource, null, this.aclCache));
|
||||
.isThrownBy(() -> new JdbcMutableAclService(this.dataSource, null, this.aclCache));
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new JdbcMutableAclService(this.dataSource, this.lookupStrategy, null));
|
||||
.isThrownBy(() -> new JdbcMutableAclService(this.dataSource, this.lookupStrategy, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -297,7 +292,7 @@ public class JdbcMutableAclServiceTests {
|
||||
this.jdbcMutableAclService.createAcl(duplicateOid);
|
||||
// Try to add the same object second time
|
||||
assertThatExceptionOfType(AlreadyExistsException.class)
|
||||
.isThrownBy(() -> this.jdbcMutableAclService.createAcl(duplicateOid));
|
||||
.isThrownBy(() -> this.jdbcMutableAclService.createAcl(duplicateOid));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -320,7 +315,7 @@ public class JdbcMutableAclServiceTests {
|
||||
try {
|
||||
// checking in the class, not database
|
||||
assertThatExceptionOfType(ChildrenExistException.class)
|
||||
.isThrownBy(() -> this.jdbcMutableAclService.deleteAcl(getTopParentOid(), false));
|
||||
.isThrownBy(() -> this.jdbcMutableAclService.deleteAcl(getTopParentOid(), false));
|
||||
}
|
||||
finally {
|
||||
// restore to the default
|
||||
@@ -355,19 +350,6 @@ public class JdbcMutableAclServiceTests {
|
||||
assertThat(this.jdbcMutableAclService.readAclById(new ObjectIdentityImpl(TARGET_CLASS, 101L))).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Transactional
|
||||
public void createAclWhenCustomSecurityContextHolderStrategyThenUses() {
|
||||
SecurityContextHolderStrategy securityContextHolderStrategy = mock(SecurityContextHolderStrategy.class);
|
||||
SecurityContext context = new SecurityContextImpl(this.auth);
|
||||
given(securityContextHolderStrategy.getContext()).willReturn(context);
|
||||
JdbcMutableAclService service = new JdbcMutableAclService(this.dataSource, this.lookupStrategy, this.aclCache);
|
||||
service.setSecurityContextHolderStrategy(securityContextHolderStrategy);
|
||||
ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS, 101);
|
||||
service.createAcl(oid);
|
||||
verify(securityContextHolderStrategy).getContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* SEC-655
|
||||
*/
|
||||
@@ -392,7 +374,7 @@ public class JdbcMutableAclServiceTests {
|
||||
child = (MutableAcl) this.jdbcMutableAclService.readAclById(childOid);
|
||||
parent = (MutableAcl) child.getParentAcl();
|
||||
assertThat(parent.getEntries()).hasSize(2)
|
||||
.withFailMessage("Fails because child has a stale reference to its parent");
|
||||
.withFailMessage("Fails because child has a stale reference to its parent");
|
||||
assertThat(parent.getEntries().get(0).getPermission().getMask()).isEqualTo(1);
|
||||
assertThat(parent.getEntries().get(0).getSid()).isEqualTo(new PrincipalSid("ben"));
|
||||
assertThat(parent.getEntries().get(1).getPermission().getMask()).isEqualTo(1);
|
||||
@@ -454,7 +436,7 @@ public class JdbcMutableAclServiceTests {
|
||||
CustomSid customSid = new CustomSid("Custom sid");
|
||||
given(customJdbcMutableAclService.createOrRetrieveSidPrimaryKey("Custom sid", false, false)).willReturn(1L);
|
||||
Long result = customJdbcMutableAclService.createOrRetrieveSidPrimaryKey(customSid, false);
|
||||
assertThat(Long.valueOf(1L)).isEqualTo(result);
|
||||
assertThat(new Long(1L)).isEqualTo(result);
|
||||
}
|
||||
|
||||
protected Authentication getAuth() {
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -79,7 +79,7 @@ public class JdbcMutableAclServiceTestsWithAclClassId extends JdbcMutableAclServ
|
||||
ObjectIdentity oid = new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, id);
|
||||
getJdbcMutableAclService().createAcl(oid);
|
||||
assertThat(getJdbcMutableAclService().readAclById(new ObjectIdentityImpl(TARGET_CLASS_WITH_UUID, id)))
|
||||
.isNotNull();
|
||||
.isNotNull();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -46,9 +46,9 @@ public class SidTests {
|
||||
// Check one Authentication-argument constructor
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new PrincipalSid((Authentication) null));
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new PrincipalSid(new TestingAuthenticationToken(null, "password")));
|
||||
.isThrownBy(() -> new PrincipalSid(new TestingAuthenticationToken(null, "password")));
|
||||
assertThatNoException()
|
||||
.isThrownBy(() -> new PrincipalSid(new TestingAuthenticationToken("johndoe", "password")));
|
||||
.isThrownBy(() -> new PrincipalSid(new TestingAuthenticationToken("johndoe", "password")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -60,7 +60,7 @@ public class SidTests {
|
||||
// Check one GrantedAuthority-argument constructor
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new GrantedAuthoritySid((GrantedAuthority) null));
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new GrantedAuthoritySid(new SimpleGrantedAuthority(null)));
|
||||
.isThrownBy(() -> new GrantedAuthoritySid(new SimpleGrantedAuthority(null)));
|
||||
assertThatNoException().isThrownBy(() -> new GrantedAuthoritySid(new SimpleGrantedAuthority("ROLE_TEST")));
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public class SidTests {
|
||||
assertThat(principalSid.hashCode()).isEqualTo(new PrincipalSid("johndoe").hashCode());
|
||||
assertThat(principalSid.hashCode()).isNotEqualTo(new PrincipalSid("scott").hashCode());
|
||||
assertThat(principalSid.hashCode())
|
||||
.isNotEqualTo(new PrincipalSid(new TestingAuthenticationToken("scott", "password")).hashCode());
|
||||
.isNotEqualTo(new PrincipalSid(new TestingAuthenticationToken("scott", "password")).hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -111,7 +111,7 @@ public class SidTests {
|
||||
assertThat(gaSid.hashCode()).isEqualTo(new GrantedAuthoritySid("ROLE_TEST").hashCode());
|
||||
assertThat(gaSid.hashCode()).isNotEqualTo(new GrantedAuthoritySid("ROLE_TEST_2").hashCode());
|
||||
assertThat(gaSid.hashCode())
|
||||
.isNotEqualTo(new GrantedAuthoritySid(new SimpleGrantedAuthority("ROLE_TEST_2")).hashCode());
|
||||
.isNotEqualTo(new GrantedAuthoritySid(new SimpleGrantedAuthority("ROLE_TEST_2")).hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -120,9 +120,9 @@ public class SidTests {
|
||||
PrincipalSid principalSid = new PrincipalSid(authentication);
|
||||
GrantedAuthority ga = new SimpleGrantedAuthority("ROLE_TEST");
|
||||
GrantedAuthoritySid gaSid = new GrantedAuthoritySid(ga);
|
||||
assertThat("johndoe").isEqualTo(principalSid.getPrincipal());
|
||||
assertThat("johndoe".equals(principalSid.getPrincipal())).isTrue();
|
||||
assertThat("scott".equals(principalSid.getPrincipal())).isFalse();
|
||||
assertThat("ROLE_TEST").isEqualTo(gaSid.getGrantedAuthority());
|
||||
assertThat("ROLE_TEST".equals(gaSid.getGrantedAuthority())).isTrue();
|
||||
assertThat("ROLE_TEST2".equals(gaSid.getGrantedAuthority())).isFalse();
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,16 @@
|
||||
<property name="dataSource" ref="dataSource"/>
|
||||
</bean>
|
||||
|
||||
<bean id="aclCache" class="org.springframework.security.acls.domain.SpringCacheBasedAclCache">
|
||||
<bean id="aclCache" class="org.springframework.security.acls.domain.EhCacheBasedAclCache">
|
||||
<constructor-arg>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
|
||||
<property name="name" value="aclCache"/>
|
||||
<bean class="org.springframework.cache.ehcache.EhCacheFactoryBean">
|
||||
<property name="cacheManager">
|
||||
<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
|
||||
<!-- This context is used in two tests so accept existing cache manager as the context will be reused -->
|
||||
<property name="acceptExisting" value="true"/>
|
||||
</bean>
|
||||
</property>
|
||||
<property name="cacheName" value="aclCache"/>
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
<constructor-arg>
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
apply plugin: 'io.spring.convention.spring-module'
|
||||
apply plugin: 'io.freefair.aspectj'
|
||||
|
||||
compileAspectj {
|
||||
sourceCompatibility "17"
|
||||
targetCompatibility "17"
|
||||
}
|
||||
compileTestAspectj {
|
||||
sourceCompatibility "17"
|
||||
targetCompatibility "17"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
management platform(project(":spring-security-dependencies"))
|
||||
api "org.aspectj:aspectjrt"
|
||||
@@ -18,8 +9,6 @@ dependencies {
|
||||
api 'org.springframework:spring-context'
|
||||
api 'org.springframework:spring-core'
|
||||
|
||||
optional project(':spring-security-access')
|
||||
|
||||
testImplementation 'org.springframework:spring-aop'
|
||||
testImplementation "org.assertj:assertj-core"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||
@@ -29,8 +18,12 @@ dependencies {
|
||||
testImplementation "org.mockito:mockito-junit-jupiter"
|
||||
testImplementation "org.springframework:spring-test"
|
||||
testAspect sourceSets.main.output
|
||||
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
sourceSets.main.aspectj.srcDir "src/main/java"
|
||||
sourceSets.main.java.srcDirs = files()
|
||||
|
||||
sourceSets.test.aspectj.srcDir "src/test/java"
|
||||
sourceSets.test.java.srcDirs = files()
|
||||
|
||||
compileAspectj.ajcOptions.outxmlfile = "META-INF/aop.xml"
|
||||
|
||||
+1
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -37,9 +37,7 @@ import org.springframework.security.access.prepost.PreFilter;
|
||||
* @author Mike Wiesner
|
||||
* @author Luke Taylor
|
||||
* @since 3.1
|
||||
* @deprecated Use aspects in {@link org.springframework.security.authorization.method.aspectj} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public aspect AnnotationSecurityAspect implements InitializingBean {
|
||||
|
||||
/**
|
||||
|
||||
-61
@@ -1,61 +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.authorization.method.aspectj;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.access.prepost.PostAuthorize;
|
||||
|
||||
/**
|
||||
* Abstract AspectJ aspect for adapting a {@link MethodInvocation}
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 5.8
|
||||
*/
|
||||
abstract aspect AbstractMethodInterceptorAspect {
|
||||
|
||||
protected abstract pointcut executionOfAnnotatedMethod();
|
||||
|
||||
private MethodInterceptor securityInterceptor;
|
||||
|
||||
Object around(): executionOfAnnotatedMethod() {
|
||||
if (this.securityInterceptor == null) {
|
||||
return proceed();
|
||||
}
|
||||
MethodInvocation invocation = new JoinPointMethodInvocation(thisJoinPoint, () -> proceed());
|
||||
try {
|
||||
return this.securityInterceptor.invoke(invocation);
|
||||
} catch (Throwable t) {
|
||||
throwUnchecked(t);
|
||||
throw new IllegalStateException("Code unexpectedly reached", t);
|
||||
}
|
||||
}
|
||||
|
||||
public void setSecurityInterceptor(MethodInterceptor securityInterceptor) {
|
||||
this.securityInterceptor = securityInterceptor;
|
||||
}
|
||||
|
||||
private static void throwUnchecked(Throwable ex) {
|
||||
AbstractMethodInterceptorAspect.<RuntimeException>throwAny(ex);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <E extends Throwable> void throwAny(Throwable ex) throws E {
|
||||
throw (E) ex;
|
||||
}
|
||||
}
|
||||
-98
@@ -1,98 +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.authorization.method.aspectj;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.reflect.CodeSignature;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
class JoinPointMethodInvocation implements MethodInvocation {
|
||||
|
||||
private final JoinPoint jp;
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Object target;
|
||||
|
||||
private final Supplier<Object> proceed;
|
||||
|
||||
JoinPointMethodInvocation(JoinPoint jp, Supplier<Object> proceed) {
|
||||
this.jp = 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 + "'");
|
||||
this.proceed = proceed;
|
||||
}
|
||||
|
||||
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.proceed.get();
|
||||
}
|
||||
|
||||
}
|
||||
-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.authorization.method.aspectj;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.access.prepost.PostAuthorize;
|
||||
|
||||
/**
|
||||
* Concrete AspectJ aspect using Spring Security @PostAuthorize annotation.
|
||||
*
|
||||
* <p>
|
||||
* When using this aspect, you <i>must</i> annotate the implementation class
|
||||
* (and/or methods within that class), <i>not</i> the interface (if any) that
|
||||
* the class implements. AspectJ follows Java's rule that annotations on
|
||||
* interfaces are <i>not</i> inherited. This will vary from Spring AOP.
|
||||
*
|
||||
* @author Mike Wiesner
|
||||
* @author Luke Taylor
|
||||
* @author Josh Cummings
|
||||
* @since 5.8
|
||||
*/
|
||||
public aspect PostAuthorizeAspect extends AbstractMethodInterceptorAspect {
|
||||
|
||||
/**
|
||||
* Matches the execution of any method with a PostAuthorize annotation.
|
||||
*/
|
||||
protected pointcut executionOfAnnotatedMethod() : execution(* *(..)) && @annotation(PostAuthorize);
|
||||
}
|
||||
-45
@@ -1,45 +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.authorization.method.aspectj;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.access.prepost.PostFilter;
|
||||
|
||||
/**
|
||||
* Concrete AspectJ aspect using Spring Security @PostFilter annotation.
|
||||
*
|
||||
* <p>
|
||||
* When using this aspect, you <i>must</i> annotate the implementation class
|
||||
* (and/or methods within that class), <i>not</i> the interface (if any) that
|
||||
* the class implements. AspectJ follows Java's rule that annotations on
|
||||
* interfaces are <i>not</i> inherited. This will vary from Spring AOP.
|
||||
*
|
||||
* @author Mike Wiesner
|
||||
* @author Luke Taylor
|
||||
* @author Josh Cummings
|
||||
* @since 5.8
|
||||
*/
|
||||
public aspect PostFilterAspect extends AbstractMethodInterceptorAspect {
|
||||
|
||||
/**
|
||||
* Matches the execution of any method with a PostFilter annotation.
|
||||
*/
|
||||
protected pointcut executionOfAnnotatedMethod() : execution(* *(..)) && @annotation(PostFilter);
|
||||
|
||||
}
|
||||
-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.authorization.method.aspectj;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
/**
|
||||
* Concrete AspectJ aspect using Spring Security @PreAuthorize annotation.
|
||||
*
|
||||
* <p>
|
||||
* When using this aspect, you <i>must</i> annotate the implementation class
|
||||
* (and/or methods within that class), <i>not</i> the interface (if any) that
|
||||
* the class implements. AspectJ follows Java's rule that annotations on
|
||||
* interfaces are <i>not</i> inherited. This will vary from Spring AOP.
|
||||
*
|
||||
* @author Mike Wiesner
|
||||
* @author Luke Taylor
|
||||
* @author Josh Cummings
|
||||
* @since 5.8
|
||||
*/
|
||||
public aspect PreAuthorizeAspect extends AbstractMethodInterceptorAspect {
|
||||
|
||||
/**
|
||||
* Matches the execution of any method with a PreAuthorize annotation.
|
||||
*/
|
||||
protected pointcut executionOfAnnotatedMethod() : execution(* *(..)) && @annotation(PreAuthorize);
|
||||
}
|
||||
-45
@@ -1,45 +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.authorization.method.aspectj;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.access.prepost.PreFilter;
|
||||
|
||||
/**
|
||||
* Concrete AspectJ aspect using Spring Security @PreFilter annotation.
|
||||
*
|
||||
* <p>
|
||||
* When using this aspect, you <i>must</i> annotate the implementation class
|
||||
* (and/or methods within that class), <i>not</i> the interface (if any) that
|
||||
* the class implements. AspectJ follows Java's rule that annotations on
|
||||
* interfaces are <i>not</i> inherited. This will vary from Spring AOP.
|
||||
*
|
||||
* @author Mike Wiesner
|
||||
* @author Luke Taylor
|
||||
* @author Josh Cummings
|
||||
* @since 5.8
|
||||
*/
|
||||
public aspect PreFilterAspect extends AbstractMethodInterceptorAspect {
|
||||
|
||||
/**
|
||||
* Matches the execution of any method with a PreFilter annotation.
|
||||
*/
|
||||
protected pointcut executionOfAnnotatedMethod() : execution(* *(..)) && @annotation(PreFilter);
|
||||
|
||||
}
|
||||
-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.authorization.method.aspectj;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
|
||||
/**
|
||||
* Concrete AspectJ aspect using Spring Security @Secured annotation.
|
||||
*
|
||||
* <p>
|
||||
* When using this aspect, you <i>must</i> annotate the implementation class
|
||||
* (and/or methods within that class), <i>not</i> the interface (if any) that
|
||||
* the class implements. AspectJ follows Java's rule that annotations on
|
||||
* interfaces are <i>not</i> inherited. This will vary from Spring AOP.
|
||||
*
|
||||
* @author Mike Wiesner
|
||||
* @author Luke Taylor
|
||||
* @author Josh Cummings
|
||||
* @since 5.8
|
||||
*/
|
||||
public aspect SecuredAspect extends AbstractMethodInterceptorAspect {
|
||||
|
||||
/**
|
||||
* Matches the execution of any public method in a type with the Secured
|
||||
* annotation, or any subtype of a type with the Secured annotation.
|
||||
*/
|
||||
private pointcut executionOfAnyPublicMethodInAtSecuredType() :
|
||||
execution(public * ((@Secured *)+).*(..)) && @this(Secured);
|
||||
|
||||
/**
|
||||
* Matches the execution of any method with the Secured annotation.
|
||||
*/
|
||||
private pointcut executionOfSecuredMethod() :
|
||||
execution(* *(..)) && @annotation(Secured);
|
||||
|
||||
protected pointcut executionOfAnnotatedMethod() :
|
||||
executionOfAnyPublicMethodInAtSecuredType() ||
|
||||
executionOfSecuredMethod();
|
||||
}
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -101,7 +101,7 @@ public class AnnotationSecurityAspectTests {
|
||||
@Test
|
||||
public void securedClassMethodDeniesUnauthenticatedAccess() {
|
||||
assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
|
||||
.isThrownBy(() -> this.secured.securedClassMethod());
|
||||
.isThrownBy(() -> this.secured.securedClassMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -143,8 +143,8 @@ public class AnnotationSecurityAspectTests {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.anne);
|
||||
List<String> objects = this.prePostSecured.postFilterMethod();
|
||||
assertThat(objects).hasSize(2);
|
||||
assertThat(objects).contains("apple");
|
||||
assertThat(objects).contains("aubergine");
|
||||
assertThat(objects.contains("apple")).isTrue();
|
||||
assertThat(objects.contains("aubergine")).isTrue();
|
||||
}
|
||||
|
||||
private void configureForElAnnotations() {
|
||||
|
||||
-180
@@ -1,180 +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.authorization.method.aspectj;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.prepost.PostAuthorize;
|
||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @since 3.0.3
|
||||
*/
|
||||
public class PostAuthorizeAspectTests {
|
||||
|
||||
private TestingAuthenticationToken anne = new TestingAuthenticationToken("anne", "", "ROLE_A");
|
||||
|
||||
private MethodInterceptor interceptor;
|
||||
|
||||
private SecuredImpl secured = new SecuredImpl();
|
||||
|
||||
private SecuredImplSubclass securedSub = new SecuredImplSubclass();
|
||||
|
||||
private PrePostSecured prePostSecured = new PrePostSecured();
|
||||
|
||||
@BeforeEach
|
||||
public final void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.interceptor = AuthorizationManagerAfterMethodInterceptor.postAuthorize();
|
||||
PostAuthorizeAspect secAspect = PostAuthorizeAspect.aspectOf();
|
||||
secAspect.setSecurityInterceptor(this.interceptor);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void clearContext() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securedInterfaceMethodAllowsAllAccess() {
|
||||
this.secured.securedMethod();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securedClassMethodDeniesUnauthenticatedAccess() {
|
||||
assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
|
||||
.isThrownBy(() -> this.secured.securedClassMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securedClassMethodAllowsAccessToRoleA() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.anne);
|
||||
this.secured.securedClassMethod();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void internalPrivateCallIsIntercepted() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.anne);
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.publicCallsPrivate());
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.securedSub.publicCallsPrivate());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void protectedMethodIsIntercepted() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.anne);
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.protectedMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overriddenProtectedMethodIsNotIntercepted() {
|
||||
// AspectJ doesn't inherit annotations
|
||||
this.securedSub.protectedMethod();
|
||||
}
|
||||
|
||||
// SEC-1262
|
||||
@Test
|
||||
public void denyAllPreAuthorizeDeniesAccess() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.anne);
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.prePostSecured::denyAllMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedDenyAllPostAuthorizeDeniesAccess() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.anne);
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(() -> this.secured.myObject().denyAllMethod());
|
||||
}
|
||||
|
||||
interface SecuredInterface {
|
||||
|
||||
@PostAuthorize("hasRole('X')")
|
||||
void securedMethod();
|
||||
|
||||
}
|
||||
|
||||
static class SecuredImpl implements SecuredInterface {
|
||||
|
||||
// Not really secured because AspectJ doesn't inherit annotations from interfaces
|
||||
@Override
|
||||
public void securedMethod() {
|
||||
}
|
||||
|
||||
@PostAuthorize("hasRole('A')")
|
||||
void securedClassMethod() {
|
||||
}
|
||||
|
||||
@PostAuthorize("hasRole('X')")
|
||||
private void privateMethod() {
|
||||
}
|
||||
|
||||
@PostAuthorize("hasRole('X')")
|
||||
protected void protectedMethod() {
|
||||
}
|
||||
|
||||
@PostAuthorize("hasRole('X')")
|
||||
void publicCallsPrivate() {
|
||||
privateMethod();
|
||||
}
|
||||
|
||||
NestedObject myObject() {
|
||||
return new NestedObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SecuredImplSubclass extends SecuredImpl {
|
||||
|
||||
@Override
|
||||
protected void protectedMethod() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publicCallsPrivate() {
|
||||
super.publicCallsPrivate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class PrePostSecured {
|
||||
|
||||
@PostAuthorize("denyAll")
|
||||
void denyAllMethod() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NestedObject {
|
||||
|
||||
@PostAuthorize("denyAll")
|
||||
void denyAllMethod() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-84
@@ -1,84 +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.authorization.method.aspectj;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.security.access.prepost.PostFilter;
|
||||
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @since 3.0.3
|
||||
*/
|
||||
public class PostFilterAspectTests {
|
||||
|
||||
private MethodInterceptor interceptor;
|
||||
|
||||
private PrePostSecured prePostSecured = new PrePostSecured();
|
||||
|
||||
@BeforeEach
|
||||
public final void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.interceptor = new PostFilterAuthorizationMethodInterceptor();
|
||||
PostFilterAspect secAspect = PostFilterAspect.aspectOf();
|
||||
secAspect.setSecurityInterceptor(this.interceptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preFilterMethodWhenListThenFilters() {
|
||||
List<String> objects = new ArrayList<>(Arrays.asList("apple", "banana", "aubergine", "orange"));
|
||||
assertThat(this.prePostSecured.postFilterMethod(objects)).containsExactly("apple", "aubergine");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedDenyAllPostFilterDeniesAccess() {
|
||||
assertThat(this.prePostSecured.myObject().denyAllMethod()).isEmpty();
|
||||
}
|
||||
|
||||
static class PrePostSecured {
|
||||
|
||||
@PostFilter("filterObject.startsWith('a')")
|
||||
List<String> postFilterMethod(List<String> objects) {
|
||||
return objects;
|
||||
}
|
||||
|
||||
NestedObject myObject() {
|
||||
return new NestedObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NestedObject {
|
||||
|
||||
@PostFilter("filterObject == null")
|
||||
List<String> denyAllMethod() {
|
||||
return new ArrayList<>(List.of("deny"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-203
@@ -1,203 +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.authorization.method.aspectj;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @since 3.0.3
|
||||
*/
|
||||
public class PreAuthorizeAspectTests {
|
||||
|
||||
private TestingAuthenticationToken anne = new TestingAuthenticationToken("anne", "", "ROLE_A");
|
||||
|
||||
private MethodInterceptor interceptor;
|
||||
|
||||
private SecuredImpl secured = new SecuredImpl();
|
||||
|
||||
private SecuredImplSubclass securedSub = new SecuredImplSubclass();
|
||||
|
||||
private PrePostSecured prePostSecured = new PrePostSecured();
|
||||
|
||||
private MultipleInterfaces multiple = new MultipleInterfaces();
|
||||
|
||||
@BeforeEach
|
||||
public final void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.interceptor = AuthorizationManagerBeforeMethodInterceptor.preAuthorize();
|
||||
PreAuthorizeAspect secAspect = PreAuthorizeAspect.aspectOf();
|
||||
secAspect.setSecurityInterceptor(this.interceptor);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void clearContext() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securedInterfaceMethodAllowsAllAccess() {
|
||||
this.secured.securedMethod();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securedClassMethodDeniesUnauthenticatedAccess() {
|
||||
assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
|
||||
.isThrownBy(() -> this.secured.securedClassMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void securedClassMethodAllowsAccessToRoleA() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.anne);
|
||||
this.secured.securedClassMethod();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void internalPrivateCallIsIntercepted() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.anne);
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.publicCallsPrivate());
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.securedSub.publicCallsPrivate());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void protectedMethodIsIntercepted() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.anne);
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> this.secured.protectedMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void overriddenProtectedMethodIsNotIntercepted() {
|
||||
// AspectJ doesn't inherit annotations
|
||||
this.securedSub.protectedMethod();
|
||||
}
|
||||
|
||||
// SEC-1262
|
||||
@Test
|
||||
public void denyAllPreAuthorizeDeniesAccess() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.anne);
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.prePostSecured::denyAllMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedDenyAllPreAuthorizeDeniesAccess() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.anne);
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(() -> this.secured.myObject().denyAllMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleInterfacesPreAuthorizeAllows() {
|
||||
// aspectj doesn't inherit annotations
|
||||
this.multiple.securedMethod();
|
||||
}
|
||||
|
||||
interface SecuredInterface {
|
||||
|
||||
@PreAuthorize("hasRole('X')")
|
||||
void securedMethod();
|
||||
|
||||
}
|
||||
|
||||
static class SecuredImpl implements SecuredInterface {
|
||||
|
||||
// Not really secured because AspectJ doesn't inherit annotations from interfaces
|
||||
@Override
|
||||
public void securedMethod() {
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('A')")
|
||||
void securedClassMethod() {
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('X')")
|
||||
private void privateMethod() {
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('X')")
|
||||
protected void protectedMethod() {
|
||||
}
|
||||
|
||||
@PreAuthorize("hasRole('A')")
|
||||
void publicCallsPrivate() {
|
||||
privateMethod();
|
||||
}
|
||||
|
||||
NestedObject myObject() {
|
||||
return new NestedObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SecuredImplSubclass extends SecuredImpl {
|
||||
|
||||
@Override
|
||||
protected void protectedMethod() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publicCallsPrivate() {
|
||||
super.publicCallsPrivate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class PrePostSecured {
|
||||
|
||||
@PreAuthorize("denyAll")
|
||||
void denyAllMethod() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NestedObject {
|
||||
|
||||
@PreAuthorize("denyAll")
|
||||
void denyAllMethod() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface AnotherSecuredInterface {
|
||||
|
||||
@PreAuthorize("hasRole('Y')")
|
||||
void securedMethod();
|
||||
|
||||
}
|
||||
|
||||
static class MultipleInterfaces implements SecuredInterface, AnotherSecuredInterface {
|
||||
|
||||
@Override
|
||||
public void securedMethod() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-84
@@ -1,84 +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.authorization.method.aspectj;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.security.access.prepost.PreFilter;
|
||||
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Luke Taylor
|
||||
* @since 3.0.3
|
||||
*/
|
||||
public class PreFilterAspectTests {
|
||||
|
||||
private MethodInterceptor interceptor;
|
||||
|
||||
private PrePostSecured prePostSecured = new PrePostSecured();
|
||||
|
||||
@BeforeEach
|
||||
public final void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.interceptor = new PreFilterAuthorizationMethodInterceptor();
|
||||
PreFilterAspect secAspect = PreFilterAspect.aspectOf();
|
||||
secAspect.setSecurityInterceptor(this.interceptor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preFilterMethodWhenListThenFilters() {
|
||||
List<String> objects = new ArrayList<>(Arrays.asList("apple", "banana", "aubergine", "orange"));
|
||||
assertThat(this.prePostSecured.preFilterMethod(objects)).containsExactly("apple", "aubergine");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedDenyAllPreFilterDeniesAccess() {
|
||||
assertThat(this.prePostSecured.myObject().denyAllMethod(new ArrayList<>(List.of("deny")))).isEmpty();
|
||||
}
|
||||
|
||||
static class PrePostSecured {
|
||||
|
||||
@PreFilter("filterObject.startsWith('a')")
|
||||
List<String> preFilterMethod(List<String> objects) {
|
||||
return objects;
|
||||
}
|
||||
|
||||
NestedObject myObject() {
|
||||
return new NestedObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class NestedObject {
|
||||
|
||||
@PreFilter("filterObject == null")
|
||||
List<String> denyAllMethod(List<String> list) {
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user