Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 02b8d4cb32 | |||
| e0655bee7c | |||
| 1a90041271 | |||
| 2ee188ee36 | |||
| 5bfd1f59d1 | |||
| 4b10c2daac | |||
| 0e4d29763f | |||
| 63e24a3026 | |||
| 514600721c | |||
| 2563b854ea | |||
| 9027dceb71 | |||
| d61a85f74b | |||
| 8b3d33ed64 | |||
| defe27d53e | |||
| c5b86370ff | |||
| da796e44e5 | |||
| f5b9dc3282 | |||
| 1ebd663253 | |||
| a8c60393f1 | |||
| 09198447af | |||
| 9ebee3e984 | |||
| 60e3f27211 | |||
| f7650b3ec0 | |||
| e1ec77a7a1 | |||
| 16e0d61005 | |||
| befa003f94 | |||
| 74a77d2f72 | |||
| a80fdee533 | |||
| 53b581bb8c | |||
| d686037419 | |||
| 5c8311b7d3 | |||
| d86545dd41 | |||
| ee1031356a | |||
| f74aeaffa1 | |||
| ec1b15ed68 | |||
| 93aa48f863 | |||
| ce995a0178 | |||
| bbc5c5ce84 | |||
| 1b8cf6cc55 | |||
| e5ee45d568 | |||
| 38ae090d3d | |||
| 3b9991fc89 | |||
| 1ecb036fba | |||
| 0e211382ee | |||
| 2477c22674 | |||
| d88f2e5133 | |||
| c35e107744 | |||
| 3f7d9e389d | |||
| 992243fc8b | |||
| d0341bf893 | |||
| 8271258618 | |||
| 761fdd228d | |||
| 1010d253a4 | |||
| c8342fcfae | |||
| c97f710747 | |||
| 2287d67106 | |||
| da4af08362 | |||
| 61df8e493d | |||
| 470e2c5c97 | |||
| ade45771b2 | |||
| 657760af5b | |||
| 8a03482856 | |||
| 71a51db3a1 | |||
| 17873adf1f | |||
| 4bab573562 |
@@ -1,23 +0,0 @@
|
||||
name: Clean build artifacts
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
main:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
permissions:
|
||||
contents: none
|
||||
steps:
|
||||
- name: Delete artifacts in cron job
|
||||
env:
|
||||
GH_ACTIONS_REPO_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
run: |
|
||||
echo "Running clean build artifacts logic"
|
||||
output=$(curl -X GET -H "Authorization: token $GH_ACTIONS_REPO_TOKEN" https://api.github.com/repos/spring-projects/spring-security/actions/artifacts | grep '"id"' | cut -d : -f2 | sed 's/,*$//g')
|
||||
echo Output is $output
|
||||
for id in $output; do curl -X DELETE -H "Authorization: token $GH_ACTIONS_REPO_TOKEN" https://api.github.com/repos/spring-projects/spring-security/actions/artifacts/$id; done;
|
||||
@@ -1,130 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- "dependabot/**"
|
||||
schedule:
|
||||
- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch: # Manual trigger
|
||||
|
||||
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 }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/build.yml@v1
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, windows-latest ]
|
||||
jdk: [ 17 ]
|
||||
with:
|
||||
runs-on: ${{ matrix.os }}
|
||||
java-version: ${{ matrix.jdk }}
|
||||
distribution: temurin
|
||||
secrets: inherit
|
||||
test:
|
||||
name: Test Against Snapshots
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/test.yml@v1
|
||||
strategy:
|
||||
matrix:
|
||||
java-version: [ 17 ]
|
||||
with:
|
||||
java-version: ${{ matrix.java-version }}
|
||||
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot -PisOverrideVersionCatalog -PspringFrameworkVersion=6.0.+ -PreactorVersion=2022.0.+ -PspringDataVersion=2022.0.+ --stacktrace
|
||||
secrets: inherit
|
||||
check-samples:
|
||||
name: Check Samples
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'spring-projects' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v2
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
- name: Check samples project
|
||||
env:
|
||||
LOCAL_REPOSITORY_PATH: ${{ github.workspace }}/build/publications/repos
|
||||
SAMPLES_DIR: ../spring-security-samples
|
||||
run: |
|
||||
# Extract version from gradle.properties
|
||||
version=$(cat gradle.properties | grep "version=" | awk -F'=' '{print $2}')
|
||||
# Extract samplesBranch from gradle.properties
|
||||
samples_branch=$(cat gradle.properties | grep "samplesBranch=" | awk -F'=' '{print $2}')
|
||||
./gradlew publishMavenJavaPublicationToLocalRepository
|
||||
./gradlew cloneRepository -PrepositoryName="spring-projects/spring-security-samples" -Pref="$samples_branch" -PcloneOutputDirectory="$SAMPLES_DIR"
|
||||
./gradlew --project-dir "$SAMPLES_DIR" --init-script spring-security-ci.gradle -PlocalRepositoryPath="$LOCAL_REPOSITORY_PATH" -PspringSecurityVersion="$version" :runAllTests
|
||||
check-tangles:
|
||||
name: Check for Package Tangles
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'spring-projects' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v2
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: temurin
|
||||
- name: Check for package tangles
|
||||
env:
|
||||
STRUCTURE101_LICENSEID: ${{ secrets.STRUCTURE101_LICENSEID }}
|
||||
run: |
|
||||
./gradlew check s101 -Ps101.licenseId="$STRUCTURE101_LICENSEID" --stacktrace
|
||||
deploy-artifacts:
|
||||
name: Deploy Artifacts
|
||||
needs: [ build, test, check-samples, check-tangles ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@v1
|
||||
with:
|
||||
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
secrets: inherit
|
||||
deploy-docs:
|
||||
name: Deploy Docs
|
||||
needs: [ build, test, check-samples, check-tangles ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-docs.yml@v1
|
||||
with:
|
||||
should-deploy-docs: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
secrets: inherit
|
||||
deploy-schema:
|
||||
name: Deploy Schema
|
||||
needs: [ build, test, check-samples, check-tangles ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@v1
|
||||
with:
|
||||
should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }}
|
||||
secrets: inherit
|
||||
perform-release:
|
||||
name: Perform Release
|
||||
needs: [ deploy-artifacts, deploy-docs, deploy-schema ]
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@v1
|
||||
with:
|
||||
should-perform-release: ${{ needs.deploy-artifacts.outputs.artifacts-deployed }}
|
||||
project-version: ${{ needs.deploy-artifacts.outputs.project-version }}
|
||||
milestone-repo-url: https://repo.spring.io/artifactory/milestone
|
||||
release-repo-url: https://repo1.maven.org/maven2
|
||||
artifact-path: org/springframework/security/spring-security-core
|
||||
slack-announcing-id: spring-security-announcing
|
||||
secrets: inherit
|
||||
notify_result:
|
||||
name: Check for failures
|
||||
needs: [ perform-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'
|
||||
@@ -1,33 +0,0 @@
|
||||
name: Deploy Docs
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- "gh-pages"
|
||||
- "dependabot/**"
|
||||
tags: '**'
|
||||
repository_dispatch:
|
||||
types: request-build-reference # legacy
|
||||
#schedule:
|
||||
#- cron: '0 10 * * *' # Once per day at 10am UTC
|
||||
workflow_dispatch:
|
||||
permissions: read-all
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: docs-build
|
||||
fetch-depth: 1
|
||||
- name: Dispatch (partial build)
|
||||
if: github.ref_type == 'branch'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }}
|
||||
- name: Dispatch (full build)
|
||||
if: github.ref_type == 'tag'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD)
|
||||
@@ -1,52 +0,0 @@
|
||||
name: Merge Dependabot PR
|
||||
|
||||
on: pull_request_target
|
||||
|
||||
run-name: Merge Dependabot PR ${{ github.ref_name }}
|
||||
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
merge-dependabot-pr:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
show-progress: false
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: Set Milestone to Dependabot Pull Request
|
||||
id: set-milestone
|
||||
run: |
|
||||
if test -f pom.xml
|
||||
then
|
||||
CURRENT_VERSION=$(mvn help:evaluate -Dexpression="project.version" -q -DforceStdout)
|
||||
else
|
||||
CURRENT_VERSION=$(cat gradle.properties | sed -n '/^version=/ { s/^version=//;p }')
|
||||
fi
|
||||
export CANDIDATE_VERSION=${CURRENT_VERSION/-SNAPSHOT}
|
||||
MILESTONE=$(gh api repos/$GITHUB_REPOSITORY/milestones --jq 'map(select(.due_on != null and (.title | startswith(env.CANDIDATE_VERSION)))) | .[0] | .title')
|
||||
|
||||
if [ -z $MILESTONE ]
|
||||
then
|
||||
gh run cancel ${{ github.run_id }}
|
||||
echo "::warning title=Cannot merge::No scheduled milestone for $CURRENT_VERSION version"
|
||||
else
|
||||
gh pr edit ${{ github.event.pull_request.number }} --milestone $MILESTONE
|
||||
echo mergeEnabled=true >> $GITHUB_OUTPUT
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Merge Dependabot pull request
|
||||
if: steps.set-milestone.outputs.mergeEnabled
|
||||
run: gh pr merge ${{ github.event.pull_request.number }} --auto --rebase
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
@@ -1,40 +0,0 @@
|
||||
name: Check Milestone
|
||||
on:
|
||||
milestone:
|
||||
types: [created, opened, edited]
|
||||
env:
|
||||
DUE_ON: ${{ github.event.milestone.due_on }}
|
||||
TITLE: ${{ github.event.milestone.title }}
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
spring-releasetrain-checks:
|
||||
name: Check DueOn is on a Release Date
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
permissions:
|
||||
contents: none
|
||||
steps:
|
||||
- name: Print Milestone Being Checked
|
||||
run: echo "Validating DueOn '$DUE_ON' for milestone '$TITLE'"
|
||||
- name: Validate DueOn
|
||||
if: env.DUE_ON != ''
|
||||
run: |
|
||||
export TOOL_VERSION=0.1.1
|
||||
wget "https://repo.maven.apache.org/maven2/io/spring/releasetrain/spring-release-train-tools/$TOOL_VERSION/spring-release-train-tools-$TOOL_VERSION.jar"
|
||||
java -cp "spring-release-train-tools-$TOOL_VERSION.jar" io.spring.releasetrain.CheckMilestoneDueOnMain --dueOn "$DUE_ON" --expectedDayOfWeek MONDAY --expectedMondayCount 3
|
||||
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.3.0
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
|
||||
channel: '#spring-security-ci'
|
||||
name: 'CI Notifier'
|
||||
@@ -1,26 +0,0 @@
|
||||
name: PR Build
|
||||
|
||||
on: pull_request
|
||||
|
||||
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 }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository == 'spring-projects/spring-security' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up gradle
|
||||
uses: spring-io/spring-gradle-build-action@v2
|
||||
with:
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew clean build --continue --scan
|
||||
@@ -1,24 +0,0 @@
|
||||
name: Release Scheduler
|
||||
on:
|
||||
schedule:
|
||||
- cron: '15 15 * * MON' # Every Monday at 3:15pm UTC
|
||||
workflow_dispatch:
|
||||
permissions: read-all
|
||||
jobs:
|
||||
dispatch_scheduled_releases:
|
||||
name: Dispatch scheduled releases
|
||||
if: github.repository_owner == 'spring-projects'
|
||||
strategy:
|
||||
matrix:
|
||||
# List of active maintenance branches.
|
||||
branch: [ main, 6.0.x, 5.8.x, 5.7.x ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Dispatch
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
run: gh workflow run update-scheduled-release-version.yml -r ${{ matrix.branch }}
|
||||
@@ -1,22 +0,0 @@
|
||||
name: Trigger Dependabot Auto Merge Forward
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*.x'
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
trigger-worflow:
|
||||
name: Trigger Workflow
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.commits[0].author.username == 'dependabot[bot]' && github.repository == 'spring-projects/spring-security' }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
id: checkout
|
||||
uses: actions/checkout@v4
|
||||
- id: trigger
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
|
||||
run: gh workflow run dependabot-auto-merge-forward.yml -r main
|
||||
@@ -1,13 +0,0 @@
|
||||
name: Update Scheduled Release Version
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Manual trigger only. Triggered by release-scheduler.yml on main.
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update-scheduled-release-version:
|
||||
name: Update Scheduled Release Version
|
||||
uses: spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml@v1
|
||||
secrets: inherit
|
||||
@@ -28,3 +28,5 @@ s101plugin.state
|
||||
|
||||
!.idea/checkstyle-idea.xml
|
||||
!.idea/externalDependencies.xml
|
||||
|
||||
node_modules
|
||||
|
||||
+207
-220
@@ -1,54 +1,236 @@
|
||||
= 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.
|
||||
The release process for Spring Security is entirely automated via the https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc[Spring Security Release Plugin] and https://github.com/spring-io/spring-security-release-tools/tree/main/.github/workflows[reusable workflows].
|
||||
The following table outlines the steps that are taken by the automation.
|
||||
|
||||
[cols="1,1"]
|
||||
WARNING: The `5.8.x` branch does not have all of the improvements from the `6.x.x` branches. See "Status (5.8.x)" for which steps are still manual.
|
||||
|
||||
In case of a failure, you can follow the links below to read about each step, which includes instructions for performing the step manually if applicable.
|
||||
See <<frequently-asked-questions,FAQ>> for troubleshooting tips.
|
||||
|
||||
[cols="1,1,1"]
|
||||
|===
|
||||
| Step | Status
|
||||
| Step | Status (5.8.x) | Status (6.0.x+)
|
||||
|
||||
| <<update-dependencies>>
|
||||
| :x: manual
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<check-all-issues-are-closed>>
|
||||
| :white_check_mark: automated (scheduled release will abort if any issues are open)
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<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
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<update-to-next-development-version>>
|
||||
| <<push-release-commit>>
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<build-locally>>
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<update-release-notes-on-github>>
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<update-version-on-project-page>>
|
||||
| :x: manual
|
||||
|
||||
| <<update-release-notes-on-github>>
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<close-create-milestone,Close milestone>>
|
||||
| :x: manual (move issues to new milestone before release)
|
||||
| :x: manual
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<announce-release-on-slack>>
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<update-to-next-development-version>>
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<close-create-milestone,Create milestone>>
|
||||
| :white_check_mark: automated (if not already created)
|
||||
| :white_check_mark: automated
|
||||
| :white_check_mark: automated
|
||||
|
||||
| <<announce-release-on-other-channels>>
|
||||
| :x: manual
|
||||
| :x: manual
|
||||
|===
|
||||
|
||||
[#update-dependencies]
|
||||
== Update dependencies
|
||||
|
||||
Dependency versions are managed in the file xref:./gradle/libs.versions.toml[libs.versions.toml] and are automatically updated by xref:./.github/dependabot.yml[dependabot].
|
||||
|
||||
[#check-all-issues-are-closed]
|
||||
== Check all issues are closed
|
||||
|
||||
The first step of a release is to check if there are any open issues remaining in a milestone.
|
||||
|
||||
NOTE: A scheduled release will not proceed if there are any open issues.
|
||||
|
||||
TIP: If you need to prevent a release from occurring automatically, the easiest way to block a release is to add an unresolved issue to the milestone.
|
||||
|
||||
The https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc#checkMilestoneHasNoOpenIssues[`checkMilestoneHasOpenIssues`] command will check if there are any open issues for the release.
|
||||
Before running the command manually, replace the following values:
|
||||
|
||||
* `<next-version>` - Replace with the title of the milestone you are releasing now (i.e. 5.5.0-RC1)
|
||||
* `<github-personal-access-token>` - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `public_repo`. This is optional since you are unlikely to reach the rate limit for such a simple check.
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew checkMilestoneHasOpenIssues -PnextVersion=<next-version> -PgitHubAccessToken=<github-personal-access-token>
|
||||
----
|
||||
|
||||
Alternatively, you can manually check using the https://github.com/spring-projects/spring-security/milestones[milestones] page.
|
||||
|
||||
[#update-release-version]
|
||||
== Update release version
|
||||
|
||||
If all issues for the release are <<check-all-issues-are-closed,closed>>, the version number is automatically updated using the milestone title.
|
||||
When performing this step manually, update the version number in `gradle.properties` for the release (for example `5.5.0`) and commit the change using the message "Release x.y.z".
|
||||
|
||||
[#tag-release]
|
||||
== Tag release
|
||||
|
||||
The release will automatically be tagged using the milestone title.
|
||||
It is not required to tag manually.
|
||||
However, you can perform this step manually by running the following command:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
git tag 5.5.0
|
||||
----
|
||||
|
||||
[#push-release-commit]
|
||||
== Push release commit
|
||||
|
||||
During a scheduled release, the release commit will automatically be pushed to trigger a build.
|
||||
If performing this step manually, you can push the commit and tag and GitHub actions will build and deploy the artifacts with the following command:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
git push --atomic origin main 5.5.0
|
||||
----
|
||||
|
||||
The build will automatically wait for artifacts to be released to Maven Central.
|
||||
You can get notified manually when uploading is complete by running the following:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./scripts/release/wait-for-done.sh 5.5.0
|
||||
----
|
||||
|
||||
[#build-locally]
|
||||
== Build
|
||||
|
||||
All checks will automatically be performed by the build prior to uploading the artifacts to Maven Central.
|
||||
If something goes wrong, you can run the build locally using:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew check
|
||||
----
|
||||
|
||||
[#update-release-notes-on-github]
|
||||
== Update release notes on GitHub
|
||||
|
||||
Once the release has been uploaded to Maven Central, release notes will automatically be generated and a GitHub release will be created.
|
||||
To do this manually, you can use the https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc#generateChangelog[`generateChangelog`] command to generate the release notes by replacing:
|
||||
|
||||
* `<next-version>` - Replace with the milestone you are releasing now (i.e. 5.5.0)
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew generateChangelog -PnextVersion=<next-version>
|
||||
----
|
||||
|
||||
Then copy the release notes to your clipboard (your mileage may vary with the following command):
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
cat build/changelog/release-notes.md | xclip -selection clipboard
|
||||
----
|
||||
|
||||
Finally, create the
|
||||
https://github.com/spring-projects/spring-security/releases[release on
|
||||
GitHub], associate it with the tag, and paste the generated notes.
|
||||
|
||||
Alternatively, you can run the https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc#createGitHubRelease[`createGitHubRelease`] command to perform these steps automatically, replacing:
|
||||
|
||||
* `<next-version>` - Replace with the milestone you are releasing now (i.e. 5.5.0)
|
||||
* `<branch>` - The name of the branch to be tagged (if the release commit has not already been tagged)
|
||||
* `<github-personal-access-token>` - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `write:org`
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew createGitHubRelease -PnextVersion=<next-version> -Pbranch=<branch> -PcreateRelease=true -PgitHubAccessToken=<github-personal-access-token>
|
||||
----
|
||||
|
||||
[#update-version-on-project-page]
|
||||
== Update version on project page
|
||||
|
||||
The build will automatically update the project versions on https://spring.io/projects/spring-security#learn.
|
||||
To do this manually, you can use the https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc#createSaganRelease[`createSaganRelease`] and https://github.com/spring-io/spring-security-release-tools/blob/main/release-plugin/README.adoc#deleteSaganRelease[`deleteSaganRelease`] commands using the following parameters:
|
||||
|
||||
* `<next-version>` - Replace with the milestone you are releasing now (i.e. 5.5.0)
|
||||
* `<previous-version>` - Replace with the previous release which will be removed from the listed versions (i.e. 5.5.0-RC1)
|
||||
* `<github-personal-access-token>` - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `read:org` as https://spring.io/restdocs/index.html#authentication[documented for spring.io api]
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew createSaganRelease deleteSaganRelease -PnextVersion=<next-version> -PpreviousVersion=<previous-version> -PgitHubAccessToken=<github-personal-access-token>
|
||||
----
|
||||
|
||||
Alternatively, you can log into Contentful and update the versions manually on the Spring Security project page.
|
||||
|
||||
[#close-create-milestone]
|
||||
== Close / Create milestone
|
||||
|
||||
The release milestone will be automatically closed once the release is complete.
|
||||
To proceed manually, perform the following steps:
|
||||
|
||||
1. Visit https://github.com/spring-projects/spring-security/milestones[GitHub
|
||||
Milestones] and create a new milestone for the next release version
|
||||
2. Move any open issues from the existing milestone you just released to the new milestone
|
||||
3. Close the milestone for the release
|
||||
|
||||
NOTE: Remember that scheduled releases <<check-all-issues-are-closed,will not proceed>> if there are still open issues in the milestone.
|
||||
|
||||
[#announce-release-on-slack]
|
||||
== Announce release on Slack
|
||||
|
||||
The release will automatically be announced on Slack.
|
||||
If proceeding manually, announce the release on Slack in the channel https://pivotal.slack.com/messages/spring-release[#spring-release], including the keyword `+spring-security-announcing+` in the message.
|
||||
Something like:
|
||||
|
||||
....
|
||||
spring-security-announcing `5.5.0` is available now
|
||||
....
|
||||
|
||||
[#update-to-next-development-version]
|
||||
== Update to next development version
|
||||
|
||||
After the release is complete and artifacts have been uploaded to Maven Central, the build will automatically update to the next development version, commit and push.
|
||||
If proceeding manually, update the version in `gradle.properties` to the next `+SNAPSHOT+` version with the commit message "Next development version" and then push.
|
||||
|
||||
[#announce-release-on-other-channels]
|
||||
== Announce release on other channels
|
||||
|
||||
* Create a blog post on Contentful
|
||||
* Tweet from https://twitter.com/springsecurity[@SpringSecurity]
|
||||
|
||||
[[frequently-asked-questions]]
|
||||
== Frequently Asked Questions
|
||||
|
||||
*When should I update dependencies manually?* Dependencies should be updated at the latest the end of the week prior to the release. This is usually the Friday following the 2nd Monday of the month (counting from the first week with a Monday). When in doubt, check the https://github.com/spring-projects/spring-security/milestones[milestones] page for release due dates.
|
||||
|
||||
*When do scheduled releases occur?* Automated releases are scheduled to occur at *3:15 PM UTC* on the *3rd Monday of the month* (counting from the first week with a Monday).
|
||||
@@ -59,9 +241,10 @@ The scheduled release process currently runs every Monday but only releases when
|
||||
The automated release process occurs on the following branches:
|
||||
|
||||
* `main`
|
||||
* `6.0.x`
|
||||
* `6.2.x`
|
||||
* `6.1.x`
|
||||
* `6.0.x` (commercial only)
|
||||
* `5.8.x`
|
||||
* `5.7.x`
|
||||
|
||||
For each of the above branches, the automated process performs the following checks before proceeding with the release:
|
||||
|
||||
@@ -71,7 +254,7 @@ For each of the above branches, the automated process performs the following che
|
||||
[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.
|
||||
If the above checks pass, the version number is updated (in `gradle.properties`) and a commit is pushed to trigger the CI process.
|
||||
|
||||
*How do I trigger a release manually?* You can trigger a release manually in two ways:
|
||||
|
||||
@@ -81,199 +264,3 @@ If the above checks pass, the version number is updated (in `gradle.properties`
|
||||
*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
|
||||
|
||||
Ensure you have no changes in your local repository.
|
||||
Change to a new branch.
|
||||
For example:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ git checkout -b 5.5.0-RC1-dependencies
|
||||
----
|
||||
|
||||
Review the rules in build.gradle to ensure the rules make sense.
|
||||
For example, we should not allow major version updates in a patch release.
|
||||
Also ensure that all of the exclusions still make sense.
|
||||
|
||||
The following Gradle command will update your dependencies creating a commit for each dependency update.
|
||||
The first invocation of the command will take quite a while (~20 minutes depending on internet speed) to run because it is indexing all the versions of all the dependencies.
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ ./gradlew updateDependencies
|
||||
----
|
||||
|
||||
Review the commits to ensure that the updated dependency versions make sense for this release. For example, we should not perform a major version update for a patch release.
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ git log
|
||||
----
|
||||
|
||||
If any of the versions don’t make sense, update `build.gradle` to ensure that the version is excluded.
|
||||
|
||||
Run all the checks:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ ./gradlew check
|
||||
----
|
||||
|
||||
If they don’t work, you can run a git bisect to discover what broke the build.
|
||||
Fix any commits that broke the build.
|
||||
|
||||
Check out the original brach:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ git checkout -
|
||||
----
|
||||
|
||||
The following command will update the dependencies again but this time creating a ticket for each update and placing `Closes gh-<number>` in the commit. Replacing the following values:
|
||||
|
||||
* <github-personal-access-token> - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `public_repo`
|
||||
* <next-version> - Replace with the title of the milestone you are releasing now (i.e. 5.5.0-RC1)
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ ./gradlew updateDependencies -PupdateMode=GITHUB_ISSUE -PgitHubAccessToken=<github-personal-access-token> -PnextVersion=<next-version>
|
||||
----
|
||||
|
||||
Apply any fixes from your previous branch that were necessary.
|
||||
|
||||
[#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:
|
||||
|
||||
* <github-personal-access-token> - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `public_repo`. This is optional since you are unlikely to reach the rate limit for such a simple check.
|
||||
* <next-version> - Replace with the title of the milestone you are releasing now (i.e. 5.5.0-RC1)
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ ./gradlew gitHubCheckMilestoneHasNoOpenIssues -PgitHubAccessToken=<github-personal-access-token> -PnextVersion=<next-version>
|
||||
----
|
||||
|
||||
Alternatively, you can manually check using https://github.com/spring-projects/spring-security/milestones
|
||||
|
||||
[#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
|
||||
|
||||
You will need to update the antora.yml version.
|
||||
If you are unsure of what the values should be, the following task will instruct you what the expected values are:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
./gradlew :spring-security-docs:antoraCheckVersion
|
||||
----
|
||||
|
||||
[#build-locally]
|
||||
== Build locally
|
||||
|
||||
Run the build using
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ ./gradlew check
|
||||
----
|
||||
|
||||
[#push-release-commit]
|
||||
== Push 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:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ ./scripts/release/wait-for-done.sh 5.5.0
|
||||
----
|
||||
|
||||
[#announce-release-on-slack]
|
||||
== Announce release on Slack
|
||||
|
||||
* Announce via Slack on
|
||||
https://pivotal.slack.com/messages/spring-release[#spring-release],
|
||||
including the keyword `+spring-security-announcing+` in the message.
|
||||
Something like:
|
||||
|
||||
....
|
||||
spring-security-announcing 5.5.0 is available.
|
||||
....
|
||||
|
||||
[#tag-release]
|
||||
== Tag release
|
||||
|
||||
* Tag the release and then push the tag
|
||||
|
||||
....
|
||||
git tag 5.4.0-RC1
|
||||
git push origin 5.4.0-RC1
|
||||
....
|
||||
|
||||
[#update-to-next-development-version]
|
||||
== 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
|
||||
|
||||
The following command will update https://spring.io/projects/spring-security#learn with the new release version using the following parameters
|
||||
|
||||
<github-personal-access-token> - Replace with a https://github.com/settings/tokens[GitHub personal access token] that has a scope of `read:org` as https://spring.io/restdocs/index.html#authentication[documented for spring.io api]
|
||||
<next-version> - Replace with the milestone you are releasing now (i.e. 5.5.0-RC1)
|
||||
<previous-version> - Replace with the previous release which will be removed from the listed versions (i.e. 5.5.0-M3)
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ ./gradlew saganCreateRelease saganDeleteRelease -PgitHubAccessToken=<github-personal-access-token> -PnextVersion=<next-version> -PpreviousVersion=<previous-version>
|
||||
----
|
||||
|
||||
[#update-release-notes-on-github]
|
||||
== Update release notes on GitHub
|
||||
|
||||
Generate the Release Notes replacing:
|
||||
|
||||
* <next-version> - Replace with the milestone you are releasing now (i.e. 5.5.0-RC1)
|
||||
|
||||
----
|
||||
$ ./gradlew generateChangelog -PnextVersion=<next-version>
|
||||
----
|
||||
|
||||
* Copy the release notes to your clipboard (your mileage may vary with
|
||||
the following command)
|
||||
|
||||
....
|
||||
cat build/changelog/release-notes.md | xclip -selection clipboard
|
||||
....
|
||||
|
||||
* Create the
|
||||
https://github.com/spring-projects/spring-security/releases[release on
|
||||
GitHub], associate it with the tag, and paste the generated notes
|
||||
|
||||
[#close-create-milestone]
|
||||
== Close / Create milestone
|
||||
|
||||
* In
|
||||
https://github.com/spring-projects/spring-security/milestones[GitHub
|
||||
Milestones], create a new milestone for the next release version
|
||||
* Move any open issues from the existing milestone you just released to
|
||||
the new milestone
|
||||
* Close the milestone for the release.
|
||||
|
||||
[#announce-release-on-other-channels]
|
||||
== Announce release on other channels
|
||||
|
||||
* Create a https://spring.io/admin/blog[Blog]
|
||||
* Tweet from [@SpringSecurity](https://twitter.com/springsecurity)
|
||||
|
||||
|
||||
|
||||
+4
-4
@@ -37,7 +37,7 @@ springRelease {
|
||||
weekOfMonth = 3
|
||||
dayOfWeek = 1
|
||||
referenceDocUrl = "https://docs.spring.io/spring-security/reference/{version}/index.html"
|
||||
apiDocUrl = "https://docs.spring.io/spring-security/docs/{version}/api/"
|
||||
apiDocUrl = "https://docs.spring.io/spring-security/site/docs/{version}/api/"
|
||||
replaceSnapshotVersionInReferenceDocUrl = true
|
||||
}
|
||||
|
||||
@@ -86,10 +86,10 @@ allprojects {
|
||||
}
|
||||
}
|
||||
|
||||
if (hasProperty('buildScan')) {
|
||||
develocity {
|
||||
buildScan {
|
||||
termsOfServiceUrl = 'https://gradle.com/terms-of-service'
|
||||
termsOfServiceAgree = 'yes'
|
||||
termsOfUseUrl = 'https://gradle.com/help/legal-terms-of-use'
|
||||
termsOfUseAgree = 'yes'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-6
@@ -41,8 +41,6 @@ public class PasswordEncoderParser {
|
||||
|
||||
public static final String ATT_HASH = "hash";
|
||||
|
||||
static final String ATT_BASE_64 = "base64";
|
||||
|
||||
static final String OPT_HASH_BCRYPT = "bcrypt";
|
||||
|
||||
private static final Map<String, Class<?>> ENCODER_CLASSES = Collections.singletonMap(OPT_HASH_BCRYPT,
|
||||
@@ -62,19 +60,17 @@ public class PasswordEncoderParser {
|
||||
return;
|
||||
}
|
||||
String hash = element.getAttribute(ATT_HASH);
|
||||
boolean useBase64 = StringUtils.hasText(element.getAttribute(ATT_BASE_64))
|
||||
&& Boolean.parseBoolean(element.getAttribute(ATT_BASE_64));
|
||||
String ref = element.getAttribute(ATT_REF);
|
||||
if (StringUtils.hasText(ref)) {
|
||||
this.passwordEncoder = new RuntimeBeanReference(ref);
|
||||
}
|
||||
else {
|
||||
this.passwordEncoder = createPasswordEncoderBeanDefinition(hash, useBase64);
|
||||
this.passwordEncoder = createPasswordEncoderBeanDefinition(hash);
|
||||
((RootBeanDefinition) this.passwordEncoder).setSource(parserContext.extractSource(element));
|
||||
}
|
||||
}
|
||||
|
||||
public static BeanDefinition createPasswordEncoderBeanDefinition(String hash, boolean useBase64) {
|
||||
public static BeanDefinition createPasswordEncoderBeanDefinition(String hash) {
|
||||
Class<?> beanClass = ENCODER_CLASSES.get(hash);
|
||||
BeanDefinitionBuilder beanBldr = BeanDefinitionBuilder.rootBeanDefinition(beanClass);
|
||||
return beanBldr.getBeanDefinition();
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ public class LdapProviderBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
else if (StringUtils.hasText(hash)) {
|
||||
authenticatorBuilder.addPropertyValue("passwordEncoder",
|
||||
PasswordEncoderParser.createPasswordEncoderBeanDefinition(hash, false));
|
||||
PasswordEncoderParser.createPasswordEncoderBeanDefinition(hash));
|
||||
}
|
||||
}
|
||||
authenticatorBuilder.addConstructorArgValue(contextSource);
|
||||
|
||||
+14
@@ -20,10 +20,13 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.security.config.test.SpringTestContext;
|
||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
@@ -65,4 +68,15 @@ public class PasswordEncoderParserTests {
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCreatePasswordEncoderBeanDefinition() throws Exception {
|
||||
String hash = "bcrypt";
|
||||
Class<?> expectedBeanClass = BCryptPasswordEncoder.class;
|
||||
|
||||
BeanDefinition beanDefinition = PasswordEncoderParser.createPasswordEncoderBeanDefinition(hash);
|
||||
|
||||
Class<?> actualBeanClass = Class.forName(beanDefinition.getBeanClassName());
|
||||
assertThat(actualBeanClass).isEqualTo(expectedBeanClass);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -129,6 +129,10 @@ public class DelegatingPasswordEncoder implements PasswordEncoder {
|
||||
|
||||
private static final String DEFAULT_ID_SUFFIX = "}";
|
||||
|
||||
public static final String NO_PASSWORD_ENCODER_MAPPED = "There is no PasswordEncoder mapped for the id \"%s\"";
|
||||
|
||||
public static final String NO_PASSWORD_ENCODER_PREFIX = "You have entered a password with no PasswordEncoder. If that is your intent, it should be prefixed with `{noop}`.";
|
||||
|
||||
private final String idPrefix;
|
||||
|
||||
private final String idSuffix;
|
||||
@@ -286,7 +290,10 @@ public class DelegatingPasswordEncoder implements PasswordEncoder {
|
||||
@Override
|
||||
public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
|
||||
String id = extractId(prefixEncodedPassword);
|
||||
throw new IllegalArgumentException("There is no PasswordEncoder mapped for the id \"" + id + "\"");
|
||||
if (id != null && !id.isEmpty()) {
|
||||
throw new IllegalArgumentException(String.format(NO_PASSWORD_ENCODER_MAPPED, id));
|
||||
}
|
||||
throw new IllegalArgumentException(NO_PASSWORD_ENCODER_PREFIX);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+18
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -43,6 +43,8 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class DelegatingPasswordEncoderTests {
|
||||
|
||||
public static final String NO_PASSWORD_ENCODER = "You have entered a password with no PasswordEncoder. If that is your intent, it should be prefixed with `{noop}`.";
|
||||
|
||||
@Mock
|
||||
private PasswordEncoder bcrypt;
|
||||
|
||||
@@ -201,7 +203,7 @@ public class DelegatingPasswordEncoderTests {
|
||||
public void matchesWhenNoClosingPrefixStringThenIllegalArgumentException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "{bcrypt" + this.rawPassword))
|
||||
.withMessage("There is no PasswordEncoder mapped for the id \"null\"");
|
||||
.withMessage(NO_PASSWORD_ENCODER);
|
||||
verifyNoMoreInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@@ -209,7 +211,7 @@ public class DelegatingPasswordEncoderTests {
|
||||
public void matchesWhenNoStartingPrefixStringThenFalse() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "bcrypt}" + this.rawPassword))
|
||||
.withMessage("There is no PasswordEncoder mapped for the id \"null\"");
|
||||
.withMessage(NO_PASSWORD_ENCODER);
|
||||
verifyNoMoreInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@@ -217,7 +219,7 @@ public class DelegatingPasswordEncoderTests {
|
||||
public void matchesWhenNoIdStringThenFalse() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "{}" + this.rawPassword))
|
||||
.withMessage("There is no PasswordEncoder mapped for the id \"\"");
|
||||
.withMessage(NO_PASSWORD_ENCODER);
|
||||
verifyNoMoreInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@@ -226,7 +228,7 @@ public class DelegatingPasswordEncoderTests {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.passwordEncoder.matches(this.rawPassword, "invalid" + this.bcryptEncodedPassword))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.withMessage("There is no PasswordEncoder mapped for the id \"null\"");
|
||||
.withMessage(NO_PASSWORD_ENCODER);
|
||||
verifyNoMoreInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@@ -236,7 +238,7 @@ public class DelegatingPasswordEncoderTests {
|
||||
DelegatingPasswordEncoder passwordEncoder = new DelegatingPasswordEncoder(this.bcryptId, this.delegates);
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> passwordEncoder.matches(this.rawPassword, this.rawPassword))
|
||||
.withMessage("There is no PasswordEncoder mapped for the id \"null\"");
|
||||
.withMessage(NO_PASSWORD_ENCODER);
|
||||
verifyNoMoreInteractions(this.bcrypt, this.noop);
|
||||
}
|
||||
|
||||
@@ -289,4 +291,14 @@ public class DelegatingPasswordEncoderTests {
|
||||
verifyNoMoreInteractions(this.bcrypt);
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchesShouldThrowIllegalArgumentExceptionWhenNoPasswordEncoderIsMappedForTheId() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.passwordEncoder.matches("rawPassword", "prefixEncodedPassword"))
|
||||
.isInstanceOf(IllegalArgumentException.class)
|
||||
.withMessage(NO_PASSWORD_ENCODER);
|
||||
verifyNoMoreInteractions(this.bcrypt, this.noop);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
antora:
|
||||
extensions:
|
||||
- require: '@springio/antora-extensions'
|
||||
root_component_name: 'security'
|
||||
site:
|
||||
title: Spring Security
|
||||
url: https://docs.spring.io/spring-security/reference
|
||||
robots: allow
|
||||
git:
|
||||
ensure_git_suffix: false
|
||||
content:
|
||||
sources:
|
||||
- url: https://github.com/spring-projects/spring-security
|
||||
branches: [main, '5.{{6..9},{1..9}+({0..9})}.x', '6.+({0..9}).x']
|
||||
tags: ['5.{{6..9},{1..9}+({0..9})}.{0..99}?(-RC+({0..9}))', '6.+({0..9}).+({0..9})?(-{RC,M}*)','!(5.6.{0..10}*)', '!(5.7.{0..8}*)', '!(5.8.{0..3}?({-RC,-M}+({0..9})))','!(6.0.{0..3}*)','!(6.1.0*)']
|
||||
start_path: docs
|
||||
asciidoc:
|
||||
attributes:
|
||||
page-stackoverflow-url: https://stackoverflow.com/tags/spring-security
|
||||
page-related-doc-categories: security
|
||||
page-related-doc-projects: framework,graphql
|
||||
hide-uri-scheme: '@'
|
||||
tabs-sync-option: '@'
|
||||
extensions:
|
||||
- '@asciidoctor/tabs'
|
||||
- '@springio/asciidoctor-extensions'
|
||||
urls:
|
||||
latest_version_segment_strategy: redirect:to
|
||||
latest_version_segment: ''
|
||||
redirect_facility: httpd
|
||||
ui:
|
||||
bundle:
|
||||
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.15/ui-bundle.zip
|
||||
snapshot: true
|
||||
runtime:
|
||||
log:
|
||||
failure_level: warn
|
||||
@@ -105,6 +105,7 @@ Java::
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static GrantedAuthorityDefaults grantedAuthorityDefaults() {
|
||||
return new GrantedAuthorityDefaults("MYPREFIX_");
|
||||
}
|
||||
@@ -113,7 +114,8 @@ static GrantedAuthorityDefaults grantedAuthorityDefaults() {
|
||||
|
||||
[TIP]
|
||||
====
|
||||
We expose `GrantedAuthorityDefaults` using a `static` method to ensure that Spring publishes it before it initializes Spring Security's method security `@Configuration` classes
|
||||
We expose `GrantedAuthorityDefaults` using a `static` method to ensure that Spring publishes it before it initializes Spring Security's method security `@Configuration` classes.
|
||||
Since the `GrantedAuthorityDefaults` bean is part of internal workings of Spring Security, we should also expose it as an infrastructural bean effectively avoiding some warnings related to bean post-processing (see https://github.com/spring-projects/spring-security/issues/14751[gh-14751]).
|
||||
====
|
||||
|
||||
[[jc-reactive-method-security-custom-authorization-manager]]
|
||||
|
||||
@@ -449,7 +449,7 @@ In Spring Security this is done by saving the `HttpServletRequest` using a <<req
|
||||
|
||||
The `HttpServletRequest` is saved in the {security-api-url}org/springframework/security/web/savedrequest/RequestCache.html[`RequestCache`].
|
||||
When the user successfully authenticates, the `RequestCache` is used to replay the original request.
|
||||
The <<requestcacheawarefilter,`RequestCacheAwareFilter`>> is what uses the `RequestCache` to save the `HttpServletRequest`.
|
||||
The <<requestcacheawarefilter,`RequestCacheAwareFilter`>> uses the `RequestCache` to get the saved `HttpServletRequest` after the user authenticates, while the `ExceptionTranslationFilter` uses the `RequestCache` to save the `HttpServletRequest` after it detects `AuthenticationException`, before redirecting the user to the login endpoint.
|
||||
|
||||
By default, an `HttpSessionRequestCache` is used.
|
||||
The code below demonstrates how to customize the `RequestCache` implementation that is used to check the `HttpSession` for a saved request if the parameter named `continue` is present.
|
||||
@@ -517,7 +517,7 @@ XML::
|
||||
[[requestcacheawarefilter]]
|
||||
=== RequestCacheAwareFilter
|
||||
|
||||
The {security-api-url}org/springframework/security/web/savedrequest/RequestCacheAwareFilter.html[`RequestCacheAwareFilter`] uses the <<requestcache,`RequestCache`>> to save the `HttpServletRequest`.
|
||||
The {security-api-url}org/springframework/security/web/savedrequest/RequestCacheAwareFilter.html[`RequestCacheAwareFilter`] uses the <<requestcache,`RequestCache`>> to replay the original request.
|
||||
|
||||
[[servlet-logging]]
|
||||
== Logging
|
||||
|
||||
@@ -131,7 +131,7 @@ public class DefaultSecurityConfig {
|
||||
InMemoryUserDetailsManager inMemoryUserDetailsManager() { <2>
|
||||
String generatedPassword = // ...;
|
||||
return new InMemoryUserDetailsManager(User.withUsername("user")
|
||||
.password(generatedPassword).roles("ROLE_USER").build());
|
||||
.password(generatedPassword).roles("USER").build());
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"antora": "3.2.0-alpha.4",
|
||||
"@antora/atlas-extension": "1.0.0-alpha.2",
|
||||
"@antora/collector-extension": "1.0.0-alpha.3",
|
||||
"@asciidoctor/tabs": "1.0.0-beta.6",
|
||||
"@springio/antora-extensions": "1.10.0",
|
||||
"@springio/asciidoctor-extensions": "1.0.0-alpha.10"
|
||||
}
|
||||
}
|
||||
@@ -8,14 +8,11 @@ apply plugin: 'io.spring.convention.docs'
|
||||
apply plugin: 'java'
|
||||
|
||||
antora {
|
||||
playbook = 'cached-antora-playbook.yml'
|
||||
playbookProvider {
|
||||
repository = 'spring-projects/spring-security'
|
||||
branch = 'docs-build'
|
||||
path = 'lib/antora/templates/per-branch-antora-playbook.yml'
|
||||
checkLocalBranch = true
|
||||
}
|
||||
options = [clean: true, fetch: !project.gradle.startParameter.offline, stacktrace: true]
|
||||
environment = [
|
||||
'BUILD_REFNAME': 'HEAD',
|
||||
'BUILD_VERSION': project.version,
|
||||
]
|
||||
}
|
||||
|
||||
tasks.named("generateAntoraYml") {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
springBootVersion=3.1.1
|
||||
version=6.1.9-SNAPSHOT
|
||||
version=6.1.10-SNAPSHOT
|
||||
samplesBranch=6.1.x
|
||||
org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError
|
||||
org.gradle.parallel=true
|
||||
|
||||
@@ -6,36 +6,36 @@ io-spring-nohttp = "0.0.11"
|
||||
jakarta-websocket = "2.1.1"
|
||||
org-apache-directory-server = "1.5.5"
|
||||
org-apache-maven-resolver = "1.8.2"
|
||||
org-aspectj = "1.9.22"
|
||||
org-aspectj = "1.9.22.1"
|
||||
org-bouncycastle = "1.70"
|
||||
org-eclipse-jetty = "11.0.20"
|
||||
org-eclipse-jetty = "11.0.21"
|
||||
org-jetbrains-kotlin = "1.8.22"
|
||||
org-jetbrains-kotlinx = "1.6.4"
|
||||
org-mockito = "4.8.1"
|
||||
org-opensaml = "4.1.1"
|
||||
org-springframework = "6.0.19"
|
||||
org-springframework = "6.0.20"
|
||||
|
||||
[libraries]
|
||||
ch-qos-logback-logback-classic = "ch.qos.logback:logback-classic:1.4.14"
|
||||
com-fasterxml-jackson-jackson-bom = "com.fasterxml.jackson:jackson-bom:2.14.3"
|
||||
com-google-inject-guice = "com.google.inject:guice:3.0"
|
||||
com-netflix-nebula-nebula-project-plugin = "com.netflix.nebula:nebula-project-plugin:8.2.0"
|
||||
com-nimbusds-nimbus-jose-jwt = "com.nimbusds:nimbus-jose-jwt:9.24.4"
|
||||
com-nimbusds-oauth2-oidc-sdk = "com.nimbusds:oauth2-oidc-sdk:9.43.3"
|
||||
com-nimbusds-nimbus-jose-jwt = "com.nimbusds:nimbus-jose-jwt:9.37.3"
|
||||
com-nimbusds-oauth2-oidc-sdk = "com.nimbusds:oauth2-oidc-sdk:9.43.4"
|
||||
com-squareup-okhttp3-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "com-squareup-okhttp3" }
|
||||
com-squareup-okhttp3-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "com-squareup-okhttp3" }
|
||||
com-unboundid-unboundid-ldapsdk = "com.unboundid:unboundid-ldapsdk:6.0.11"
|
||||
commons-collections = "commons-collections:commons-collections:3.2.2"
|
||||
io-freefair-gradle-aspectj-plugin = "io.freefair.gradle:aspectj-plugin:6.6.3"
|
||||
io-micrometer-micrometer-observation = "io.micrometer:micrometer-observation:1.10.13"
|
||||
io-mockk = "io.mockk:mockk:1.13.10"
|
||||
io-projectreactor-reactor-bom = "io.projectreactor:reactor-bom:2022.0.18"
|
||||
io-mockk = "io.mockk:mockk:1.13.11"
|
||||
io-projectreactor-reactor-bom = "io.projectreactor:reactor-bom:2022.0.19"
|
||||
io-rsocket-rsocket-bom = { module = "io.rsocket:rsocket-bom", version.ref = "io-rsocket" }
|
||||
io-spring-javaformat-spring-javaformat-checkstyle = { module = "io.spring.javaformat:spring-javaformat-checkstyle", version.ref = "io-spring-javaformat" }
|
||||
io-spring-javaformat-spring-javaformat-gradle-plugin = { module = "io.spring.javaformat:spring-javaformat-gradle-plugin", version.ref = "io-spring-javaformat" }
|
||||
io-spring-nohttp-nohttp-checkstyle = { module = "io.spring.nohttp:nohttp-checkstyle", version.ref = "io-spring-nohttp" }
|
||||
io-spring-nohttp-nohttp-gradle = { module = "io.spring.nohttp:nohttp-gradle", version.ref = "io-spring-nohttp" }
|
||||
io-spring-security-release-plugin = "io.spring.gradle:spring-security-release-plugin:1.0.2"
|
||||
io-spring-security-release-plugin = "io.spring.gradle:spring-security-release-plugin:1.0.3"
|
||||
jakarta-annotation-jakarta-annotation-api = "jakarta.annotation:jakarta.annotation-api:2.1.1"
|
||||
jakarta-inject-jakarta-inject-api = "jakarta.inject:jakarta.inject-api:2.0.1"
|
||||
jakarta-persistence-jakarta-persistence-api = "jakarta.persistence:jakarta.persistence-api:3.1.0"
|
||||
@@ -97,7 +97,7 @@ org-apache-commons-commons-io = "org.apache.commons:commons-io:1.3.2"
|
||||
io-github-gradle-nexus-publish-plugin = "io.github.gradle-nexus:publish-plugin:1.1.0"
|
||||
org-gretty-gretty = "org.gretty:gretty:4.0.3"
|
||||
com-github-ben-manes-gradle-versions-plugin = "com.github.ben-manes:gradle-versions-plugin:0.38.0"
|
||||
com-github-spullara-mustache-java-compiler = "com.github.spullara.mustache.java:compiler:0.9.11"
|
||||
com-github-spullara-mustache-java-compiler = "com.github.spullara.mustache.java:compiler:0.9.13"
|
||||
org-hidetake-gradle-ssh-plugin = "org.hidetake:gradle-ssh-plugin:2.10.1"
|
||||
org-jfrog-buildinfo-build-info-extractor-gradle = "org.jfrog.buildinfo:build-info-extractor-gradle:4.29.4"
|
||||
org-sonarsource-scanner-gradle-sonarqube-gradle-plugin = "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.7.1"
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -54,6 +54,7 @@ import org.opensaml.saml.saml2.assertion.SubjectConfirmationValidator;
|
||||
import org.opensaml.saml.saml2.assertion.impl.AudienceRestrictionConditionValidator;
|
||||
import org.opensaml.saml.saml2.assertion.impl.BearerSubjectConfirmationValidator;
|
||||
import org.opensaml.saml.saml2.assertion.impl.DelegationRestrictionConditionValidator;
|
||||
import org.opensaml.saml.saml2.assertion.impl.ProxyRestrictionConditionValidator;
|
||||
import org.opensaml.saml.saml2.core.Assertion;
|
||||
import org.opensaml.saml.saml2.core.Attribute;
|
||||
import org.opensaml.saml.saml2.core.AttributeStatement;
|
||||
@@ -804,6 +805,7 @@ public final class OpenSaml4AuthenticationProvider implements AuthenticationProv
|
||||
return ValidationResult.VALID;
|
||||
}
|
||||
});
|
||||
conditions.add(new ProxyRestrictionConditionValidator());
|
||||
subjects.add(new BearerSubjectConfirmationValidator() {
|
||||
@Override
|
||||
protected ValidationResult validateAddress(SubjectConfirmation confirmation, Assertion assertion,
|
||||
|
||||
+16
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -53,6 +53,7 @@ import org.opensaml.saml.saml2.core.EncryptedAttribute;
|
||||
import org.opensaml.saml.saml2.core.EncryptedID;
|
||||
import org.opensaml.saml.saml2.core.NameID;
|
||||
import org.opensaml.saml.saml2.core.OneTimeUse;
|
||||
import org.opensaml.saml.saml2.core.ProxyRestriction;
|
||||
import org.opensaml.saml.saml2.core.Response;
|
||||
import org.opensaml.saml.saml2.core.StatusCode;
|
||||
import org.opensaml.saml.saml2.core.SubjectConfirmation;
|
||||
@@ -61,6 +62,7 @@ import org.opensaml.saml.saml2.core.impl.AttributeBuilder;
|
||||
import org.opensaml.saml.saml2.core.impl.EncryptedAssertionBuilder;
|
||||
import org.opensaml.saml.saml2.core.impl.EncryptedIDBuilder;
|
||||
import org.opensaml.saml.saml2.core.impl.NameIDBuilder;
|
||||
import org.opensaml.saml.saml2.core.impl.ProxyRestrictionBuilder;
|
||||
import org.opensaml.xmlsec.encryption.impl.EncryptedDataBuilder;
|
||||
import org.opensaml.xmlsec.signature.support.SignatureConstants;
|
||||
import org.w3c.dom.Element;
|
||||
@@ -741,6 +743,19 @@ public class OpenSaml4AuthenticationProviderTests {
|
||||
.withMessageContaining("did not match any valid issuers");
|
||||
}
|
||||
|
||||
// gh-14931
|
||||
@Test
|
||||
public void authenticateWhenAssertionHasProxyRestrictionThenParses() {
|
||||
OpenSaml4AuthenticationProvider provider = new OpenSaml4AuthenticationProvider();
|
||||
Response response = response();
|
||||
Assertion assertion = assertion();
|
||||
ProxyRestriction condition = new ProxyRestrictionBuilder().buildObject();
|
||||
assertion.getConditions().getConditions().add(condition);
|
||||
response.getAssertions().add(assertion);
|
||||
Saml2AuthenticationToken token = token(signed(response), verifying(registration()));
|
||||
provider.authenticate(token);
|
||||
}
|
||||
|
||||
private <T extends XMLObject> T build(QName qName) {
|
||||
return (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName);
|
||||
}
|
||||
|
||||
@@ -11,9 +11,11 @@ changelog:
|
||||
labels: ["type: bug"]
|
||||
sort: "title"
|
||||
- title: ":hammer: Dependency Upgrades"
|
||||
emoji: ":hammer:"
|
||||
labels: ["type: dependency-upgrade"]
|
||||
sort: "title"
|
||||
- title: ":nut_and_bolt: Build Updates"
|
||||
labels: ["type: task", "in: build"]
|
||||
sort: "title"
|
||||
issues:
|
||||
exclude:
|
||||
labels: ["status: duplicate"]
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@ pluginManagement {
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "com.gradle.enterprise" version "3.12.6"
|
||||
id "io.spring.ge.conventions" version "0.0.16"
|
||||
id "com.gradle.develocity" version "3.17.4"
|
||||
id "io.spring.ge.conventions" version "0.0.17"
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
|
||||
+8
-2
@@ -29,6 +29,7 @@ import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.util.UrlUtils;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher.MatchResult;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcherEntry;
|
||||
@@ -68,7 +69,7 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho
|
||||
@Override
|
||||
public AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest request) {
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace(LogMessage.format("Authorizing %s", request));
|
||||
this.logger.trace(LogMessage.format("Authorizing %s", requestLine(request)));
|
||||
}
|
||||
for (RequestMatcherEntry<AuthorizationManager<RequestAuthorizationContext>> mapping : this.mappings) {
|
||||
|
||||
@@ -77,7 +78,8 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho
|
||||
if (matchResult.isMatch()) {
|
||||
AuthorizationManager<RequestAuthorizationContext> manager = mapping.getEntry();
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace(LogMessage.format("Checking authorization on %s using %s", request, manager));
|
||||
this.logger.trace(
|
||||
LogMessage.format("Checking authorization on %s using %s", requestLine(request), manager));
|
||||
}
|
||||
return manager.check(authentication,
|
||||
new RequestAuthorizationContext(request, matchResult.getVariables()));
|
||||
@@ -89,6 +91,10 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho
|
||||
return DENY;
|
||||
}
|
||||
|
||||
private static String requestLine(HttpServletRequest request) {
|
||||
return request.getMethod() + " " + UrlUtils.buildRequestUrl(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a builder for {@link RequestMatcherDelegatingAuthorizationManager}.
|
||||
* @return the new {@link Builder} instance
|
||||
|
||||
Reference in New Issue
Block a user