Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b38b495630 | |||
| 5a7f12f1a9 |
@@ -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.
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
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;
|
||||
@@ -0,0 +1,314 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch: # Manual trigger
|
||||
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
GRADLE_ENTERPRISE_CACHE_USER: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
|
||||
GRADLE_ENTERPRISE_SECRET_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
COMMIT_OWNER: ${{ github.event.pusher.name }}
|
||||
COMMIT_SHA: ${{ github.sha }}
|
||||
STRUCTURE101_LICENSEID: ${{ secrets.STRUCTURE101_LICENSEID }}
|
||||
ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
prerequisites:
|
||||
name: Pre-requisites for building
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
outputs:
|
||||
runjobs: ${{ steps.continue.outputs.runjobs }}
|
||||
project_version: ${{ steps.continue.outputs.project_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- id: continue
|
||||
name: Determine if should continue
|
||||
run: |
|
||||
# Run jobs if in upstream repository
|
||||
echo "runjobs=true" >>$GITHUB_OUTPUT
|
||||
# Extract version from gradle.properties
|
||||
version=$(cat gradle.properties | grep "version=" | awk -F'=' '{print $2}')
|
||||
echo "project_version=$version" >>$GITHUB_OUTPUT
|
||||
build_jdk_11:
|
||||
name: Build JDK 11
|
||||
needs: [prerequisites]
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
if: needs.prerequisites.outputs.runjobs
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
cache: 'gradle'
|
||||
- name: Set up Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
- name: Set up gradle user name
|
||||
run: echo 'systemProp.user.name=spring-builds+github' >> gradle.properties
|
||||
- name: Build with Gradle
|
||||
env:
|
||||
GRADLE_ENTERPRISE_CACHE_USERNAME: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
run: ./gradlew clean build --continue -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD"
|
||||
snapshot_tests:
|
||||
name: Test against snapshots
|
||||
needs: [prerequisites]
|
||||
runs-on: ubuntu-latest
|
||||
if: needs.prerequisites.outputs.runjobs
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- name: Snapshot Tests
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
./gradlew test --refresh-dependencies -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" -PforceMavenRepositories=snapshot -PspringVersion='5.+' -PreactorVersion='20+' -PspringDataVersion='Neumann-BUILD-SNAPSHOT' -PrsocketVersion=1.1.0-SNAPSHOT -PspringBootVersion=2.4.0-SNAPSHOT -PlocksDisabled --stacktrace
|
||||
check_samples:
|
||||
name: Check Samples project
|
||||
needs: [prerequisites]
|
||||
runs-on: ubuntu-latest
|
||||
if: needs.prerequisites.outputs.runjobs
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- name: Check samples project
|
||||
env:
|
||||
LOCAL_REPOSITORY_PATH: ${{ github.workspace }}/build/publications/repos
|
||||
SAMPLES_DIR: ../spring-security-samples
|
||||
VERSION: ${{ needs.prerequisites.outputs.project_version }}
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
./gradlew publishMavenJavaPublicationToLocalRepository
|
||||
./gradlew cloneSamples -PcloneOutputDirectory="$SAMPLES_DIR"
|
||||
./gradlew --project-dir "$SAMPLES_DIR" --init-script spring-security-ci.gradle -PlocalRepositoryPath="$LOCAL_REPOSITORY_PATH" -PspringSecurityVersion="$VERSION" :runAllTests
|
||||
check_tangles:
|
||||
name: Check for Package Tangles
|
||||
needs: [ prerequisites ]
|
||||
runs-on: ubuntu-latest
|
||||
if: needs.prerequisites.outputs.runjobs
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- name: Check for package tangles
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
./gradlew check s101 -Ps101.licenseId="$STRUCTURE101_LICENSEID" --stacktrace
|
||||
deploy_artifacts:
|
||||
name: Deploy Artifacts
|
||||
needs: [build_jdk_11, snapshot_tests, check_samples, check_tangles]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- name: Deploy artifacts
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
./gradlew publishArtifacts finalizeDeployArtifacts -PossrhUsername="$OSSRH_TOKEN_USERNAME" -PossrhPassword="$OSSRH_TOKEN_PASSWORD" -PartifactoryUsername="$ARTIFACTORY_USERNAME" -PartifactoryPassword="$ARTIFACTORY_PASSWORD" --stacktrace
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_PASSPHRASE }}
|
||||
OSSRH_TOKEN_USERNAME: ${{ secrets.OSSRH_S01_TOKEN_USERNAME }}
|
||||
OSSRH_TOKEN_PASSWORD: ${{ secrets.OSSRH_S01_TOKEN_PASSWORD }}
|
||||
ARTIFACTORY_USERNAME: ${{ secrets.ARTIFACTORY_USERNAME }}
|
||||
ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
|
||||
deploy_docs:
|
||||
name: Deploy Docs
|
||||
needs: [build_jdk_11, snapshot_tests, check_samples, check_tangles]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- name: Deploy Docs
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
./gradlew deployDocs -PdeployDocsSshKey="$DOCS_SSH_KEY" -PdeployDocsSshUsername="$DOCS_USERNAME" -PdeployDocsHost="$DOCS_HOST" --stacktrace
|
||||
env:
|
||||
DOCS_USERNAME: ${{ secrets.DOCS_USERNAME }}
|
||||
DOCS_SSH_KEY: ${{ secrets.DOCS_SSH_KEY }}
|
||||
DOCS_HOST: ${{ secrets.DOCS_HOST }}
|
||||
deploy_schema:
|
||||
name: Deploy Schema
|
||||
needs: [build_jdk_11, snapshot_tests, check_samples, check_tangles]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- name: Deploy Schema
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
./gradlew deploySchema -PdeployDocsSshKey="$DOCS_SSH_KEY" -PdeployDocsSshUsername="$DOCS_USERNAME" -PdeployDocsHost="$DOCS_HOST" --stacktrace --info
|
||||
env:
|
||||
DOCS_USERNAME: ${{ secrets.DOCS_USERNAME }}
|
||||
DOCS_SSH_KEY: ${{ secrets.DOCS_SSH_KEY }}
|
||||
DOCS_HOST: ${{ secrets.DOCS_HOST }}
|
||||
perform_release:
|
||||
name: Perform release
|
||||
needs: [prerequisites, deploy_artifacts, deploy_docs, deploy_schema]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
timeout-minutes: 90
|
||||
if: ${{ !endsWith(needs.prerequisites.outputs.project_version, '-SNAPSHOT') }}
|
||||
env:
|
||||
REPO: ${{ github.repository }}
|
||||
BRANCH: ${{ github.ref_name }}
|
||||
TOKEN: ${{ github.token }}
|
||||
VERSION: ${{ needs.prerequisites.outputs.project_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- name: Wait for Artifactory Artifacts
|
||||
if: ${{ contains(needs.prerequisites.outputs.project_version, '-RC') || contains(needs.prerequisites.outputs.project_version, '-M') }}
|
||||
run: |
|
||||
echo "Wait for artifacts of $REPO@$VERSION to appear on Artifactory."
|
||||
until curl -f -s https://repo.spring.io/artifactory/milestone/org/springframework/security/spring-security-core/$VERSION/ > /dev/null
|
||||
do
|
||||
sleep 30
|
||||
echo "."
|
||||
done
|
||||
echo "Artifacts for $REPO@$VERSION have been released to Artifactory."
|
||||
- name: Wait for Maven Central Artifacts
|
||||
if: ${{ !contains(needs.prerequisites.outputs.project_version, '-RC') && !contains(needs.prerequisites.outputs.project_version, '-M') }}
|
||||
run: |
|
||||
echo "Wait for artifacts of $REPO@$VERSION to appear on Maven Central."
|
||||
until curl -f -s https://repo1.maven.org/maven2/org/springframework/security/spring-security-core/$VERSION/ > /dev/null
|
||||
do
|
||||
sleep 30
|
||||
echo "."
|
||||
done
|
||||
echo "Artifacts for $REPO@$VERSION have been released to Maven Central."
|
||||
- name: Create GitHub Release
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
echo "Tagging and publishing $REPO@$VERSION release on GitHub."
|
||||
./gradlew createGitHubRelease -PnextVersion=$VERSION -Pbranch=$BRANCH -PcreateRelease=true -PgitHubAccessToken=$TOKEN
|
||||
- name: Announce Release on Slack
|
||||
id: spring-security-announcing
|
||||
uses: slackapi/slack-github-action@v1.19.0
|
||||
with:
|
||||
payload: |
|
||||
{
|
||||
"text": "spring-security-announcing `${{ env.VERSION }}` is available now",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "spring-security-announcing `${{ env.VERSION }}` is available now"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SPRING_RELEASE_SLACK_WEBHOOK_URL }}
|
||||
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK
|
||||
- name: Setup git config
|
||||
run: |
|
||||
git config user.name 'github-actions[bot]'
|
||||
git config user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
- name: Update to next Snapshot Version
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
echo "Updating $REPO@$VERSION to next snapshot version."
|
||||
./gradlew :updateToSnapshotVersion
|
||||
git commit -am "Next development version"
|
||||
git push
|
||||
perform_post_release:
|
||||
name: Perform post-release
|
||||
needs: [prerequisites, deploy_artifacts, deploy_docs, deploy_schema]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
timeout-minutes: 90
|
||||
if: ${{ endsWith(needs.prerequisites.outputs.project_version, '-SNAPSHOT') }}
|
||||
env:
|
||||
TOKEN: ${{ github.token }}
|
||||
VERSION: ${{ needs.prerequisites.outputs.project_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- name: Schedule next release (if not already scheduled)
|
||||
run: ./gradlew scheduleNextRelease -PnextVersion=$VERSION -PgitHubAccessToken=$TOKEN
|
||||
notify_result:
|
||||
name: Check for failures
|
||||
needs: [perform_release, perform_post_release]
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
steps:
|
||||
- name: Send Slack message
|
||||
# Workaround while waiting for Gamesight/slack-workflow-status#38 to be fixed
|
||||
# See https://github.com/Gamesight/slack-workflow-status/issues/38
|
||||
uses: sjohnr/slack-workflow-status@v1-beta
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
channel: '#spring-security-ci'
|
||||
name: 'CI Notifier'
|
||||
@@ -0,0 +1,31 @@
|
||||
name: Deploy Docs
|
||||
on:
|
||||
push:
|
||||
branches-ignore: [ gh-pages ]
|
||||
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@v3
|
||||
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)
|
||||
@@ -0,0 +1,40 @@
|
||||
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
|
||||
notify_result:
|
||||
name: Check for failures
|
||||
needs: [spring-releasetrain-checks]
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
steps:
|
||||
- name: Send Slack message
|
||||
uses: Gamesight/slack-workflow-status@v1.0.1
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
channel: '#spring-security-ci'
|
||||
name: 'CI Notifier'
|
||||
@@ -0,0 +1,21 @@
|
||||
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@v3
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew clean build --continue --scan
|
||||
@@ -0,0 +1,80 @@
|
||||
name: Update Scheduled Release Version
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Manual trigger only. Triggered by release-scheduler.yml on main.
|
||||
|
||||
env:
|
||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
GRADLE_ENTERPRISE_CACHE_USER: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USER }}
|
||||
GRADLE_ENTERPRISE_CACHE_PASSWORD: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }}
|
||||
GRADLE_ENTERPRISE_SECRET_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_SECRET_ACCESS_KEY }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update_scheduled_release_version:
|
||||
name: Initiate Release If Scheduled
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
steps:
|
||||
- id: checkout-source
|
||||
name: Checkout Source Code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v1
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
- id: check-release-due
|
||||
name: Check Release Due
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
./gradlew gitHubCheckNextVersionDueToday
|
||||
echo "is_due_today=$(cat build/github/milestones/is-due-today)" >>$GITHUB_OUTPUT
|
||||
- id: check-open-issues
|
||||
name: Check for open issues
|
||||
if: steps.check-release-due.outputs.is_due_today == 'true'
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
./gradlew gitHubCheckMilestoneHasNoOpenIssues
|
||||
echo "is_open_issues=$(cat build/github/milestones/is-open-issues)" >>$GITHUB_OUTPUT
|
||||
- id: validate-release-state
|
||||
name: Validate State of Release
|
||||
if: steps.check-release-due.outputs.is_due_today == 'true' && steps.check-open-issues.outputs.is_open_issues == 'true'
|
||||
run: |
|
||||
echo "The release is due today but there are open issues"
|
||||
exit 1
|
||||
- id: update-version-and-push
|
||||
name: Update version and push
|
||||
if: steps.check-release-due.outputs.is_due_today == 'true' && steps.check-open-issues.outputs.is_open_issues == 'false'
|
||||
run: |
|
||||
export GRADLE_ENTERPRISE_CACHE_USERNAME="$GRADLE_ENTERPRISE_CACHE_USER"
|
||||
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
|
||||
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
|
||||
git config user.name 'github-actions[bot]'
|
||||
git config user.email 'github-actions[bot]@users.noreply.github.com'
|
||||
./gradlew :updateProjectVersion
|
||||
updatedVersion=$(cat gradle.properties | grep "version=" | awk -F'=' '{print $2}')
|
||||
git commit -am "Release $updatedVersion"
|
||||
git tag $updatedVersion
|
||||
git push
|
||||
git push origin $updatedVersion
|
||||
- id: send-slack-notification
|
||||
name: Send Slack message
|
||||
if: failure()
|
||||
uses: Gamesight/slack-workflow-status@v1.0.1
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
channel: '#spring-security-ci'
|
||||
name: 'CI Notifier'
|
||||
@@ -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
|
||||
}
|
||||
+8
-19
@@ -6,8 +6,8 @@ image:https://img.shields.io/badge/Revved%20up%20by-Gradle%20Enterprise-06A0CE?l
|
||||
|
||||
= 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].
|
||||
|
||||
@@ -30,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]
|
||||
@@ -40,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
|
||||
|
||||
+15
-110
@@ -1,89 +1,4 @@
|
||||
= Release Process
|
||||
|
||||
The release process for Spring Security is partially automated. The following table outlines which steps are automated and which are manual. Follow the links to read about each step.
|
||||
|
||||
[cols="1,1"]
|
||||
|===
|
||||
| Step | Status
|
||||
|
||||
| <<update-dependencies>>
|
||||
| :x: manual
|
||||
|
||||
| <<check-all-issues-are-closed>>
|
||||
| :white_check_mark: automated (scheduled release will abort if any issues are open)
|
||||
|
||||
| <<update-release-version>>
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<update-antora-version>>
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<build-locally>>
|
||||
| :x: manual (when updating dependencies)
|
||||
|
||||
| <<push-release-commit>>
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<announce-release-on-slack>>
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<tag-release>>
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<update-to-next-development-version>>
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<update-version-on-project-page>>
|
||||
| :x: manual
|
||||
|
||||
| <<update-release-notes-on-github>>
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<close-create-milestone,Close milestone>>
|
||||
| :x: manual (move issues to new milestone before release)
|
||||
|
||||
| <<close-create-milestone,Create milestone>>
|
||||
| :white_check_mark: automated (if not already created)
|
||||
|
||||
| <<announce-release-on-other-channels>>
|
||||
| :x: manual
|
||||
|===
|
||||
|
||||
*When should I update dependencies manually?* Dependencies should be updated at the latest the end of the week prior to the release. This is usually the Friday following the 2nd Monday of the month (counting from the first week with a Monday). When in doubt, check the https://github.com/spring-projects/spring-security/milestones[milestones] page for release due dates.
|
||||
|
||||
*When do scheduled releases occur?* Automated releases are scheduled to occur at *3:15 PM UTC* on the *3rd Monday of the month* (counting from the first week with a Monday).
|
||||
|
||||
[NOTE]
|
||||
The scheduled release process currently runs every Monday but only releases when a release is due. See the performed checks below for more information.
|
||||
|
||||
The automated release process occurs on the following branches:
|
||||
|
||||
* `main`
|
||||
* `5.8.x`
|
||||
* `5.7.x`
|
||||
* `5.6.x`
|
||||
|
||||
For each of the above branches, the automated process performs the following checks before proceeding with the release:
|
||||
|
||||
1. _Check if the milestone is due today._ This check compares the current (SNAPSHOT) version of the branch with available milestones and chooses the first match (sorted alphabetically). If the due date on the matched milestone is *not* today, the process stops.
|
||||
2. _Check if all issues are closed._ This check uses the milestone from the previous step and looks for open issues. If any open issues are found, the process stops.
|
||||
|
||||
[IMPORTANT]
|
||||
You should ensure all issues are closed or moved to another milestone prior to a scheduled release.
|
||||
|
||||
If the above checks pass, the version number is updated (in `gradle.properties` and `antora.yml`) and a commit is pushed to trigger the CI process.
|
||||
|
||||
*How do I trigger a release manually?* You can trigger a release manually in two ways:
|
||||
|
||||
1. Trigger a release for a particular branch via https://github.com/spring-projects/spring-security/actions/workflows/update-scheduled-release-version.yml[`update-scheduled-release-version.yml`] on the desired branch. The above checks are performed for that branch, and the release will proceed if all checks pass. _This is the recommended way to trigger a release that did not pass the above checks during a regularly scheduled release._
|
||||
2. Trigger releases for all branches via https://github.com/spring-projects/spring-security/actions/workflows/release-scheduler.yml[`release-scheduler.yml`] on the `main` branch. The above checks are performed for each branch, and only releases that pass all checks will proceed.
|
||||
|
||||
*When should additional manual steps be performed?* All other automated steps listed above occur during the normal CI process. Additional manual steps can be performed at any time once the builds pass and releases are finished.
|
||||
|
||||
*What if something goes wrong?* If the normal CI process fails, you can retry by re-running the failed jobs with the "Re-run failed jobs" option in GitHub Actions. If changes are required, you should revert the "Release x.y.z" commit, delete the tag, and proceed manually.
|
||||
|
||||
[#update-dependencies]
|
||||
== Update dependencies
|
||||
= Update Dependencies
|
||||
|
||||
Ensure you have no changes in your local repository.
|
||||
Change to a new branch.
|
||||
@@ -144,8 +59,7 @@ $ ./gradlew updateDependencies -PupdateMode=GITHUB_ISSUE -PgitHubAccessToken=<gi
|
||||
|
||||
Apply any fixes from your previous branch that were necessary.
|
||||
|
||||
[#check-all-issues-are-closed]
|
||||
== Check all issues are closed
|
||||
= 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:
|
||||
@@ -160,13 +74,11 @@ $ ./gradlew gitHubCheckMilestoneHasNoOpenIssues -PgitHubAccessToken=<github-pers
|
||||
|
||||
Alternatively, you can manually check using https://github.com/spring-projects/spring-security/milestones
|
||||
|
||||
[#update-release-version]
|
||||
== Update release version
|
||||
= Update Release Version
|
||||
|
||||
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]
|
||||
== Update antora version
|
||||
= 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:
|
||||
@@ -176,8 +88,7 @@ If you are unsure of what the values should be, the following task will instruct
|
||||
./gradlew :spring-security-docs:antoraCheckVersion
|
||||
----
|
||||
|
||||
[#build-locally]
|
||||
== Build locally
|
||||
= Build Locally
|
||||
|
||||
Run the build using
|
||||
|
||||
@@ -186,8 +97,7 @@ Run the build using
|
||||
$ ./gradlew check
|
||||
----
|
||||
|
||||
[#push-release-commit]
|
||||
== Push release commit
|
||||
= Push the Release Commit
|
||||
|
||||
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:
|
||||
@@ -197,8 +107,7 @@ If you are pushing to Maven Central, then you can get notified when it’s uploa
|
||||
$ ./scripts/release/wait-for-done.sh 5.5.0
|
||||
----
|
||||
|
||||
[#announce-release-on-slack]
|
||||
== Announce release on Slack
|
||||
= Announce the release on Slack
|
||||
|
||||
* Announce via Slack on
|
||||
https://pivotal.slack.com/messages/spring-release[#spring-release],
|
||||
@@ -209,8 +118,7 @@ Something like:
|
||||
spring-security-announcing 5.5.0 is available.
|
||||
....
|
||||
|
||||
[#tag-release]
|
||||
== Tag release
|
||||
= Tag the release
|
||||
|
||||
* Tag the release and then push the tag
|
||||
|
||||
@@ -219,13 +127,11 @@ git tag 5.4.0-RC1
|
||||
git push origin 5.4.0-RC1
|
||||
....
|
||||
|
||||
[#update-to-next-development-version]
|
||||
== Update to next development version
|
||||
== 7. Update to Next Development Version
|
||||
|
||||
* Update `gradle.properties` version to next `+SNAPSHOT+` version, update antora.yml, and then push
|
||||
|
||||
[#update-version-on-project-page]
|
||||
== Update version on project page
|
||||
== 8. Update version on project page
|
||||
|
||||
The following command will update https://spring.io/projects/spring-security#learn with the new release version using the following parameters
|
||||
|
||||
@@ -238,8 +144,9 @@ The following command will update https://spring.io/projects/spring-security#lea
|
||||
$ ./gradlew saganCreateRelease saganDeleteRelease -PgitHubAccessToken=<github-personal-access-token> -PnextVersion=<next-version> -PpreviousVersion=<previous-version>
|
||||
----
|
||||
|
||||
[#update-release-notes-on-github]
|
||||
== Update release notes on GitHub
|
||||
|
||||
|
||||
== 9. Update Release Notes on GitHub
|
||||
|
||||
Generate the Release Notes replacing:
|
||||
|
||||
@@ -260,8 +167,7 @@ cat build/changelog/release-notes.md | xclip -selection clipboard
|
||||
https://github.com/spring-projects/spring-security/releases[release on
|
||||
GitHub], associate it with the tag, and paste the generated notes
|
||||
|
||||
[#close-create-milestone]
|
||||
== Close / Create milestone
|
||||
== 10. Close / Create Milestone
|
||||
|
||||
* In
|
||||
https://github.com/spring-projects/spring-security/milestones[GitHub
|
||||
@@ -270,8 +176,7 @@ Milestones], create a new milestone for the next release version
|
||||
the new milestone
|
||||
* Close the milestone for the release.
|
||||
|
||||
[#announce-release-on-other-channels]
|
||||
== Announce release on other channels
|
||||
== 11. Announce the release on other channels
|
||||
|
||||
* Create a https://spring.io/admin/blog[Blog]
|
||||
* Tweet from [@SpringSecurity](https://twitter.com/springsecurity)
|
||||
|
||||
@@ -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"
|
||||
|
||||
+4
-20
@@ -27,9 +27,7 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -48,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;
|
||||
@@ -86,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())
|
||||
@@ -151,15 +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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
-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) {
|
||||
|
||||
@@ -579,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");
|
||||
|
||||
+1
-16
@@ -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
|
||||
@@ -477,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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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
-2
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
+6
-7
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -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
-22
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -29,13 +29,9 @@ 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.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* @author Rob Winch
|
||||
@@ -44,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;
|
||||
@@ -62,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
|
||||
@@ -86,16 +76,6 @@ public class AclAuthorizationStrategyImplTests {
|
||||
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 {
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
+11
-11
@@ -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
-1
@@ -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));
|
||||
|
||||
@@ -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]");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+21
-51
@@ -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
-1
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -168,20 +168,20 @@ public class JdbcAclServiceTests {
|
||||
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);
|
||||
|
||||
+12
-30
@@ -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);
|
||||
|
||||
+1
-1
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
+1
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.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 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.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 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.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 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.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 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.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 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.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 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.security.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();
|
||||
}
|
||||
+1
-1
@@ -101,7 +101,7 @@ public class AnnotationSecurityAspectTests {
|
||||
@Test
|
||||
public void securedClassMethodDeniesUnauthenticatedAccess() {
|
||||
assertThatExceptionOfType(AuthenticationCredentialsNotFoundException.class)
|
||||
.isThrownBy(() -> this.secured.securedClassMethod());
|
||||
.isThrownBy(() -> this.secured.securedClassMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
-160
@@ -1,160 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.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);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SecuredImplSubclass extends SecuredImpl {
|
||||
|
||||
@Override
|
||||
protected void protectedMethod() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publicCallsPrivate() {
|
||||
super.publicCallsPrivate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class PrePostSecured {
|
||||
|
||||
@PostAuthorize("denyAll")
|
||||
void denyAllMethod() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.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");
|
||||
}
|
||||
|
||||
static class PrePostSecured {
|
||||
|
||||
@PostFilter("filterObject.startsWith('a')")
|
||||
List<String> postFilterMethod(List<String> objects) {
|
||||
return objects;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-160
@@ -1,160 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.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();
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
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('X')")
|
||||
void publicCallsPrivate() {
|
||||
privateMethod();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SecuredImplSubclass extends SecuredImpl {
|
||||
|
||||
@Override
|
||||
protected void protectedMethod() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publicCallsPrivate() {
|
||||
super.publicCallsPrivate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class PrePostSecured {
|
||||
|
||||
@PreAuthorize("denyAll")
|
||||
void denyAllMethod() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.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");
|
||||
}
|
||||
|
||||
static class PrePostSecured {
|
||||
|
||||
@PreFilter("filterObject.startsWith('a')")
|
||||
List<String> preFilterMethod(List<String> objects) {
|
||||
return objects;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.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.annotation.Secured;
|
||||
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 SecuredAspectTests {
|
||||
|
||||
private TestingAuthenticationToken anne = new TestingAuthenticationToken("anne", "", "ROLE_A");
|
||||
|
||||
private MethodInterceptor interceptor;
|
||||
|
||||
private SecuredImpl secured = new SecuredImpl();
|
||||
|
||||
private SecuredImplSubclass securedSub = new SecuredImplSubclass();
|
||||
|
||||
@BeforeEach
|
||||
public final void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.interceptor = AuthorizationManagerBeforeMethodInterceptor.secured();
|
||||
SecuredAspect secAspect = SecuredAspect.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();
|
||||
}
|
||||
|
||||
interface SecuredInterface {
|
||||
|
||||
@Secured("ROLE_X")
|
||||
void securedMethod();
|
||||
|
||||
}
|
||||
|
||||
static class SecuredImpl implements SecuredInterface {
|
||||
|
||||
// Not really secured because AspectJ doesn't inherit annotations from interfaces
|
||||
@Override
|
||||
public void securedMethod() {
|
||||
}
|
||||
|
||||
@Secured("ROLE_A")
|
||||
void securedClassMethod() {
|
||||
}
|
||||
|
||||
@Secured("ROLE_X")
|
||||
private void privateMethod() {
|
||||
}
|
||||
|
||||
@Secured("ROLE_X")
|
||||
protected void protectedMethod() {
|
||||
}
|
||||
|
||||
@Secured("ROLE_X")
|
||||
void publicCallsPrivate() {
|
||||
privateMethod();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class SecuredImplSubclass extends SecuredImpl {
|
||||
|
||||
@Override
|
||||
protected void protectedMethod() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publicCallsPrivate() {
|
||||
super.publicCallsPrivate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+80
-24
@@ -1,16 +1,15 @@
|
||||
import io.spring.gradle.IncludeRepoTask
|
||||
import trang.RncToXsd
|
||||
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath libs.io.spring.javaformat.spring.javaformat.gradle.plugin
|
||||
classpath libs.io.spring.nohttp.nohttp.gradle
|
||||
classpath libs.io.freefair.gradle.aspectj.plugin
|
||||
classpath libs.org.jetbrains.kotlin.kotlin.gradle.plugin
|
||||
classpath libs.com.netflix.nebula.nebula.project.plugin
|
||||
classpath "io.spring.javaformat:spring-javaformat-gradle-plugin:$springJavaformatVersion"
|
||||
classpath 'io.spring.nohttp:nohttp-gradle:0.0.11'
|
||||
classpath "io.freefair.gradle:aspectj-plugin:6.4.3.1"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
classpath "com.netflix.nebula:nebula-project-plugin:8.2.0"
|
||||
}
|
||||
repositories {
|
||||
maven { url 'https://plugins.gradle.org/m2/' }
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,12 +18,12 @@ apply plugin: 'locks'
|
||||
apply plugin: 's101'
|
||||
apply plugin: 'io.spring.convention.root'
|
||||
apply plugin: 'org.jetbrains.kotlin.jvm'
|
||||
apply plugin: 'org.springframework.security.update-dependencies'
|
||||
apply plugin: 'org.springframework.security.update-version'
|
||||
apply plugin: 'org.springframework.security.sagan'
|
||||
apply plugin: 'org.springframework.github.milestone'
|
||||
apply plugin: 'org.springframework.github.changelog'
|
||||
apply plugin: 'org.springframework.github.release'
|
||||
apply plugin: 'org.springframework.security.versions.verify-dependencies-versions'
|
||||
|
||||
group = 'org.springframework.security'
|
||||
description = 'Spring Security'
|
||||
@@ -86,15 +85,79 @@ tasks.named("dispatchGitHubWorkflow") {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named("updateDependencies") {
|
||||
// we aren't Gradle 7 compatible yet
|
||||
checkForGradleUpdate = false
|
||||
}
|
||||
|
||||
updateDependenciesSettings {
|
||||
gitHub {
|
||||
organization = "spring-projects"
|
||||
repository = "spring-security"
|
||||
}
|
||||
addFiles({
|
||||
return [
|
||||
project.file("buildSrc/src/main/groovy/io/spring/gradle/convention/CheckstylePlugin.groovy")
|
||||
]
|
||||
})
|
||||
dependencyExcludes {
|
||||
majorVersionBump()
|
||||
minorVersionBump()
|
||||
releaseCandidatesVersions()
|
||||
milestoneVersions()
|
||||
alphaBetaVersions()
|
||||
snapshotVersions()
|
||||
addRule { components ->
|
||||
components.withModule("commons-codec:commons-codec") { selection ->
|
||||
ModuleComponentIdentifier candidate = selection.getCandidate();
|
||||
if (!candidate.getVersion().equals(selection.getCurrentVersion())) {
|
||||
selection.reject("commons-codec updates break saml tests");
|
||||
}
|
||||
}
|
||||
components.withModule("org.python:jython") { selection ->
|
||||
ModuleComponentIdentifier candidate = selection.getCandidate();
|
||||
if (!candidate.getVersion().equals(selection.getCurrentVersion())) {
|
||||
selection.reject("jython updates break integration tests");
|
||||
}
|
||||
}
|
||||
components.withModule("com.nimbusds:nimbus-jose-jwt") { selection ->
|
||||
ModuleComponentIdentifier candidate = selection.getCandidate();
|
||||
if (!candidate.getVersion().equals(selection.getCurrentVersion())) {
|
||||
selection.reject("nimbus-jose-jwt gets updated when oauth2-oidc-sdk is updated to ensure consistency");
|
||||
}
|
||||
}
|
||||
components.all { selection ->
|
||||
ModuleComponentIdentifier candidate = selection.getCandidate();
|
||||
// Do not compare version due to multiple versions existing
|
||||
// will cause opensaml 3.x to be updated to 4.x
|
||||
if (candidate.getGroup().equals("org.opensaml")) {
|
||||
selection.reject("org.opensaml maintains two different versions, so it must be updated manually");
|
||||
}
|
||||
}
|
||||
components.withModule("io.spring.javaformat:spring-javaformat-gradle-plugin") { selection ->
|
||||
ModuleComponentIdentifier candidate = selection.getCandidate();
|
||||
if (!candidate.getVersion().equals(selection.getCurrentVersion())) {
|
||||
selection.reject("spring-javaformat-gradle-plugin updates break checkstyle");
|
||||
}
|
||||
}
|
||||
components.withModule("io.spring.javaformat:spring-javaformat-checkstyle") { selection ->
|
||||
ModuleComponentIdentifier candidate = selection.getCandidate();
|
||||
if (!candidate.getVersion().equals(selection.getCurrentVersion())) {
|
||||
selection.reject("spring-javaformat-checkstyle updates break checkstyle");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
plugins.withType(JavaPlugin) {
|
||||
java {
|
||||
sourceCompatibility=JavaVersion.VERSION_17
|
||||
}
|
||||
project.sourceCompatibility='1.8'
|
||||
}
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = "UTF-8"
|
||||
options.compilerArgs.add("-parameters")
|
||||
options.release = 8
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +170,7 @@ allprojects {
|
||||
pluginManager.withPlugin("io.spring.convention.checkstyle", { plugin ->
|
||||
configure(plugin) {
|
||||
dependencies {
|
||||
checkstyle libs.io.spring.javaformat.spring.javaformat.checkstyle
|
||||
checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:$springJavaformatVersion"
|
||||
}
|
||||
checkstyle {
|
||||
toolVersion = '8.34'
|
||||
@@ -123,12 +186,6 @@ allprojects {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
javaCompiler = javaToolchains.compilerFor {
|
||||
languageVersion = JavaLanguageVersion.of(17)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasProperty('buildScan')) {
|
||||
@@ -140,14 +197,13 @@ if (hasProperty('buildScan')) {
|
||||
|
||||
nohttp {
|
||||
source.exclude "buildSrc/build/**"
|
||||
source.builtBy(project(':spring-security-config').tasks.withType(RncToXsd))
|
||||
|
||||
}
|
||||
|
||||
tasks.register('cloneRepository', IncludeRepoTask) {
|
||||
repository = project.getProperties().get("repositoryName")
|
||||
ref = project.getProperties().get("ref")
|
||||
var defaultDirectory = project.file("build/tmp/clone")
|
||||
outputDirectory = project.hasProperty("cloneOutputDirectory") ? project.file("$cloneOutputDirectory") : defaultDirectory
|
||||
tasks.register('cloneSamples', IncludeRepoTask) {
|
||||
repository = 'spring-projects/spring-security-samples'
|
||||
ref = samplesBranch
|
||||
outputDirectory = project.hasProperty("cloneOutputDirectory") ? project.file("$cloneOutputDirectory") : project.file("build/samples")
|
||||
}
|
||||
|
||||
s101 {
|
||||
|
||||
+29
-32
@@ -2,11 +2,10 @@ plugins {
|
||||
id "java-gradle-plugin"
|
||||
id "java"
|
||||
id "groovy"
|
||||
id 'com.apollographql.apollo' version '2.4.5'
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
@@ -42,6 +41,10 @@ gradlePlugin {
|
||||
id = "io.spring.convention.management-configuration"
|
||||
implementationClass = "io.spring.gradle.convention.ManagementConfigurationPlugin"
|
||||
}
|
||||
updateDependencies {
|
||||
id = "org.springframework.security.update-dependencies"
|
||||
implementationClass = "org.springframework.security.convention.versions.UpdateDependenciesPlugin"
|
||||
}
|
||||
updateProjectVersion {
|
||||
id = "org.springframework.security.update-version"
|
||||
implementationClass = "org.springframework.security.convention.versions.UpdateProjectVersionPlugin"
|
||||
@@ -66,10 +69,6 @@ gradlePlugin {
|
||||
id = "s101"
|
||||
implementationClass = "s101.S101Plugin"
|
||||
}
|
||||
verifyDependenciesVersions {
|
||||
id = "org.springframework.security.versions.verify-dependencies-versions"
|
||||
implementationClass = "org.springframework.security.convention.versions.VerifyDependenciesVersionsPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,39 +79,37 @@ configurations {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation platform(libs.io.projectreactor.reactor.bom)
|
||||
|
||||
implementation libs.com.google.code.gson.gson
|
||||
implementation libs.com.thaiopensource.trag
|
||||
implementation libs.net.sourceforge.saxon.saxon
|
||||
implementation libs.org.yaml.snakeyaml
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
implementation 'com.thaiopensource:trang:20091111'
|
||||
implementation 'net.sourceforge.saxon:saxon:9.1.0.8'
|
||||
implementation 'org.yaml:snakeyaml:1.30'
|
||||
implementation localGroovy()
|
||||
|
||||
implementation libs.io.github.gradle.nexus.publish.plugin
|
||||
implementation 'io.projectreactor:reactor-core'
|
||||
implementation libs.org.gretty.gretty
|
||||
implementation libs.com.github.ben.manes.gradle.versions.plugin
|
||||
implementation libs.com.github.spullara.mustache.java.compiler
|
||||
implementation libs.io.spring.javaformat.spring.javaformat.gradle.plugin
|
||||
implementation libs.io.spring.nohttp.nohttp.gradle
|
||||
implementation libs.net.sourceforge.htmlunit
|
||||
implementation libs.org.hidetake.gradle.ssh.plugin
|
||||
implementation libs.org.jfrog.buildinfo.build.info.extractor.gradle
|
||||
implementation libs.org.sonarsource.scanner.gradle.sonarqube.gradle.plugin
|
||||
implementation libs.com.squareup.okhttp3.okhttp
|
||||
implementation 'io.github.gradle-nexus:publish-plugin:1.1.0'
|
||||
implementation 'io.projectreactor:reactor-core:3.5.0'
|
||||
implementation 'org.gretty:gretty:3.0.9'
|
||||
implementation 'com.apollographql.apollo:apollo-runtime:2.4.5'
|
||||
implementation 'com.github.ben-manes:gradle-versions-plugin:0.38.0'
|
||||
implementation 'com.github.spullara.mustache.java:compiler:0.9.4'
|
||||
implementation 'io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.15'
|
||||
implementation 'io.spring.nohttp:nohttp-gradle:0.0.11'
|
||||
implementation 'net.sourceforge.htmlunit:htmlunit:2.37.0'
|
||||
implementation 'org.hidetake:gradle-ssh-plugin:2.10.1'
|
||||
implementation 'org.jfrog.buildinfo:build-info-extractor-gradle:4.29.0'
|
||||
implementation 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1'
|
||||
|
||||
testImplementation platform(libs.org.junit.junit.bom)
|
||||
testImplementation platform(libs.org.mockito.mockito.bom)
|
||||
testImplementation platform('org.junit:junit-bom:5.8.2')
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-api"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-params"
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-engine"
|
||||
testImplementation libs.org.apache.commons.commons.io
|
||||
testImplementation libs.org.assertj.assertj.core
|
||||
testImplementation 'org.mockito:mockito-core'
|
||||
testImplementation 'org.mockito:mockito-junit-jupiter'
|
||||
testImplementation libs.com.squareup.okhttp3.mockwebserver
|
||||
testImplementation 'org.apache.commons:commons-io:1.3.2'
|
||||
testImplementation 'org.assertj:assertj-core:3.13.2'
|
||||
testImplementation 'org.mockito:mockito-core:3.12.4'
|
||||
testImplementation 'org.mockito:mockito-junit-jupiter:3.12.4'
|
||||
testImplementation "com.squareup.okhttp3:mockwebserver:3.14.9"
|
||||
}
|
||||
|
||||
|
||||
tasks.named('test', Test).configure {
|
||||
onlyIf { !project.hasProperty("buildSrc.skipTests") }
|
||||
useJUnitPlatform()
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
libs {
|
||||
from(files("../gradle/libs.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,6 @@ public abstract class AbstractSpringJavaPlugin implements Plugin<Project> {
|
||||
pluginManager.apply("io.spring.convention.javadoc-options");
|
||||
pluginManager.apply("io.spring.convention.checkstyle");
|
||||
pluginManager.apply(CopyPropertiesPlugin);
|
||||
pluginManager.apply("io.spring.convention.eclipse");
|
||||
|
||||
project.jar {
|
||||
manifest.attributes["Created-By"] =
|
||||
|
||||
@@ -21,38 +21,17 @@ import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
|
||||
|
||||
class ArtifactoryPlugin implements Plugin<Project> {
|
||||
|
||||
private static final String ARTIFACTORY_URL_NAME = "ARTIFACTORY_URL"
|
||||
|
||||
private static final String ARTIFACTORY_SNAPSHOT_REPOSITORY = "ARTIFACTORY_SNAPSHOT_REPOSITORY"
|
||||
|
||||
private static final String ARTIFACTORY_MILESTONE_REPOSITORY = "ARTIFACTORY_MILESTONE_REPOSITORY"
|
||||
|
||||
private static final String ARTIFACTORY_RELEASE_REPOSITORY = "ARTIFACTORY_RELEASE_REPOSITORY"
|
||||
|
||||
private static final String DEFAULT_ARTIFACTORY_URL = "https://repo.spring.io"
|
||||
|
||||
private static final String DEFAULT_ARTIFACTORY_SNAPSHOT_REPOSITORY = "libs-snapshot-local"
|
||||
|
||||
private static final String DEFAULT_ARTIFACTORY_MILESTONE_REPOSITORY = "libs-milestone-local"
|
||||
|
||||
private static final String DEFAULT_ARTIFACTORY_RELEASE_REPOSITORY = "libs-release-local"
|
||||
|
||||
@Override
|
||||
void apply(Project project) {
|
||||
project.plugins.apply('com.jfrog.artifactory')
|
||||
String name = Utils.getProjectName(project);
|
||||
boolean isSnapshot = Utils.isSnapshot(project);
|
||||
boolean isMilestone = Utils.isMilestone(project);
|
||||
Map<String, String> env = System.getenv()
|
||||
String artifactoryUrl = env.getOrDefault(ARTIFACTORY_URL_NAME, DEFAULT_ARTIFACTORY_URL)
|
||||
String snapshotRepository = env.getOrDefault(ARTIFACTORY_SNAPSHOT_REPOSITORY, DEFAULT_ARTIFACTORY_SNAPSHOT_REPOSITORY)
|
||||
String milestoneRepository = env.getOrDefault(ARTIFACTORY_MILESTONE_REPOSITORY, DEFAULT_ARTIFACTORY_MILESTONE_REPOSITORY)
|
||||
String releaseRepository = env.getOrDefault(ARTIFACTORY_RELEASE_REPOSITORY, DEFAULT_ARTIFACTORY_RELEASE_REPOSITORY)
|
||||
project.artifactory {
|
||||
contextUrl = artifactoryUrl
|
||||
contextUrl = 'https://repo.spring.io'
|
||||
publish {
|
||||
repository {
|
||||
repoKey = isSnapshot ? snapshotRepository : isMilestone ? milestoneRepository : releaseRepository
|
||||
repoKey = isSnapshot ? 'libs-snapshot-local' : isMilestone ? 'libs-milestone-local' : 'libs-release-local'
|
||||
if(project.hasProperty('artifactoryUsername')) {
|
||||
username = artifactoryUsername
|
||||
password = artifactoryPassword
|
||||
|
||||
@@ -18,7 +18,6 @@ package io.spring.gradle.convention
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.VersionCatalogsExtension
|
||||
import org.gradle.api.plugins.JavaPlugin
|
||||
|
||||
/**
|
||||
@@ -32,14 +31,12 @@ class CheckstylePlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
void apply(Project project) {
|
||||
def versionCatalog = project.rootProject.extensions.getByType(VersionCatalogsExtension.class)
|
||||
.named("libs")
|
||||
project.plugins.withType(JavaPlugin) {
|
||||
def checkstyleDir = project.rootProject.file(CHECKSTYLE_DIR)
|
||||
if (checkstyleDir.exists() && checkstyleDir.directory) {
|
||||
project.getPluginManager().apply('checkstyle')
|
||||
project.dependencies.add('checkstyle', versionCatalog.findLibrary('io-spring-javaformat-spring-javaformat-checkstyle').get())
|
||||
project.dependencies.add('checkstyle', versionCatalog.findLibrary('io-spring-nohttp-nohttp-checkstyle').get())
|
||||
project.dependencies.add('checkstyle', 'io.spring.javaformat:spring-javaformat-checkstyle:0.0.15')
|
||||
project.dependencies.add('checkstyle', 'io.spring.nohttp:nohttp-checkstyle:0.0.11')
|
||||
|
||||
project.checkstyle {
|
||||
configDirectory = checkstyleDir
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package io.spring.gradle.convention
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.plugins.ide.eclipse.EclipsePlugin
|
||||
|
||||
/**
|
||||
* Plugin to tweak .classpath settings so that eclipse/sts/vscode can
|
||||
* be imported cleanly.
|
||||
*
|
||||
* @author Janne Valkealahti
|
||||
*/
|
||||
class EclipsePlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
void apply(Project project) {
|
||||
project.plugins.apply(EclipsePlugin)
|
||||
|
||||
project.eclipse {
|
||||
classpath {
|
||||
file {
|
||||
whenMerged {
|
||||
// for aspects gradle eclipse integration wrongly creates lib entries for
|
||||
// aspects/build/classes/java/main and aspects/build/resources/main which
|
||||
// never has anything. eclipse/sts don't care, vscode language server
|
||||
// complains about missing folders. So remove those from a .classpath
|
||||
entries.removeAll {
|
||||
entry -> entry.kind == 'lib' && (entry.path.contains('aspects/build/classes/java/main') || entry.path.contains('aspects/build/resources/main'))
|
||||
}
|
||||
// there are cycles and then dependencies between projects main sources and
|
||||
// test sources. Looks like gradle eclipse integration is getting confused.
|
||||
// thus just relax rules so that projects always sees everything from
|
||||
// dependant project.
|
||||
entries
|
||||
.findAll { entry -> entry instanceof org.gradle.plugins.ide.eclipse.model.ProjectDependency }
|
||||
.each {
|
||||
it.entryAttributes['without_test_code'] = 'false'
|
||||
it.entryAttributes['test'] = 'false'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
jdt {
|
||||
file {
|
||||
withProperties { properties ->
|
||||
// there are cycles and then dependencies between projects main sources and
|
||||
// test sources. Relax those from error to warning
|
||||
properties['org.eclipse.jdt.core.circularClasspath'] = 'warning'
|
||||
properties['org.eclipse.jdt.core.incompleteClasspath'] = 'warning'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -52,15 +52,6 @@ public class IntegrationTestPlugin implements Plugin<Project> {
|
||||
// ensure we don't add if no tests to avoid adding Gretty
|
||||
return
|
||||
}
|
||||
project.sourceSets {
|
||||
integrationTest {
|
||||
java.srcDir project.file('src/integration-test/java')
|
||||
resources.srcDir project.file('src/integration-test/resources')
|
||||
compileClasspath = project.sourceSets.main.output + project.sourceSets.test.output + project.configurations.integrationTestCompileClasspath
|
||||
runtimeClasspath = output + compileClasspath + project.configurations.integrationTestRuntimeClasspath
|
||||
}
|
||||
}
|
||||
|
||||
project.configurations {
|
||||
integrationTestCompile {
|
||||
extendsFrom testImplementation
|
||||
@@ -70,9 +61,20 @@ public class IntegrationTestPlugin implements Plugin<Project> {
|
||||
}
|
||||
integrationTestCompileClasspath {
|
||||
extendsFrom integrationTestCompile
|
||||
canBeResolved = true
|
||||
}
|
||||
integrationTestRuntimeClasspath {
|
||||
extendsFrom integrationTestRuntime
|
||||
canBeResolved = true
|
||||
}
|
||||
}
|
||||
|
||||
project.sourceSets {
|
||||
integrationTest {
|
||||
java.srcDir project.file('src/integration-test/java')
|
||||
resources.srcDir project.file('src/integration-test/resources')
|
||||
compileClasspath = project.sourceSets.main.output + project.sourceSets.test.output + project.configurations.integrationTestCompileClasspath
|
||||
runtimeClasspath = output + compileClasspath + project.configurations.integrationTestRuntimeClasspath
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class JacocoPlugin implements Plugin<Project> {
|
||||
project.tasks.check.dependsOn project.tasks.jacocoTestReport
|
||||
|
||||
project.jacoco {
|
||||
toolVersion = '0.8.9'
|
||||
toolVersion = '0.8.2'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-11
@@ -34,14 +34,6 @@ class RepositoryConventionPlugin implements Plugin<Project> {
|
||||
if (forceMavenRepositories?.contains('local')) {
|
||||
mavenLocal()
|
||||
}
|
||||
maven {
|
||||
name = 'shibboleth'
|
||||
url = 'https://build.shibboleth.net/nexus/content/repositories/releases/'
|
||||
content {
|
||||
includeGroupByRegex('org\\.opensaml.*')
|
||||
includeGroupByRegex('net\\.shibboleth.*')
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
if (isSnapshot) {
|
||||
maven {
|
||||
@@ -75,11 +67,12 @@ class RepositoryConventionPlugin implements Plugin<Project> {
|
||||
password project.artifactoryPassword
|
||||
}
|
||||
}
|
||||
content {
|
||||
excludeGroup('net.minidev')
|
||||
}
|
||||
url = 'https://repo.spring.io/release/'
|
||||
}
|
||||
maven {
|
||||
name = 'shibboleth'
|
||||
url = 'https://build.shibboleth.net/nexus/content/repositories/releases/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package io.spring.gradle.convention;
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaLibraryPlugin;
|
||||
import org.gradle.api.plugins.MavenPlugin;
|
||||
import org.gradle.api.plugins.PluginManager
|
||||
import org.springframework.gradle.classpath.CheckClasspathForProhibitedDependenciesPlugin;
|
||||
import org.springframework.gradle.maven.SpringMavenPlugin;
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ public class TestsConfigurationPlugin implements Plugin<Project> {
|
||||
}
|
||||
|
||||
project.tasks.create('testJar', Jar) {
|
||||
archiveClassifier = 'test'
|
||||
classifier = 'test'
|
||||
from project.sourceSets.test.output
|
||||
}
|
||||
|
||||
|
||||
-4
@@ -17,14 +17,10 @@
|
||||
package org.springframework.gradle.github.milestones;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ public abstract class GitHubMilestoneHasNoOpenIssuesTask extends DefaultTask {
|
||||
long milestoneNumber = this.milestones.findMilestoneNumberByTitle(this.repository, this.milestoneTitle);
|
||||
boolean isOpenIssues = this.milestones.isOpenIssuesForMilestoneNumber(this.repository, milestoneNumber);
|
||||
Path isOpenIssuesPath = getIsOpenIssuesFile().getAsFile().get().toPath();
|
||||
Files.writeString(isOpenIssuesPath, String.valueOf(isOpenIssues));
|
||||
Files.write(isOpenIssuesPath, String.valueOf(isOpenIssues).getBytes());
|
||||
if (isOpenIssues) {
|
||||
System.out.println("The repository " + this.repository + " has open issues for milestone with the title " + this.milestoneTitle + " and number " + milestoneNumber);
|
||||
}
|
||||
|
||||
-3
@@ -16,13 +16,10 @@
|
||||
|
||||
package org.springframework.gradle.github.milestones;
|
||||
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
|
||||
import org.springframework.gradle.github.RepositoryRef;
|
||||
|
||||
public class GitHubMilestonePlugin implements Plugin<Project> {
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.gradle.github.milestones;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Steve Riesenberg
|
||||
|
||||
@@ -19,13 +19,13 @@ package org.springframework.gradle.sagan;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.core.runtime.Assert;
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import org.springframework.gradle.github.user.GitHubUserApi;
|
||||
import org.springframework.gradle.github.user.User;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
public class SaganCreateReleaseTask extends DefaultTask {
|
||||
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2019-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.convention.versions;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Scanner;
|
||||
|
||||
class CommandLineUtils {
|
||||
static void runCommand(File dir, String... args) {
|
||||
try {
|
||||
Process process = new ProcessBuilder()
|
||||
.directory(dir)
|
||||
.command(args)
|
||||
.start();
|
||||
writeLinesTo(process.getInputStream(), System.out);
|
||||
writeLinesTo(process.getErrorStream(), System.out);
|
||||
if (process.waitFor() != 0) {
|
||||
new RuntimeException("Failed to run " + Arrays.toString(args));
|
||||
}
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new RuntimeException("Failed to run " + Arrays.toString(args), e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeLinesTo(InputStream input, PrintStream out) {
|
||||
Scanner scanner = new Scanner(input);
|
||||
while(scanner.hasNextLine()) {
|
||||
out.println(scanner.nextLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
package org.springframework.security.convention.versions;
|
||||
|
||||
import com.apollographql.apollo.ApolloCall;
|
||||
import com.apollographql.apollo.ApolloClient;
|
||||
import com.apollographql.apollo.api.Input;
|
||||
import com.apollographql.apollo.api.Response;
|
||||
import com.apollographql.apollo.exception.ApolloException;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.util.retry.Retry;
|
||||
import reactor.util.retry.RetrySpec;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class GitHubApi {
|
||||
|
||||
private final ApolloClient apolloClient;
|
||||
|
||||
public GitHubApi(String githubToken) {
|
||||
if (githubToken == null) {
|
||||
throw new IllegalArgumentException("githubToken is required. You can set it using -PgitHubAccessToken=");
|
||||
}
|
||||
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
|
||||
clientBuilder.addInterceptor(new AuthorizationInterceptor(githubToken));
|
||||
this.apolloClient = ApolloClient.builder()
|
||||
.serverUrl("https://api.github.com/graphql")
|
||||
.okHttpClient(clientBuilder.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
public Mono<FindCreateIssueResult> findCreateIssueInput(String owner, String name, String milestone) {
|
||||
String label = "\"type: dependency-upgrade\"";
|
||||
FindCreateIssueInputQuery findCreateIssueInputQuery = new FindCreateIssueInputQuery(owner, name, Input.optional(label), Input.optional(milestone));
|
||||
return Mono.create( sink -> this.apolloClient.query(findCreateIssueInputQuery)
|
||||
.enqueue(new ApolloCall.Callback<FindCreateIssueInputQuery.Data>() {
|
||||
@Override
|
||||
public void onResponse(@NotNull Response<FindCreateIssueInputQuery.Data> response) {
|
||||
if (response.hasErrors()) {
|
||||
sink.error(new RuntimeException(response.getErrors().stream().map(e -> e.getMessage()).collect(Collectors.joining(" "))));
|
||||
} else {
|
||||
FindCreateIssueInputQuery.Data data = response.getData();
|
||||
FindCreateIssueInputQuery.Repository repository = data.repository();
|
||||
List<String> labels = repository.labels().nodes().stream().map(FindCreateIssueInputQuery.Node::id).collect(Collectors.toList());
|
||||
if (labels.isEmpty()) {
|
||||
sink.error(new IllegalArgumentException("Could not find label for " + label));
|
||||
return;
|
||||
}
|
||||
Optional<String> firstMilestoneId = repository.milestones().nodes().stream().map(FindCreateIssueInputQuery.Node1::id).findFirst();
|
||||
if (!firstMilestoneId.isPresent()) {
|
||||
sink.error(new IllegalArgumentException("Could not find OPEN milestone id for " + milestone));
|
||||
return;
|
||||
}
|
||||
String milestoneId = firstMilestoneId.get();
|
||||
String repositoryId = repository.id();
|
||||
String assigneeId = data.viewer().id();
|
||||
sink.success(new FindCreateIssueResult(repositoryId, labels, milestoneId, assigneeId));
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onFailure(@NotNull ApolloException e) {
|
||||
sink.error(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public static class FindCreateIssueResult {
|
||||
private final String repositoryId;
|
||||
private final List<String> labelIds;
|
||||
private final String milestoneId;
|
||||
private final String assigneeId;
|
||||
|
||||
public FindCreateIssueResult(String repositoryId, List<String> labelIds, String milestoneId, String assigneeId) {
|
||||
this.repositoryId = repositoryId;
|
||||
this.labelIds = labelIds;
|
||||
this.milestoneId = milestoneId;
|
||||
this.assigneeId = assigneeId;
|
||||
}
|
||||
|
||||
public String getRepositoryId() {
|
||||
return repositoryId;
|
||||
}
|
||||
|
||||
public List<String> getLabelIds() {
|
||||
return labelIds;
|
||||
}
|
||||
|
||||
public String getMilestoneId() {
|
||||
return milestoneId;
|
||||
}
|
||||
|
||||
public String getAssigneeId() {
|
||||
return assigneeId;
|
||||
}
|
||||
}
|
||||
|
||||
public Mono<RateLimitQuery.RateLimit> findRateLimit() {
|
||||
return Mono.create( sink -> this.apolloClient.query(new RateLimitQuery())
|
||||
.enqueue(new ApolloCall.Callback<RateLimitQuery.Data>() {
|
||||
@Override
|
||||
public void onResponse(@NotNull Response<RateLimitQuery.Data> response) {
|
||||
if (response.hasErrors()) {
|
||||
sink.error(new RuntimeException(response.getErrors().stream().map(e -> e.getMessage()).collect(Collectors.joining(" "))));
|
||||
} else {
|
||||
sink.success(response.getData().rateLimit());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onFailure(@NotNull ApolloException e) {
|
||||
sink.error(e);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
public Mono<Integer> createIssue(String repositoryId, String title, List<String> labelIds, String milestoneId, String assigneeId) {
|
||||
CreateIssueInputMutation createIssue = new CreateIssueInputMutation.Builder()
|
||||
.repositoryId(repositoryId)
|
||||
.title(title)
|
||||
.labelIds(labelIds)
|
||||
.milestoneId(milestoneId)
|
||||
.assigneeId(assigneeId)
|
||||
.build();
|
||||
return Mono.create( sink -> this.apolloClient.mutate(createIssue)
|
||||
.enqueue(new ApolloCall.Callback<CreateIssueInputMutation.Data>() {
|
||||
@Override
|
||||
public void onResponse(@NotNull Response<CreateIssueInputMutation.Data> response) {
|
||||
if (response.hasErrors()) {
|
||||
String message = response.getErrors().stream().map(e -> e.getMessage() + " " + e.getCustomAttributes() + " " + e.getLocations()).collect(Collectors.joining(" "));
|
||||
if (message.contains("was submitted too quickly")) {
|
||||
sink.error(new SubmittedTooQuick(message));
|
||||
} else {
|
||||
sink.error(new RuntimeException(message));
|
||||
}
|
||||
} else {
|
||||
sink.success(response.getData().createIssue().issue().number());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onFailure(@NotNull ApolloException e) {
|
||||
sink.error(e);
|
||||
}
|
||||
}))
|
||||
.retryWhen(
|
||||
RetrySpec.fixedDelay(3, Duration.ofMinutes(1))
|
||||
.filter(SubmittedTooQuick.class::isInstance)
|
||||
.doBeforeRetry(r -> System.out.println("Pausing for 1 minute and then retrying due to receiving \"submitted too quickly\" error from GitHub API"))
|
||||
)
|
||||
.cast(Integer.class);
|
||||
}
|
||||
|
||||
public static class SubmittedTooQuick extends RuntimeException {
|
||||
public SubmittedTooQuick(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
private static class AuthorizationInterceptor implements Interceptor {
|
||||
|
||||
private final String token;
|
||||
|
||||
public AuthorizationInterceptor(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public okhttp3.Response intercept(Chain chain) throws IOException {
|
||||
Request request = chain.request().newBuilder()
|
||||
.addHeader("Authorization", "Bearer " + this.token).build();
|
||||
return chain.proceed(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2019-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.
|
||||
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
package org.springframework.security.convention.versions;
|
||||
|
||||
import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ComponentSelectionRulesWithCurrent;
|
||||
import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ComponentSelectionWithCurrent;
|
||||
import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ResolutionStrategyWithCurrent;
|
||||
import org.gradle.api.Action;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class UpdateDependenciesExtension {
|
||||
private Supplier<List<File>> files;
|
||||
|
||||
private UpdateMode updateMode = UpdateMode.COMMIT;
|
||||
|
||||
private DependencyExcludes dependencyExcludes = new DependencyExcludes();
|
||||
|
||||
private GitHub gitHub = new GitHub();
|
||||
|
||||
public UpdateDependenciesExtension(Supplier<List<File>> files) {
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
public void setUpdateMode(UpdateMode updateMode) {
|
||||
this.updateMode = updateMode;
|
||||
}
|
||||
|
||||
public UpdateMode getUpdateMode() {
|
||||
return updateMode;
|
||||
}
|
||||
|
||||
GitHub getGitHub() {
|
||||
return this.gitHub;
|
||||
}
|
||||
|
||||
DependencyExcludes getExcludes() {
|
||||
return dependencyExcludes;
|
||||
}
|
||||
|
||||
Supplier<List<File>> getFiles() {
|
||||
return files;
|
||||
}
|
||||
|
||||
public void setFiles(Supplier<List<File>> files) {
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
public void addFiles(Supplier<List<File>> files) {
|
||||
Supplier<List<File>> original = this.files;
|
||||
setFiles(() -> {
|
||||
List<File> result = new ArrayList<>(original.get());
|
||||
result.addAll(files.get());
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
public void dependencyExcludes(Action<DependencyExcludes> excludes) {
|
||||
excludes.execute(this.dependencyExcludes);
|
||||
}
|
||||
|
||||
public void gitHub(Action<GitHub> gitHub) {
|
||||
gitHub.execute(this.gitHub);
|
||||
}
|
||||
|
||||
public enum UpdateMode {
|
||||
COMMIT,
|
||||
GITHUB_ISSUE
|
||||
}
|
||||
|
||||
public class GitHub {
|
||||
private String organization;
|
||||
|
||||
private String repository;
|
||||
|
||||
private String accessToken;
|
||||
|
||||
private String milestone;
|
||||
|
||||
public String getOrganization() {
|
||||
return organization;
|
||||
}
|
||||
|
||||
public void setOrganization(String organization) {
|
||||
this.organization = organization;
|
||||
}
|
||||
|
||||
public String getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public void setRepository(String repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
public String getMilestone() {
|
||||
return milestone;
|
||||
}
|
||||
|
||||
public void setMilestone(String milestone) {
|
||||
this.milestone = milestone;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Consider creating some Predicates instead since they are composible
|
||||
*/
|
||||
public class DependencyExcludes {
|
||||
private List<Action<ComponentSelectionWithCurrent>> actions = new ArrayList<>();
|
||||
private List<Action<ComponentSelectionRulesWithCurrent>> components = new ArrayList<>();
|
||||
|
||||
List<Action<ComponentSelectionWithCurrent>> getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
public List<Action<ComponentSelectionRulesWithCurrent>> getComponents() {
|
||||
return components;
|
||||
}
|
||||
|
||||
public DependencyExcludes alphaBetaVersions() {
|
||||
this.actions.add(excludeVersionWithRegex("(?i).*?(alpha|beta).*", "an alpha or beta version"));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DependencyExcludes majorVersionBump() {
|
||||
this.actions.add((selection) -> {
|
||||
String currentVersion = selection.getCurrentVersion();
|
||||
int separator = currentVersion.indexOf(".");
|
||||
String major = separator > 0 ? currentVersion.substring(0, separator) : currentVersion;
|
||||
String candidateVersion = selection.getCandidate().getVersion();
|
||||
Pattern calVerPattern = Pattern.compile("\\d\\d\\d\\d.*");
|
||||
boolean isCalVer = calVerPattern.matcher(candidateVersion).matches();
|
||||
if (!isCalVer && !candidateVersion.startsWith(major)) {
|
||||
selection.reject("Cannot upgrade to new Major Version");
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public DependencyExcludes minorVersionBump() {
|
||||
this.actions.add(createExcludeMinorVersionBump());
|
||||
return this;
|
||||
}
|
||||
|
||||
public Action<ComponentSelectionWithCurrent> createExcludeMinorVersionBump() {
|
||||
return (selection) -> {
|
||||
String currentVersion = selection.getCurrentVersion();
|
||||
int majorSeparator = currentVersion.indexOf(".");
|
||||
int separator = currentVersion.indexOf(".", majorSeparator + 1);
|
||||
String majorMinor = separator > 0 ? currentVersion.substring(0, separator) : currentVersion;
|
||||
String candidateVersion = selection.getCandidate().getVersion();
|
||||
if (!candidateVersion.startsWith(majorMinor)) {
|
||||
selection.reject("Cannot upgrade to new Minor Version");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public DependencyExcludes releaseCandidatesVersions() {
|
||||
this.actions.add(excludeVersionWithRegex("(?i).*?rc.*", "a release candidate version"));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DependencyExcludes milestoneVersions() {
|
||||
this.actions.add(excludeVersionWithRegex("(?i).*?m\\d+.*", "a milestone version"));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DependencyExcludes snapshotVersions() {
|
||||
this.actions.add(excludeVersionWithRegex(".*?-SNAPSHOT.*", "a SNAPSHOT version"));
|
||||
return this;
|
||||
}
|
||||
|
||||
public DependencyExcludes addRule(Action<ComponentSelectionRulesWithCurrent> rule) {
|
||||
this.components.add(rule);
|
||||
return this;
|
||||
}
|
||||
|
||||
private Action<ComponentSelectionWithCurrent> excludeVersionWithRegex(String regex, String reason) {
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
return (selection) -> {
|
||||
String candidateVersion = selection.getCandidate().getVersion();
|
||||
if (pattern.matcher(candidateVersion).matches()) {
|
||||
selection.reject(candidateVersion + " is not allowed because it is " + reason);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* Copyright 2019-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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.convention.versions;
|
||||
|
||||
import com.github.benmanes.gradle.versions.reporter.result.Dependency;
|
||||
import com.github.benmanes.gradle.versions.reporter.result.DependencyOutdated;
|
||||
import com.github.benmanes.gradle.versions.reporter.result.Result;
|
||||
import com.github.benmanes.gradle.versions.reporter.result.VersionAvailable;
|
||||
import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask;
|
||||
import com.github.benmanes.gradle.versions.updates.gradle.GradleUpdateResult;
|
||||
import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ComponentSelectionRulesWithCurrent;
|
||||
import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ComponentSelectionWithCurrent;
|
||||
import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ResolutionStrategyWithCurrent;
|
||||
import groovy.lang.Closure;
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.springframework.security.convention.versions.TransitiveDependencyLookupUtils.NIMBUS_JOSE_JWT_NAME;
|
||||
import static org.springframework.security.convention.versions.TransitiveDependencyLookupUtils.OIDC_SDK_NAME;
|
||||
|
||||
public class UpdateDependenciesPlugin implements Plugin<Project> {
|
||||
private GitHubApi gitHubApi;
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
UpdateDependenciesExtension updateDependenciesSettings = project.getExtensions().create("updateDependenciesSettings", UpdateDependenciesExtension.class, defaultFiles(project));
|
||||
if (project.hasProperty("updateMode")) {
|
||||
String updateMode = String.valueOf(project.findProperty("updateMode"));
|
||||
updateDependenciesSettings.setUpdateMode(UpdateDependenciesExtension.UpdateMode.valueOf(updateMode));
|
||||
}
|
||||
if (project.hasProperty("nextVersion")) {
|
||||
String nextVersion = String.valueOf(project.findProperty("nextVersion"));
|
||||
updateDependenciesSettings.getGitHub().setMilestone(nextVersion);
|
||||
}
|
||||
if (project.hasProperty("gitHubAccessToken")) {
|
||||
String gitHubAccessToken = String.valueOf(project.findProperty("gitHubAccessToken"));
|
||||
updateDependenciesSettings.getGitHub().setAccessToken(gitHubAccessToken);
|
||||
}
|
||||
project.getTasks().register("updateDependencies", DependencyUpdatesTask.class, new Action<DependencyUpdatesTask>() {
|
||||
@Override
|
||||
public void execute(DependencyUpdatesTask updateDependencies) {
|
||||
updateDependencies.setDescription("Update the dependencies");
|
||||
updateDependencies.setCheckConstraints(true);
|
||||
updateDependencies.setOutputFormatter(new Closure<Void>(null) {
|
||||
@Override
|
||||
public Void call(Object argument) {
|
||||
Result result = (Result) argument;
|
||||
if (gitHubApi == null && updateDependenciesSettings.getUpdateMode() != UpdateDependenciesExtension.UpdateMode.COMMIT) {
|
||||
gitHubApi = new GitHubApi(updateDependenciesSettings.getGitHub().getAccessToken());
|
||||
}
|
||||
updateDependencies(result, project, updateDependenciesSettings);
|
||||
updateGradleVersion(result, project, updateDependenciesSettings);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
updateDependencies.resolutionStrategy(new Action<ResolutionStrategyWithCurrent>() {
|
||||
@Override
|
||||
public void execute(ResolutionStrategyWithCurrent resolution) {
|
||||
resolution.componentSelection(new Action<ComponentSelectionRulesWithCurrent>() {
|
||||
@Override
|
||||
public void execute(ComponentSelectionRulesWithCurrent components) {
|
||||
updateDependenciesSettings.getExcludes().getActions().forEach((action) -> {
|
||||
components.all(action);
|
||||
});
|
||||
updateDependenciesSettings.getExcludes().getComponents().forEach((action) -> {
|
||||
action.execute(components);
|
||||
});
|
||||
components.all((selection) -> {
|
||||
ModuleComponentIdentifier candidate = selection.getCandidate();
|
||||
if (candidate.getGroup().startsWith("org.apache.directory.") && !candidate.getVersion().equals(selection.getCurrentVersion())) {
|
||||
selection.reject("org.apache.directory.* has breaking changes in newer versions");
|
||||
}
|
||||
});
|
||||
String jaxbBetaRegex = ".*?b\\d+.*";
|
||||
components.withModule("javax.xml.bind:jaxb-api", excludeWithRegex(jaxbBetaRegex, "Reject jaxb-api beta versions"));
|
||||
components.withModule("com.sun.xml.bind:jaxb-impl", excludeWithRegex(jaxbBetaRegex, "Reject jaxb-api beta versions"));
|
||||
components.withModule("commons-collections:commons-collections", excludeWithRegex("^\\d{3,}.*", "Reject commons-collections date based releases"));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateDependencies(Result result, Project project, UpdateDependenciesExtension updateDependenciesSettings) {
|
||||
SortedSet<DependencyOutdated> dependencies = result.getOutdated().getDependencies();
|
||||
if (dependencies.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, List<DependencyOutdated>> groups = new LinkedHashMap<>();
|
||||
dependencies.forEach(outdated -> {
|
||||
groups.computeIfAbsent(outdated.getGroup(), (key) -> new ArrayList<>()).add(outdated);
|
||||
});
|
||||
List<DependencyOutdated> nimbusds = groups.getOrDefault("com.nimbusds", new ArrayList<>());
|
||||
DependencyOutdated oidcSdc = nimbusds.stream().filter(d -> d.getName().equals(OIDC_SDK_NAME)).findFirst().orElseGet(() -> null);
|
||||
if(oidcSdc != null) {
|
||||
String oidcVersion = updatedVersion(oidcSdc);
|
||||
String jwtVersion = TransitiveDependencyLookupUtils.lookupJwtVersion(oidcVersion);
|
||||
|
||||
Dependency nimbusJoseJwtDependency = result.getCurrent().getDependencies().stream().filter(d -> d.getName().equals(NIMBUS_JOSE_JWT_NAME)).findFirst().get();
|
||||
DependencyOutdated outdatedJwt = new DependencyOutdated();
|
||||
outdatedJwt.setVersion(nimbusJoseJwtDependency.getVersion());
|
||||
outdatedJwt.setGroup(oidcSdc.getGroup());
|
||||
outdatedJwt.setName(NIMBUS_JOSE_JWT_NAME);
|
||||
VersionAvailable available = new VersionAvailable();
|
||||
available.setRelease(jwtVersion);
|
||||
outdatedJwt.setAvailable(available);
|
||||
nimbusds.add(outdatedJwt);
|
||||
}
|
||||
File gradlePropertiesFile = project.getRootProject().file(Project.GRADLE_PROPERTIES);
|
||||
Mono<GitHubApi.FindCreateIssueResult> createIssueResult = createIssueResultMono(updateDependenciesSettings);
|
||||
List<File> filesWithDependencies = updateDependenciesSettings.getFiles().get();
|
||||
groups.forEach((group, outdated) -> {
|
||||
outdated.forEach((dependency) -> {
|
||||
String ga = dependency.getGroup() + ":" + dependency.getName() + ":";
|
||||
String originalDependency = ga + dependency.getVersion();
|
||||
String replacementDependency = ga + updatedVersion(dependency);
|
||||
System.out.println("Update " + originalDependency + " to " + replacementDependency);
|
||||
filesWithDependencies.forEach((fileWithDependency) -> {
|
||||
updateDependencyInlineVersion(fileWithDependency, dependency);
|
||||
updateDependencyWithVersionVariable(fileWithDependency, gradlePropertiesFile, dependency);
|
||||
});
|
||||
});
|
||||
|
||||
// commit
|
||||
DependencyOutdated firstDependency = outdated.get(0);
|
||||
String updatedVersion = updatedVersion(firstDependency);
|
||||
String title = outdated.size() == 1 ? "Update " + firstDependency.getName() + " to " + updatedVersion : "Update " + firstDependency.getGroup() + " to " + updatedVersion;
|
||||
afterGroup(updateDependenciesSettings, project.getRootDir(), title, createIssueResult);
|
||||
});
|
||||
}
|
||||
|
||||
private void afterGroup(UpdateDependenciesExtension updateDependenciesExtension, File rootDir, String title, Mono<GitHubApi.FindCreateIssueResult> createIssueResultMono) {
|
||||
|
||||
String commitMessage = title;
|
||||
if (updateDependenciesExtension.getUpdateMode() == UpdateDependenciesExtension.UpdateMode.GITHUB_ISSUE) {
|
||||
GitHubApi.FindCreateIssueResult createIssueResult = createIssueResultMono.block();
|
||||
Integer issueNumber = gitHubApi.createIssue(createIssueResult.getRepositoryId(), title, createIssueResult.getLabelIds(), createIssueResult.getMilestoneId(), createIssueResult.getAssigneeId()).delayElement(Duration.ofSeconds(1)).block();
|
||||
commitMessage += "\n\nCloses gh-" + issueNumber;
|
||||
}
|
||||
CommandLineUtils.runCommand(rootDir, "git", "commit", "-am", commitMessage);
|
||||
}
|
||||
|
||||
private Mono<GitHubApi.FindCreateIssueResult> createIssueResultMono(UpdateDependenciesExtension updateDependenciesExtension) {
|
||||
return Mono.defer(() -> {
|
||||
UpdateDependenciesExtension.GitHub gitHub = updateDependenciesExtension.getGitHub();
|
||||
return gitHubApi.findCreateIssueInput(gitHub.getOrganization(), gitHub.getRepository(), gitHub.getMilestone()).cache();
|
||||
});
|
||||
}
|
||||
|
||||
private void updateGradleVersion(Result result, Project project, UpdateDependenciesExtension updateDependenciesSettings) {
|
||||
if (!result.getGradle().isEnabled()) {
|
||||
return;
|
||||
}
|
||||
GradleUpdateResult current = result.getGradle().getCurrent();
|
||||
GradleUpdateResult running = result.getGradle().getRunning();
|
||||
if (current.compareTo(running) > 0) {
|
||||
String title = "Update Gradle to " + current.getVersion();
|
||||
System.out.println(title);
|
||||
CommandLineUtils.runCommand(project.getRootDir(), "./gradlew", "wrapper", "--gradle-version", current.getVersion(), "--no-daemon");
|
||||
afterGroup(updateDependenciesSettings, project.getRootDir(), title, createIssueResultMono(updateDependenciesSettings));
|
||||
}
|
||||
}
|
||||
|
||||
private static Supplier<List<File>> defaultFiles(Project project) {
|
||||
return () -> {
|
||||
List<File> result = new ArrayList<>();
|
||||
result.add(project.getBuildFile());
|
||||
project.getChildProjects().values().forEach((childProject) ->
|
||||
result.add(childProject.getBuildFile())
|
||||
);
|
||||
result.add(project.getRootProject().file("buildSrc/build.gradle"));
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
static Action<ComponentSelectionWithCurrent> excludeWithRegex(String regex, String reason) {
|
||||
Pattern pattern = Pattern.compile(regex);
|
||||
return (selection) -> {
|
||||
String candidateVersion = selection.getCandidate().getVersion();
|
||||
if (pattern.matcher(candidateVersion).matches()) {
|
||||
selection.reject(candidateVersion + " is not allowed because it is " + reason);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static void updateDependencyInlineVersion(File buildFile, DependencyOutdated dependency){
|
||||
String ga = dependency.getGroup() + ":" + dependency.getName() + ":";
|
||||
String originalDependency = ga + dependency.getVersion();
|
||||
String replacementDependency = ga + updatedVersion(dependency);
|
||||
FileUtils.replaceFileText(buildFile, buildFileText -> buildFileText.replace(originalDependency, replacementDependency));
|
||||
}
|
||||
|
||||
static void updateDependencyWithVersionVariable(File scanFile, File gradlePropertiesFile, DependencyOutdated dependency) {
|
||||
if (!gradlePropertiesFile.exists()) {
|
||||
return;
|
||||
}
|
||||
FileUtils.replaceFileText(gradlePropertiesFile, (gradlePropertiesText) -> {
|
||||
String ga = dependency.getGroup() + ":" + dependency.getName() + ":";
|
||||
Pattern pattern = Pattern.compile("\"" + ga + "\\$\\{?([^'\"]+?)\\}?\"");
|
||||
String buildFileText = FileUtils.readString(scanFile);
|
||||
Matcher matcher = pattern.matcher(buildFileText);
|
||||
while (matcher.find()) {
|
||||
String versionVariable = matcher.group(1);
|
||||
gradlePropertiesText = gradlePropertiesText.replace(versionVariable + "=" + dependency.getVersion(), versionVariable + "=" + updatedVersion(dependency));
|
||||
}
|
||||
return gradlePropertiesText;
|
||||
});
|
||||
}
|
||||
|
||||
private static String updatedVersion(DependencyOutdated dependency) {
|
||||
VersionAvailable available = dependency.getAvailable();
|
||||
String release = available.getRelease();
|
||||
if (release != null) {
|
||||
return release;
|
||||
}
|
||||
return available.getMilestone();
|
||||
}
|
||||
}
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.convention.versions;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency;
|
||||
import org.gradle.api.artifacts.VersionCatalog;
|
||||
import org.gradle.api.artifacts.VersionCatalogsExtension;
|
||||
import org.gradle.api.plugins.JavaBasePlugin;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
|
||||
public class VerifyDependenciesVersionsPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project project) {
|
||||
TaskProvider<VerifyDependenciesVersionsTask> verifyDependenciesVersionsTaskProvider = project.getTasks().register("verifyDependenciesVersions", VerifyDependenciesVersionsTask.class, (task) -> {
|
||||
task.setGroup("Verification");
|
||||
task.setDescription("Verify that specific dependencies are using the same version");
|
||||
VersionCatalog versionCatalog = project.getExtensions().getByType(VersionCatalogsExtension.class).named("libs");
|
||||
MinimalExternalModuleDependency oauth2OidcSdk = versionCatalog.findLibrary("com-nimbusds-oauth2-oidc-sdk").get().get();
|
||||
MinimalExternalModuleDependency nimbusJoseJwt = versionCatalog.findLibrary("com-nimbusds-nimbus-jose-jwt").get().get();
|
||||
task.setOauth2OidcSdkVersion(oauth2OidcSdk.getVersion());
|
||||
task.setExpectedNimbusJoseJwtVersion(nimbusJoseJwt.getVersion());
|
||||
});
|
||||
project.getTasks().named(JavaBasePlugin.CHECK_TASK_NAME, checkTask -> checkTask.dependsOn(verifyDependenciesVersionsTaskProvider));
|
||||
}
|
||||
|
||||
public static class VerifyDependenciesVersionsTask extends DefaultTask {
|
||||
|
||||
private String oauth2OidcSdkVersion;
|
||||
|
||||
private String expectedNimbusJoseJwtVersion;
|
||||
|
||||
public void setOauth2OidcSdkVersion(String oauth2OidcSdkVersion) {
|
||||
this.oauth2OidcSdkVersion = oauth2OidcSdkVersion;
|
||||
}
|
||||
|
||||
public void setExpectedNimbusJoseJwtVersion(String expectedNimbusJoseJwtVersion) {
|
||||
this.expectedNimbusJoseJwtVersion = expectedNimbusJoseJwtVersion;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
public void verify() {
|
||||
String transitiveNimbusJoseJwtVersion = TransitiveDependencyLookupUtils.lookupJwtVersion(this.oauth2OidcSdkVersion);
|
||||
if (!transitiveNimbusJoseJwtVersion.equals(this.expectedNimbusJoseJwtVersion)) {
|
||||
String message = String.format("Found transitive nimbus-jose-jwt:%s in oauth2-oidc-sdk:%s, but the project contains a different version of nimbus-jose-jwt [%s]. Please align the versions.", transitiveNimbusJoseJwtVersion, this.oauth2OidcSdkVersion, this.expectedNimbusJoseJwtVersion);
|
||||
throw new IllegalStateException(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-1
@@ -1 +0,0 @@
|
||||
implementation-class=io.spring.gradle.convention.EclipsePlugin
|
||||
@@ -5,7 +5,6 @@ import org.apache.commons.io.FileUtils;
|
||||
import org.gradle.testkit.runner.BuildResult;
|
||||
import org.gradle.testkit.runner.TaskOutcome;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
@@ -29,7 +28,7 @@ public class JavadocApiPluginITest {
|
||||
.build();
|
||||
assertThat(result.task(":api").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
|
||||
File allClasses = new File(testKit.getRootDir(), "build/api/allclasses-noframe.html");
|
||||
File index = new File(testKit.getRootDir(), "build/api/allclasses-index.html");
|
||||
File index = new File(testKit.getRootDir(), "build/api/allclasses.html");
|
||||
File listing = allClasses.exists() ? allClasses : index;
|
||||
String listingText = FileUtils.readFileToString(listing);
|
||||
assertThat(listingText).contains("sample/Api.html");
|
||||
|
||||
+7
-7
@@ -125,27 +125,27 @@ public class RepositoryConventionPluginTests {
|
||||
|
||||
private void assertSnapshotRepository(RepositoryHandler repositories) {
|
||||
assertThat(repositories).extracting(ArtifactRepository::getName).hasSize(5);
|
||||
assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())
|
||||
assertThat(((MavenArtifactRepository) repositories.get(0)).getUrl().toString())
|
||||
.isEqualTo("https://repo.maven.apache.org/maven2/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString())
|
||||
assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())
|
||||
.isEqualTo("https://repo.spring.io/snapshot/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(3)).getUrl().toString())
|
||||
assertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString())
|
||||
.isEqualTo("https://repo.spring.io/milestone/");
|
||||
}
|
||||
|
||||
private void assertMilestoneRepository(RepositoryHandler repositories) {
|
||||
assertThat(repositories).extracting(ArtifactRepository::getName).hasSize(4);
|
||||
assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())
|
||||
assertThat(((MavenArtifactRepository) repositories.get(0)).getUrl().toString())
|
||||
.isEqualTo("https://repo.maven.apache.org/maven2/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString())
|
||||
assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())
|
||||
.isEqualTo("https://repo.spring.io/milestone/");
|
||||
}
|
||||
|
||||
private void assertReleaseRepository(RepositoryHandler repositories) {
|
||||
assertThat(repositories).extracting(ArtifactRepository::getName).hasSize(3);
|
||||
assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())
|
||||
assertThat(((MavenArtifactRepository) repositories.get(0)).getUrl().toString())
|
||||
.isEqualTo("https://repo.maven.apache.org/maven2/");
|
||||
assertThat(((MavenArtifactRepository) repositories.get(2)).getUrl().toString())
|
||||
assertThat(((MavenArtifactRepository) repositories.get(1)).getUrl().toString())
|
||||
.isEqualTo("https://repo.spring.io/release/");
|
||||
}
|
||||
|
||||
|
||||
-1
@@ -3,7 +3,6 @@ package org.springframework.gradle.github.milestones;
|
||||
import java.nio.charset.Charset;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2019-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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.convention.versions;
|
||||
|
||||
import com.github.benmanes.gradle.versions.updates.resolutionstrategy.ComponentSelectionWithCurrent;
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.artifacts.ComponentSelection;
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class DependencyExcludesTests {
|
||||
|
||||
@Test
|
||||
public void createExcludeMinorVersionBumpWhenMajorVersionBumpThenReject() {
|
||||
ComponentSelection componentSelection = executeCreateExcludeMinorVersionBump("1.0.0", "2.0.0");
|
||||
verify(componentSelection).reject(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createExcludeMinorVersionBumpWhenMajorCalVersionBumpThenReject() {
|
||||
ComponentSelection componentSelection = executeCreateExcludeMinorVersionBump("2000.0.0", "2001.0.0");
|
||||
verify(componentSelection).reject(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createExcludeMinorVersionBumpWhenMinorVersionBumpThenReject() {
|
||||
ComponentSelection componentSelection = executeCreateExcludeMinorVersionBump("1.0.0", "1.1.0");
|
||||
verify(componentSelection).reject(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createExcludeMinorVersionBumpWhenMinorCalVersionBumpThenReject() {
|
||||
ComponentSelection componentSelection = executeCreateExcludeMinorVersionBump("2000.0.0", "2000.1.0");
|
||||
verify(componentSelection).reject(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createExcludeMinorVersionBumpWhenMinorAndPatchVersionBumpThenReject() {
|
||||
ComponentSelection componentSelection = executeCreateExcludeMinorVersionBump("1.0.0", "1.1.1");
|
||||
verify(componentSelection).reject(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createExcludeMinorVersionBumpWhenPatchVersionBumpThenDoesNotReject() {
|
||||
ComponentSelection componentSelection = executeCreateExcludeMinorVersionBump("1.0.0", "1.0.1");
|
||||
verify(componentSelection, times(0)).reject(any());
|
||||
}
|
||||
|
||||
private ComponentSelection executeCreateExcludeMinorVersionBump(String currentVersion, String candidateVersion) {
|
||||
ComponentSelection componentSelection = mock(ComponentSelection.class);
|
||||
UpdateDependenciesExtension.DependencyExcludes excludes = new UpdateDependenciesExtension(() -> Collections.emptyList()).new DependencyExcludes();
|
||||
Action<ComponentSelectionWithCurrent> excludeMinorVersionBump = excludes.createExcludeMinorVersionBump();
|
||||
ComponentSelectionWithCurrent selection = currentVersionAndCandidateVersion(componentSelection, currentVersion, candidateVersion);
|
||||
excludeMinorVersionBump.execute(selection);
|
||||
return componentSelection;
|
||||
}
|
||||
|
||||
private ComponentSelectionWithCurrent currentVersionAndCandidateVersion(ComponentSelection componentSelection, String currentVersion, String candidateVersion) {
|
||||
ModuleComponentIdentifier candidate = mock(ModuleComponentIdentifier.class);
|
||||
given(componentSelection.getCandidate()).willReturn(candidate);
|
||||
ComponentSelectionWithCurrent selection = new ComponentSelectionWithCurrent(currentVersion, componentSelection);
|
||||
given(candidate.getVersion()).willReturn(candidateVersion);
|
||||
given(componentSelection.getCandidate()).willReturn(candidate);
|
||||
return selection;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2019-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.
|
||||
*/
|
||||
|
||||
package org.springframework.security.convention.versions;
|
||||
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TransitiveDependencyLookupUtilsTest {
|
||||
|
||||
@Test
|
||||
public void lookupJwtVersionWhen93Then961() {
|
||||
String s = TransitiveDependencyLookupUtils.lookupJwtVersion("9.3");
|
||||
assertThat(s).isEqualTo("9.6.1");
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
optional 'jakarta.servlet:jakarta.servlet-api:5.0.0'
|
||||
optional 'jakarta.servlet:jakarta.servlet-api:3.1.0'
|
||||
testCompile 'junit:junit:4.12'
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
package sample;
|
||||
|
||||
import org.junit.Test;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
public class TheTest {
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
apply plugin: 'io.spring.convention.spring-module'
|
||||
|
||||
dependencies {
|
||||
management platform(project(":spring-security-dependencies"))
|
||||
api project(':spring-security-core')
|
||||
api project(':spring-security-web')
|
||||
api('org.jasig.cas.client:cas-client-core') {
|
||||
exclude group: 'org.glassfish.jaxb', module: 'jaxb-core'
|
||||
exclude group: 'javax.xml.bind', module: 'jaxb-api'
|
||||
}
|
||||
api 'org.springframework:spring-beans'
|
||||
api 'org.springframework:spring-context'
|
||||
api 'org.springframework:spring-core'
|
||||
api 'org.springframework:spring-web'
|
||||
|
||||
optional 'com.fasterxml.jackson.core:jackson-databind'
|
||||
optional 'net.sf.ehcache:ehcache'
|
||||
|
||||
provided 'jakarta.servlet:jakarta.servlet-api'
|
||||
|
||||
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-test"
|
||||
testImplementation 'org.skyscreamer:jsonassert'
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.cas;
|
||||
|
||||
/**
|
||||
* Sets the appropriate parameters for CAS's implementation of SAML (which is not
|
||||
* guaranteed to be actually SAML compliant).
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class SamlServiceProperties extends ServiceProperties {
|
||||
|
||||
public static final String DEFAULT_SAML_ARTIFACT_PARAMETER = "SAMLart";
|
||||
|
||||
public static final String DEFAULT_SAML_SERVICE_PARAMETER = "TARGET";
|
||||
|
||||
public SamlServiceProperties() {
|
||||
super.setArtifactParameter(DEFAULT_SAML_ARTIFACT_PARAMETER);
|
||||
super.setServiceParameter(DEFAULT_SAML_SERVICE_PARAMETER);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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.cas;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Stores properties related to this CAS service.
|
||||
* <p>
|
||||
* Each web application capable of processing CAS tickets is known as a service. This
|
||||
* class stores the properties that are relevant to the local CAS service, being the
|
||||
* application that is being secured by Spring Security.
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public class ServiceProperties implements InitializingBean {
|
||||
|
||||
public static final String DEFAULT_CAS_ARTIFACT_PARAMETER = "ticket";
|
||||
|
||||
public static final String DEFAULT_CAS_SERVICE_PARAMETER = "service";
|
||||
|
||||
private String service;
|
||||
|
||||
private boolean authenticateAllArtifacts;
|
||||
|
||||
private boolean sendRenew = false;
|
||||
|
||||
private String artifactParameter = DEFAULT_CAS_ARTIFACT_PARAMETER;
|
||||
|
||||
private String serviceParameter = DEFAULT_CAS_SERVICE_PARAMETER;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.hasLength(this.service, "service cannot be empty.");
|
||||
Assert.hasLength(this.artifactParameter, "artifactParameter cannot be empty.");
|
||||
Assert.hasLength(this.serviceParameter, "serviceParameter cannot be empty.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the service the user is authenticating to.
|
||||
* <p>
|
||||
* This service is the callback URL belonging to the local Spring Security System for
|
||||
* Spring secured application. For example,
|
||||
*
|
||||
* <pre>
|
||||
* https://www.mycompany.com/application/login/cas
|
||||
* </pre>
|
||||
* @return the URL of the service the user is authenticating to
|
||||
*/
|
||||
public final String getService() {
|
||||
return this.service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the <code>renew</code> parameter should be sent to the CAS login
|
||||
* URL and CAS validation URL.
|
||||
* <p>
|
||||
* If <code>true</code>, it will force CAS to authenticate the user again (even if the
|
||||
* user has previously authenticated). During ticket validation it will require the
|
||||
* ticket was generated as a consequence of an explicit login. High security
|
||||
* applications would probably set this to <code>true</code>. Defaults to
|
||||
* <code>false</code>, providing automated single sign on.
|
||||
* @return whether to send the <code>renew</code> parameter to CAS
|
||||
*/
|
||||
public final boolean isSendRenew() {
|
||||
return this.sendRenew;
|
||||
}
|
||||
|
||||
public final void setSendRenew(final boolean sendRenew) {
|
||||
this.sendRenew = sendRenew;
|
||||
}
|
||||
|
||||
public final void setService(final String service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
public final String getArtifactParameter() {
|
||||
return this.artifactParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the Request Parameter to look for when attempting to see if a CAS ticket
|
||||
* was sent from the server.
|
||||
* @param artifactParameter the id to use. Default is "ticket".
|
||||
*/
|
||||
public final void setArtifactParameter(final String artifactParameter) {
|
||||
this.artifactParameter = artifactParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the Request parameter to look for when attempting to send a request to
|
||||
* CAS.
|
||||
* @return the service parameter to use. Default is "service".
|
||||
*/
|
||||
public final String getServiceParameter() {
|
||||
return this.serviceParameter;
|
||||
}
|
||||
|
||||
public final void setServiceParameter(final String serviceParameter) {
|
||||
this.serviceParameter = serviceParameter;
|
||||
}
|
||||
|
||||
public final boolean isAuthenticateAllArtifacts() {
|
||||
return this.authenticateAllArtifacts;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, then any non-null artifact (ticket) should be authenticated. Additionally,
|
||||
* the service will be determined dynamically in order to ensure the service matches
|
||||
* the expected value for this artifact.
|
||||
* @param authenticateAllArtifacts
|
||||
*/
|
||||
public final void setAuthenticateAllArtifacts(final boolean authenticateAllArtifacts) {
|
||||
this.authenticateAllArtifacts = authenticateAllArtifacts;
|
||||
}
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.cas.authentication;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
|
||||
/**
|
||||
* Temporary authentication object needed to load the user details service.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class CasAssertionAuthenticationToken extends AbstractAuthenticationToken {
|
||||
|
||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final Assertion assertion;
|
||||
|
||||
private final String ticket;
|
||||
|
||||
public CasAssertionAuthenticationToken(final Assertion assertion, final String ticket) {
|
||||
super(new ArrayList<>());
|
||||
this.assertion = assertion;
|
||||
this.ticket = ticket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.assertion.getPrincipal().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return this.ticket;
|
||||
}
|
||||
|
||||
public Assertion getAssertion() {
|
||||
return this.assertion;
|
||||
}
|
||||
|
||||
}
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* 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.cas.authentication;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
import org.jasig.cas.client.validation.TicketValidationException;
|
||||
import org.jasig.cas.client.validation.TicketValidator;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.MessageSourceAware;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.authentication.AccountStatusUserDetailsChecker;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.authentication.BadCredentialsException;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
import org.springframework.security.cas.web.CasAuthenticationFilter;
|
||||
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.SpringSecurityMessageSource;
|
||||
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
|
||||
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
|
||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
|
||||
import org.springframework.security.core.userdetails.UserDetailsChecker;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* An {@link AuthenticationProvider} implementation that integrates with JA-SIG Central
|
||||
* Authentication Service (CAS).
|
||||
* <p>
|
||||
* This <code>AuthenticationProvider</code> is capable of validating
|
||||
* {@link UsernamePasswordAuthenticationToken} requests which contain a
|
||||
* <code>principal</code> name equal to either
|
||||
* {@link CasAuthenticationFilter#CAS_STATEFUL_IDENTIFIER} or
|
||||
* {@link CasAuthenticationFilter#CAS_STATELESS_IDENTIFIER}. It can also validate a
|
||||
* previously created {@link CasAuthenticationToken}.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Scott Battaglia
|
||||
*/
|
||||
public class CasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
|
||||
|
||||
private AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService;
|
||||
|
||||
private final UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
|
||||
|
||||
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
|
||||
|
||||
private StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache();
|
||||
|
||||
private String key;
|
||||
|
||||
private TicketValidator ticketValidator;
|
||||
|
||||
private ServiceProperties serviceProperties;
|
||||
|
||||
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(this.authenticationUserDetailsService, "An authenticationUserDetailsService must be set");
|
||||
Assert.notNull(this.ticketValidator, "A ticketValidator must be set");
|
||||
Assert.notNull(this.statelessTicketCache, "A statelessTicketCache must be set");
|
||||
Assert.hasText(this.key,
|
||||
"A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated");
|
||||
Assert.notNull(this.messages, "A message source must be set");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
|
||||
if (!supports(authentication.getClass())) {
|
||||
return null;
|
||||
}
|
||||
if (authentication instanceof UsernamePasswordAuthenticationToken
|
||||
&& (!CasAuthenticationFilter.CAS_STATEFUL_IDENTIFIER.equals(authentication.getPrincipal().toString())
|
||||
&& !CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER
|
||||
.equals(authentication.getPrincipal().toString()))) {
|
||||
// UsernamePasswordAuthenticationToken not CAS related
|
||||
return null;
|
||||
}
|
||||
// If an existing CasAuthenticationToken, just check we created it
|
||||
if (authentication instanceof CasAuthenticationToken) {
|
||||
if (this.key.hashCode() != ((CasAuthenticationToken) authentication).getKeyHash()) {
|
||||
throw new BadCredentialsException(this.messages.getMessage("CasAuthenticationProvider.incorrectKey",
|
||||
"The presented CasAuthenticationToken does not contain the expected key"));
|
||||
}
|
||||
return authentication;
|
||||
}
|
||||
|
||||
// Ensure credentials are presented
|
||||
if ((authentication.getCredentials() == null) || "".equals(authentication.getCredentials())) {
|
||||
throw new BadCredentialsException(this.messages.getMessage("CasAuthenticationProvider.noServiceTicket",
|
||||
"Failed to provide a CAS service ticket to validate"));
|
||||
}
|
||||
|
||||
boolean stateless = (authentication instanceof UsernamePasswordAuthenticationToken
|
||||
&& CasAuthenticationFilter.CAS_STATELESS_IDENTIFIER.equals(authentication.getPrincipal()));
|
||||
CasAuthenticationToken result = null;
|
||||
|
||||
if (stateless) {
|
||||
// Try to obtain from cache
|
||||
result = this.statelessTicketCache.getByTicketId(authentication.getCredentials().toString());
|
||||
}
|
||||
if (result == null) {
|
||||
result = this.authenticateNow(authentication);
|
||||
result.setDetails(authentication.getDetails());
|
||||
}
|
||||
if (stateless) {
|
||||
// Add to cache
|
||||
this.statelessTicketCache.putTicketInCache(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
|
||||
try {
|
||||
Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(),
|
||||
getServiceUrl(authentication));
|
||||
UserDetails userDetails = loadUserByAssertion(assertion);
|
||||
this.userDetailsChecker.check(userDetails);
|
||||
return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(),
|
||||
this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, assertion);
|
||||
}
|
||||
catch (TicketValidationException ex) {
|
||||
throw new BadCredentialsException(ex.getMessage(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the serviceUrl. If the {@link Authentication#getDetails()} is an instance of
|
||||
* {@link ServiceAuthenticationDetails}, then
|
||||
* {@link ServiceAuthenticationDetails#getServiceUrl()} is used. Otherwise, the
|
||||
* {@link ServiceProperties#getService()} is used.
|
||||
* @param authentication
|
||||
* @return
|
||||
*/
|
||||
private String getServiceUrl(Authentication authentication) {
|
||||
String serviceUrl;
|
||||
if (authentication.getDetails() instanceof ServiceAuthenticationDetails) {
|
||||
return ((ServiceAuthenticationDetails) authentication.getDetails()).getServiceUrl();
|
||||
}
|
||||
Assert.state(this.serviceProperties != null,
|
||||
"serviceProperties cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.");
|
||||
Assert.state(this.serviceProperties.getService() != null,
|
||||
"serviceProperties.getService() cannot be null unless Authentication.getDetails() implements ServiceAuthenticationDetails.");
|
||||
serviceUrl = this.serviceProperties.getService();
|
||||
logger.debug(LogMessage.format("serviceUrl = %s", serviceUrl));
|
||||
return serviceUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method for retrieving the UserDetails based on the assertion. Default is
|
||||
* to call configured userDetailsService and pass the username. Deployers can override
|
||||
* this method and retrieve the user based on any criteria they desire.
|
||||
* @param assertion The CAS Assertion.
|
||||
* @return the UserDetails.
|
||||
*/
|
||||
protected UserDetails loadUserByAssertion(final Assertion assertion) {
|
||||
final CasAssertionAuthenticationToken token = new CasAssertionAuthenticationToken(assertion, "");
|
||||
return this.authenticationUserDetailsService.loadUserDetails(token);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
/**
|
||||
* Sets the UserDetailsService to use. This is a convenience method to invoke
|
||||
*/
|
||||
public void setUserDetailsService(final UserDetailsService userDetailsService) {
|
||||
this.authenticationUserDetailsService = new UserDetailsByNameServiceWrapper(userDetailsService);
|
||||
}
|
||||
|
||||
public void setAuthenticationUserDetailsService(
|
||||
final AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService) {
|
||||
this.authenticationUserDetailsService = authenticationUserDetailsService;
|
||||
}
|
||||
|
||||
public void setServiceProperties(final ServiceProperties serviceProperties) {
|
||||
this.serviceProperties = serviceProperties;
|
||||
}
|
||||
|
||||
protected String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public StatelessTicketCache getStatelessTicketCache() {
|
||||
return this.statelessTicketCache;
|
||||
}
|
||||
|
||||
protected TicketValidator getTicketValidator() {
|
||||
return this.ticketValidator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageSource(final MessageSource messageSource) {
|
||||
this.messages = new MessageSourceAccessor(messageSource);
|
||||
}
|
||||
|
||||
public void setStatelessTicketCache(final StatelessTicketCache statelessTicketCache) {
|
||||
this.statelessTicketCache = statelessTicketCache;
|
||||
}
|
||||
|
||||
public void setTicketValidator(final TicketValidator ticketValidator) {
|
||||
this.ticketValidator = ticketValidator;
|
||||
}
|
||||
|
||||
public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) {
|
||||
this.authoritiesMapper = authoritiesMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(final Class<?> authentication) {
|
||||
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication))
|
||||
|| (CasAuthenticationToken.class.isAssignableFrom(authentication))
|
||||
|| (CasAssertionAuthenticationToken.class.isAssignableFrom(authentication));
|
||||
}
|
||||
|
||||
}
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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.cas.authentication;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.SpringSecurityCoreVersion;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Represents a successful CAS <code>Authentication</code>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Scott Battaglia
|
||||
*/
|
||||
public class CasAuthenticationToken extends AbstractAuthenticationToken implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
|
||||
|
||||
private final Object credentials;
|
||||
|
||||
private final Object principal;
|
||||
|
||||
private final UserDetails userDetails;
|
||||
|
||||
private final int keyHash;
|
||||
|
||||
private final Assertion assertion;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param key to identify if this object made by a given
|
||||
* {@link CasAuthenticationProvider}
|
||||
* @param principal typically the UserDetails object (cannot be <code>null</code>)
|
||||
* @param credentials the service/proxy ticket ID from CAS (cannot be
|
||||
* <code>null</code>)
|
||||
* @param authorities the authorities granted to the user (from the
|
||||
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
|
||||
* be <code>null</code>)
|
||||
* @param userDetails the user details (from the
|
||||
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
|
||||
* be <code>null</code>)
|
||||
* @param assertion the assertion returned from the CAS servers. It contains the
|
||||
* principal and how to obtain a proxy ticket for the user.
|
||||
* @throws IllegalArgumentException if a <code>null</code> was passed
|
||||
*/
|
||||
public CasAuthenticationToken(final String key, final Object principal, final Object credentials,
|
||||
final Collection<? extends GrantedAuthority> authorities, final UserDetails userDetails,
|
||||
final Assertion assertion) {
|
||||
this(extractKeyHash(key), principal, credentials, authorities, userDetails, assertion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor for Jackson Deserialization support
|
||||
* @param keyHash hashCode of provided key to identify if this object made by a given
|
||||
* {@link CasAuthenticationProvider}
|
||||
* @param principal typically the UserDetails object (cannot be <code>null</code>)
|
||||
* @param credentials the service/proxy ticket ID from CAS (cannot be
|
||||
* <code>null</code>)
|
||||
* @param authorities the authorities granted to the user (from the
|
||||
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
|
||||
* be <code>null</code>)
|
||||
* @param userDetails the user details (from the
|
||||
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
|
||||
* be <code>null</code>)
|
||||
* @param assertion the assertion returned from the CAS servers. It contains the
|
||||
* principal and how to obtain a proxy ticket for the user.
|
||||
* @throws IllegalArgumentException if a <code>null</code> was passed
|
||||
* @since 4.2
|
||||
*/
|
||||
private CasAuthenticationToken(final Integer keyHash, final Object principal, final Object credentials,
|
||||
final Collection<? extends GrantedAuthority> authorities, final UserDetails userDetails,
|
||||
final Assertion assertion) {
|
||||
super(authorities);
|
||||
if ((principal == null) || "".equals(principal) || (credentials == null) || "".equals(credentials)
|
||||
|| (authorities == null) || (userDetails == null) || (assertion == null)) {
|
||||
throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
|
||||
}
|
||||
this.keyHash = keyHash;
|
||||
this.principal = principal;
|
||||
this.credentials = credentials;
|
||||
this.userDetails = userDetails;
|
||||
this.assertion = assertion;
|
||||
setAuthenticated(true);
|
||||
}
|
||||
|
||||
private static Integer extractKeyHash(String key) {
|
||||
Assert.hasLength(key, "key cannot be null or empty");
|
||||
return key.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (!super.equals(obj)) {
|
||||
return false;
|
||||
}
|
||||
if (obj instanceof CasAuthenticationToken) {
|
||||
CasAuthenticationToken test = (CasAuthenticationToken) obj;
|
||||
if (!this.assertion.equals(test.getAssertion())) {
|
||||
return false;
|
||||
}
|
||||
if (this.getKeyHash() != test.getKeyHash()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + this.credentials.hashCode();
|
||||
result = 31 * result + this.principal.hashCode();
|
||||
result = 31 * result + this.userDetails.hashCode();
|
||||
result = 31 * result + this.keyHash;
|
||||
result = 31 * result + ObjectUtils.nullSafeHashCode(this.assertion);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCredentials() {
|
||||
return this.credentials;
|
||||
}
|
||||
|
||||
public int getKeyHash() {
|
||||
return this.keyHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrincipal() {
|
||||
return this.principal;
|
||||
}
|
||||
|
||||
public Assertion getAssertion() {
|
||||
return this.assertion;
|
||||
}
|
||||
|
||||
public UserDetails getUserDetails() {
|
||||
return this.userDetails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(super.toString());
|
||||
sb.append(" Assertion: ").append(this.assertion);
|
||||
sb.append(" Credentials (Service/Proxy Ticket): ").append(this.credentials);
|
||||
return (sb.toString());
|
||||
}
|
||||
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.cas.authentication;
|
||||
|
||||
import net.sf.ehcache.Ehcache;
|
||||
import net.sf.ehcache.Element;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Caches tickets using a Spring IoC defined
|
||||
* <a href="https://www.ehcache.org/">EHCACHE</a>.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @deprecated since 5.6. In favor of JCache based implementations
|
||||
*/
|
||||
@Deprecated
|
||||
public class EhCacheBasedTicketCache implements StatelessTicketCache, InitializingBean {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(EhCacheBasedTicketCache.class);
|
||||
|
||||
private Ehcache cache;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(this.cache, "cache mandatory");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
|
||||
final Element element = this.cache.get(serviceTicket);
|
||||
logger.debug(LogMessage.of(() -> "Cache hit: " + (element != null) + "; service ticket: " + serviceTicket));
|
||||
return (element != null) ? (CasAuthenticationToken) element.getValue() : null;
|
||||
}
|
||||
|
||||
public Ehcache getCache() {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putTicketInCache(final CasAuthenticationToken token) {
|
||||
final Element element = new Element(token.getCredentials().toString(), token);
|
||||
logger.debug(LogMessage.of(() -> "Cache put: " + element.getKey()));
|
||||
this.cache.put(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTicketFromCache(final CasAuthenticationToken token) {
|
||||
logger.debug(LogMessage.of(() -> "Cache remove: " + token.getCredentials().toString()));
|
||||
this.removeTicketFromCache(token.getCredentials().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTicketFromCache(final String serviceTicket) {
|
||||
this.cache.remove(serviceTicket);
|
||||
}
|
||||
|
||||
public void setCache(final Ehcache cache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.cas.authentication;
|
||||
|
||||
/**
|
||||
* Implementation of @link {@link StatelessTicketCache} that has no backing cache. Useful
|
||||
* in instances where storing of tickets for stateless session management is not required.
|
||||
* <p>
|
||||
* This is the default StatelessTicketCache of the @link {@link CasAuthenticationProvider}
|
||||
* to eliminate the unnecessary dependency on EhCache that applications have even if they
|
||||
* are not using the stateless session management.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @see CasAuthenticationProvider
|
||||
*/
|
||||
public final class NullStatelessTicketCache implements StatelessTicketCache {
|
||||
|
||||
/**
|
||||
* @return null since we are not storing any tickets.
|
||||
*/
|
||||
@Override
|
||||
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a no-op since we are not storing tickets.
|
||||
*/
|
||||
@Override
|
||||
public void putTicketInCache(final CasAuthenticationToken token) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a no-op since we are not storing tickets.
|
||||
*/
|
||||
@Override
|
||||
public void removeTicketFromCache(final CasAuthenticationToken token) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a no-op since we are not storing tickets.
|
||||
*/
|
||||
@Override
|
||||
public void removeTicketFromCache(final String serviceTicket) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.cas.authentication;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Caches tickets using a Spring IoC defined {@link Cache}.
|
||||
*
|
||||
* @author Marten Deinum
|
||||
* @since 3.2
|
||||
*
|
||||
*/
|
||||
public class SpringCacheBasedTicketCache implements StatelessTicketCache {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(SpringCacheBasedTicketCache.class);
|
||||
|
||||
private final Cache cache;
|
||||
|
||||
public SpringCacheBasedTicketCache(Cache cache) {
|
||||
Assert.notNull(cache, "cache mandatory");
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
|
||||
final Cache.ValueWrapper element = (serviceTicket != null) ? this.cache.get(serviceTicket) : null;
|
||||
logger.debug(LogMessage.of(() -> "Cache hit: " + (element != null) + "; service ticket: " + serviceTicket));
|
||||
return (element != null) ? (CasAuthenticationToken) element.get() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putTicketInCache(final CasAuthenticationToken token) {
|
||||
String key = token.getCredentials().toString();
|
||||
logger.debug(LogMessage.of(() -> "Cache put: " + key));
|
||||
this.cache.put(key, token);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTicketFromCache(final CasAuthenticationToken token) {
|
||||
logger.debug(LogMessage.of(() -> "Cache remove: " + token.getCredentials().toString()));
|
||||
this.removeTicketFromCache(token.getCredentials().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTicketFromCache(final String serviceTicket) {
|
||||
this.cache.evict(serviceTicket);
|
||||
}
|
||||
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2004 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.cas.authentication;
|
||||
|
||||
/**
|
||||
* Caches CAS service tickets and CAS proxy tickets for stateless connections.
|
||||
*
|
||||
* <p>
|
||||
* When a service ticket or proxy ticket is validated against the CAS server, it is unable
|
||||
* to be used again. Most types of callers are stateful and are associated with a given
|
||||
* <code>HttpSession</code>. This allows the affirmative CAS validation outcome to be
|
||||
* stored in the <code>HttpSession</code>, meaning the removal of the ticket from the CAS
|
||||
* server is not an issue.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* Stateless callers, such as remoting protocols, cannot take advantage of
|
||||
* <code>HttpSession</code>. If the stateless caller is located a significant network
|
||||
* distance from the CAS server, acquiring a fresh service ticket or proxy ticket for each
|
||||
* invocation would be expensive.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* To avoid this issue with stateless callers, it is expected stateless callers will
|
||||
* obtain a single service ticket or proxy ticket, and then present this same ticket to
|
||||
* the Spring Security secured application on each occasion. As no
|
||||
* <code>HttpSession</code> is available for such callers, the affirmative CAS validation
|
||||
* outcome cannot be stored in this location.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* The <code>StatelessTicketCache</code> enables the service tickets and proxy tickets
|
||||
* belonging to stateless callers to be placed in a cache. This in-memory cache stores the
|
||||
* <code>CasAuthenticationToken</code>, effectively providing the same capability as a
|
||||
* <code>HttpSession</code> with the ticket identifier being the key rather than a session
|
||||
* identifier.
|
||||
* </p>
|
||||
*
|
||||
* <P>
|
||||
* Implementations should provide a reasonable timeout on stored entries, such that the
|
||||
* stateless caller are not required to unnecessarily acquire fresh CAS service tickets or
|
||||
* proxy tickets.
|
||||
* </p>
|
||||
*
|
||||
* @author Ben Alex
|
||||
*/
|
||||
public interface StatelessTicketCache {
|
||||
|
||||
/**
|
||||
* Retrieves the <code>CasAuthenticationToken</code> associated with the specified
|
||||
* ticket.
|
||||
*
|
||||
* <P>
|
||||
* If not found, returns a <code>null</code><code>CasAuthenticationToken</code>.
|
||||
* </p>
|
||||
* @return the fully populated authentication token
|
||||
*/
|
||||
CasAuthenticationToken getByTicketId(String serviceTicket);
|
||||
|
||||
/**
|
||||
* Adds the specified <code>CasAuthenticationToken</code> to the cache.
|
||||
*
|
||||
* <P>
|
||||
* The {@link CasAuthenticationToken#getCredentials()} method is used to retrieve the
|
||||
* service ticket number.
|
||||
* </p>
|
||||
* @param token to be added to the cache
|
||||
*/
|
||||
void putTicketInCache(CasAuthenticationToken token);
|
||||
|
||||
/**
|
||||
* Removes the specified ticket from the cache, as per
|
||||
* {@link #removeTicketFromCache(String)}.
|
||||
*
|
||||
* <P>
|
||||
* Implementations should use {@link CasAuthenticationToken#getCredentials()} to
|
||||
* obtain the ticket and then delegate to the {@link #removeTicketFromCache(String)}
|
||||
* method.
|
||||
* </p>
|
||||
* @param token to be removed
|
||||
*/
|
||||
void removeTicketFromCache(CasAuthenticationToken token);
|
||||
|
||||
/**
|
||||
* Removes the specified ticket from the cache, meaning that future calls will require
|
||||
* a new service ticket.
|
||||
*
|
||||
* <P>
|
||||
* This is in case applications wish to provide a session termination capability for
|
||||
* their stateless clients.
|
||||
* </p>
|
||||
* @param serviceTicket to be removed
|
||||
*/
|
||||
void removeTicketFromCache(String serviceTicket);
|
||||
|
||||
}
|
||||
+4
-15
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 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.
|
||||
@@ -14,19 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.oauth2.client.web;
|
||||
|
||||
/**
|
||||
* @author Steve Riesenberg
|
||||
* @since 5.8
|
||||
* An {@code AuthenticationProvider} that can process CAS service tickets and proxy
|
||||
* tickets.
|
||||
*/
|
||||
class InvalidClientRegistrationIdException extends IllegalArgumentException {
|
||||
|
||||
/**
|
||||
* @param message the exception message
|
||||
*/
|
||||
InvalidClientRegistrationIdException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
package org.springframework.security.cas.authentication;
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2015-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.cas.jackson2;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import org.jasig.cas.client.authentication.AttributePrincipal;
|
||||
|
||||
/**
|
||||
* Helps in jackson deserialization of class
|
||||
* {@link org.jasig.cas.client.validation.AssertionImpl}, which is used with
|
||||
* {@link org.springframework.security.cas.authentication.CasAuthenticationToken}. To use
|
||||
* this class we need to register with
|
||||
* {@link com.fasterxml.jackson.databind.ObjectMapper}. Type information will be stored
|
||||
* in @class property.
|
||||
* <p>
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new CasJackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see CasJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
class AssertionImplMixin {
|
||||
|
||||
/**
|
||||
* Mixin Constructor helps in deserialize
|
||||
* {@link org.jasig.cas.client.validation.AssertionImpl}
|
||||
* @param principal the Principal to associate with the Assertion.
|
||||
* @param validFromDate when the assertion is valid from.
|
||||
* @param validUntilDate when the assertion is valid to.
|
||||
* @param authenticationDate when the assertion is authenticated.
|
||||
* @param attributes the key/value pairs for this attribute.
|
||||
*/
|
||||
@JsonCreator
|
||||
AssertionImplMixin(@JsonProperty("principal") AttributePrincipal principal,
|
||||
@JsonProperty("validFromDate") Date validFromDate, @JsonProperty("validUntilDate") Date validUntilDate,
|
||||
@JsonProperty("authenticationDate") Date authenticationDate,
|
||||
@JsonProperty("attributes") Map<String, Object> attributes) {
|
||||
}
|
||||
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2015-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.cas.jackson2;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import org.jasig.cas.client.proxy.ProxyRetriever;
|
||||
|
||||
/**
|
||||
* Helps in deserialize {@link org.jasig.cas.client.authentication.AttributePrincipalImpl}
|
||||
* which is used with
|
||||
* {@link org.springframework.security.cas.authentication.CasAuthenticationToken}. Type
|
||||
* information will be stored in property named @class.
|
||||
* <p>
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new CasJackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see CasJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
class AttributePrincipalImplMixin {
|
||||
|
||||
/**
|
||||
* Mixin Constructor helps in deserialize
|
||||
* {@link org.jasig.cas.client.authentication.AttributePrincipalImpl}
|
||||
* @param name the unique identifier for the principal.
|
||||
* @param attributes the key/value pairs for this principal.
|
||||
* @param proxyGrantingTicket the ticket associated with this principal.
|
||||
* @param proxyRetriever the ProxyRetriever implementation to call back to the CAS
|
||||
* server.
|
||||
*/
|
||||
@JsonCreator
|
||||
AttributePrincipalImplMixin(@JsonProperty("name") String name,
|
||||
@JsonProperty("attributes") Map<String, Object> attributes,
|
||||
@JsonProperty("proxyGrantingTicket") String proxyGrantingTicket,
|
||||
@JsonProperty("proxyRetriever") ProxyRetriever proxyRetriever) {
|
||||
}
|
||||
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2015-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.cas.jackson2;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
|
||||
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
|
||||
import org.springframework.security.cas.authentication.CasAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
/**
|
||||
* Mixin class which helps in deserialize
|
||||
* {@link org.springframework.security.cas.authentication.CasAuthenticationToken} using
|
||||
* jackson. Two more dependent classes needs to register along with this mixin class.
|
||||
* <ol>
|
||||
* <li>{@link org.springframework.security.cas.jackson2.AssertionImplMixin}</li>
|
||||
* <li>{@link org.springframework.security.cas.jackson2.AttributePrincipalImplMixin}</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new CasJackson2Module());
|
||||
* </pre>
|
||||
*
|
||||
* @author Jitendra Singh
|
||||
* @since 4.2
|
||||
* @see CasJackson2Module
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
*/
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,
|
||||
getterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
class CasAuthenticationTokenMixin {
|
||||
|
||||
/**
|
||||
* Mixin Constructor helps in deserialize {@link CasAuthenticationToken}
|
||||
* @param keyHash hashCode of provided key to identify if this object made by a given
|
||||
* {@link CasAuthenticationProvider}
|
||||
* @param principal typically the UserDetails object (cannot be <code>null</code>)
|
||||
* @param credentials the service/proxy ticket ID from CAS (cannot be
|
||||
* <code>null</code>)
|
||||
* @param authorities the authorities granted to the user (from the
|
||||
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
|
||||
* be <code>null</code>)
|
||||
* @param userDetails the user details (from the
|
||||
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
|
||||
* be <code>null</code>)
|
||||
* @param assertion the assertion returned from the CAS servers. It contains the
|
||||
* principal and how to obtain a proxy ticket for the user.
|
||||
*/
|
||||
@JsonCreator
|
||||
CasAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash, @JsonProperty("principal") Object principal,
|
||||
@JsonProperty("credentials") Object credentials,
|
||||
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
|
||||
@JsonProperty("userDetails") UserDetails userDetails, @JsonProperty("assertion") Assertion assertion) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2015-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.cas.jackson2;
|
||||
|
||||
import com.fasterxml.jackson.core.Version;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import org.jasig.cas.client.authentication.AttributePrincipalImpl;
|
||||
import org.jasig.cas.client.validation.AssertionImpl;
|
||||
|
||||
import org.springframework.security.cas.authentication.CasAuthenticationToken;
|
||||
import org.springframework.security.jackson2.SecurityJackson2Modules;
|
||||
|
||||
/**
|
||||
* Jackson module for spring-security-cas. This module register
|
||||
* {@link AssertionImplMixin}, {@link AttributePrincipalImplMixin} and
|
||||
* {@link CasAuthenticationTokenMixin}. If no default typing enabled by default then it'll
|
||||
* enable it because typing info is needed to properly serialize/deserialize objects. In
|
||||
* order to use this module just add this module into your ObjectMapper configuration.
|
||||
*
|
||||
* <pre>
|
||||
* ObjectMapper mapper = new ObjectMapper();
|
||||
* mapper.registerModule(new CasJackson2Module());
|
||||
* </pre> <b>Note: use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get list
|
||||
* of all security modules on the classpath.</b>
|
||||
*
|
||||
* @author Jitendra Singh.
|
||||
* @since 4.2
|
||||
* @see org.springframework.security.jackson2.SecurityJackson2Modules
|
||||
*/
|
||||
public class CasJackson2Module extends SimpleModule {
|
||||
|
||||
public CasJackson2Module() {
|
||||
super(CasJackson2Module.class.getName(), new Version(1, 0, 0, null, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupModule(SetupContext context) {
|
||||
SecurityJackson2Modules.enableDefaultTyping(context.getOwner());
|
||||
context.setMixInAnnotations(AssertionImpl.class, AssertionImplMixin.class);
|
||||
context.setMixInAnnotations(AttributePrincipalImpl.class, AttributePrincipalImplMixin.class);
|
||||
context.setMixInAnnotations(CasAuthenticationToken.class, CasAuthenticationTokenMixin.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Spring Security support for Jasig's Central Authentication Service
|
||||
* (<a href="https://www.jasig.org/cas">CAS</a>).
|
||||
*/
|
||||
package org.springframework.security.cas;
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.cas.userdetails;
|
||||
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
|
||||
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
|
||||
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
/**
|
||||
* Abstract class for using the provided CAS assertion to construct a new User object.
|
||||
* This generally is most useful when combined with a SAML-based response from the CAS
|
||||
* Server/client.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class AbstractCasAssertionUserDetailsService
|
||||
implements AuthenticationUserDetailsService<CasAssertionAuthenticationToken> {
|
||||
|
||||
@Override
|
||||
public final UserDetails loadUserDetails(final CasAssertionAuthenticationToken token) {
|
||||
return loadUserDetails(token.getAssertion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected template method for construct a
|
||||
* {@link org.springframework.security.core.userdetails.UserDetails} via the supplied
|
||||
* CAS assertion.
|
||||
* @param assertion the assertion to use to construct the new UserDetails. CANNOT be
|
||||
* NULL.
|
||||
* @return the newly constructed UserDetails.
|
||||
*/
|
||||
protected abstract UserDetails loadUserDetails(Assertion assertion);
|
||||
|
||||
}
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.cas.userdetails;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jasig.cas.client.validation.Assertion;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Populates the {@link org.springframework.security.core.GrantedAuthority}s for a user by
|
||||
* reading a list of attributes that were returned as part of the CAS response. Each
|
||||
* attribute is read and each value of the attribute is turned into a GrantedAuthority. If
|
||||
* the attribute has no value then its not added.
|
||||
*
|
||||
* @author Scott Battaglia
|
||||
* @since 3.0
|
||||
*/
|
||||
public final class GrantedAuthorityFromAssertionAttributesUserDetailsService
|
||||
extends AbstractCasAssertionUserDetailsService {
|
||||
|
||||
private static final String NON_EXISTENT_PASSWORD_VALUE = "NO_PASSWORD";
|
||||
|
||||
private final String[] attributes;
|
||||
|
||||
private boolean convertToUpperCase = true;
|
||||
|
||||
public GrantedAuthorityFromAssertionAttributesUserDetailsService(final String[] attributes) {
|
||||
Assert.notNull(attributes, "attributes cannot be null.");
|
||||
Assert.isTrue(attributes.length > 0, "At least one attribute is required to retrieve roles from.");
|
||||
this.attributes = attributes;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected UserDetails loadUserDetails(final Assertion assertion) {
|
||||
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
|
||||
for (String attribute : this.attributes) {
|
||||
Object value = assertion.getPrincipal().getAttributes().get(attribute);
|
||||
if (value != null) {
|
||||
if (value instanceof List) {
|
||||
for (Object o : (List<?>) value) {
|
||||
grantedAuthorities.add(createSimpleGrantedAuthority(o));
|
||||
}
|
||||
}
|
||||
else {
|
||||
grantedAuthorities.add(createSimpleGrantedAuthority(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
return new User(assertion.getPrincipal().getName(), NON_EXISTENT_PASSWORD_VALUE, true, true, true, true,
|
||||
grantedAuthorities);
|
||||
}
|
||||
|
||||
private SimpleGrantedAuthority createSimpleGrantedAuthority(Object o) {
|
||||
return new SimpleGrantedAuthority(this.convertToUpperCase ? o.toString().toUpperCase() : o.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the returned attribute values to uppercase values.
|
||||
* @param convertToUpperCase true if it should convert, false otherwise.
|
||||
*/
|
||||
public void setConvertToUpperCase(final boolean convertToUpperCase) {
|
||||
this.convertToUpperCase = convertToUpperCase;
|
||||
}
|
||||
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.cas.web;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.jasig.cas.client.util.CommonUtils;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.security.cas.ServiceProperties;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Used by the <code>ExceptionTranslationFilter</code> to commence authentication via the
|
||||
* JA-SIG Central Authentication Service (CAS).
|
||||
* <p>
|
||||
* The user's browser will be redirected to the JA-SIG CAS enterprise-wide login page.
|
||||
* This page is specified by the <code>loginUrl</code> property. Once login is complete,
|
||||
* the CAS login page will redirect to the page indicated by the <code>service</code>
|
||||
* property. The <code>service</code> is a HTTP URL belonging to the current application.
|
||||
* The <code>service</code> URL is monitored by the {@link CasAuthenticationFilter}, which
|
||||
* will validate the CAS login was successful.
|
||||
*
|
||||
* @author Ben Alex
|
||||
* @author Scott Battaglia
|
||||
*/
|
||||
public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {
|
||||
|
||||
private ServiceProperties serviceProperties;
|
||||
|
||||
private String loginUrl;
|
||||
|
||||
/**
|
||||
* Determines whether the Service URL should include the session id for the specific
|
||||
* user. As of CAS 3.0.5, the session id will automatically be stripped. However,
|
||||
* older versions of CAS (i.e. CAS 2), do not automatically strip the session
|
||||
* identifier (this is a bug on the part of the older server implementations), so an
|
||||
* option to disable the session encoding is provided for backwards compatibility.
|
||||
*
|
||||
* By default, encoding is enabled.
|
||||
*/
|
||||
private boolean encodeServiceUrlWithSessionId = true;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Assert.hasLength(this.loginUrl, "loginUrl must be specified");
|
||||
Assert.notNull(this.serviceProperties, "serviceProperties must be specified");
|
||||
Assert.notNull(this.serviceProperties.getService(), "serviceProperties.getService() cannot be null.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void commence(final HttpServletRequest servletRequest, HttpServletResponse response,
|
||||
AuthenticationException authenticationException) throws IOException {
|
||||
String urlEncodedService = createServiceUrl(servletRequest, response);
|
||||
String redirectUrl = createRedirectUrl(urlEncodedService);
|
||||
preCommence(servletRequest, response);
|
||||
response.sendRedirect(redirectUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new Service Url. The default implementation relies on the CAS client
|
||||
* to do the bulk of the work.
|
||||
* @param request the HttpServletRequest
|
||||
* @param response the HttpServlet Response
|
||||
* @return the constructed service url. CANNOT be NULL.
|
||||
*/
|
||||
protected String createServiceUrl(HttpServletRequest request, HttpServletResponse response) {
|
||||
return CommonUtils.constructServiceUrl(null, response, this.serviceProperties.getService(), null,
|
||||
this.serviceProperties.getArtifactParameter(), this.encodeServiceUrlWithSessionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs the Url for Redirection to the CAS server. Default implementation relies
|
||||
* on the CAS client to do the bulk of the work.
|
||||
* @param serviceUrl the service url that should be included.
|
||||
* @return the redirect url. CANNOT be NULL.
|
||||
*/
|
||||
protected String createRedirectUrl(String serviceUrl) {
|
||||
return CommonUtils.constructRedirectUrl(this.loginUrl, this.serviceProperties.getServiceParameter(), serviceUrl,
|
||||
this.serviceProperties.isSendRenew(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method for you to do your own pre-processing before the redirect occurs.
|
||||
* @param request the HttpServletRequest
|
||||
* @param response the HttpServletResponse
|
||||
*/
|
||||
protected void preCommence(HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The enterprise-wide CAS login URL. Usually something like
|
||||
* <code>https://www.mycompany.com/cas/login</code>.
|
||||
* @return the enterprise-wide CAS login URL
|
||||
*/
|
||||
public final String getLoginUrl() {
|
||||
return this.loginUrl;
|
||||
}
|
||||
|
||||
public final ServiceProperties getServiceProperties() {
|
||||
return this.serviceProperties;
|
||||
}
|
||||
|
||||
public final void setLoginUrl(String loginUrl) {
|
||||
this.loginUrl = loginUrl;
|
||||
}
|
||||
|
||||
public final void setServiceProperties(ServiceProperties serviceProperties) {
|
||||
this.serviceProperties = serviceProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to encode the service url with the session id or not.
|
||||
* @param encodeServiceUrlWithSessionId whether to encode the service url with the
|
||||
* session id or not.
|
||||
*/
|
||||
public final void setEncodeServiceUrlWithSessionId(boolean encodeServiceUrlWithSessionId) {
|
||||
this.encodeServiceUrlWithSessionId = encodeServiceUrlWithSessionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to encode the service url with the session id or not.
|
||||
* @return whether to encode the service url with the session id or not.
|
||||
*
|
||||
*/
|
||||
protected boolean getEncodeServiceUrlWithSessionId() {
|
||||
return this.encodeServiceUrlWithSessionId;
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user