1
0
mirror of synced 2026-07-11 05:40:02 +00:00

Compare commits

..

1 Commits

Author SHA1 Message Date
Josh Cummings e3e7d76b70 Add Workflow to Finalize a Release 2025-11-04 09:32:41 -07:00
356 changed files with 1949 additions and 8060 deletions
+8
View File
@@ -111,3 +111,11 @@ updates:
labels:
- 'type: task'
- 'in: build'
- package-ecosystem: npm
target-branch: 6.3.x
directory: /docs
schedule:
interval: weekly
labels:
- 'type: task'
- 'in: build'
+5 -3
View File
@@ -14,15 +14,17 @@ permissions:
jobs:
snapshot-test:
name: Test Against Snapshots
uses: spring-io/spring-security-release-tools/.github/workflows/test.yml@ed473b4dafba053c63a453d2d88a89df3b3e18b3 # v1
uses: spring-io/spring-security-release-tools/.github/workflows/test.yml@v1
strategy:
matrix:
include:
- java-version: 21-ea
toolchain: 21
- java-version: 17
toolchain: 17
with:
java-version: ${{ matrix.java-version }}
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot,https://oss.sonatype.org/content/repositories/snapshots -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=7.0.+ -PreactorVersion=2025.0.+ -PspringDataVersion=2025.1.+ -PmicrometerVersion=1.16.+ --stacktrace
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot,https://oss.sonatype.org/content/repositories/snapshots -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=7.+ -PreactorVersion=2025.+ -PspringDataVersion=2025.+ --stacktrace
secrets: inherit
send-notification:
name: Send Notification
@@ -31,6 +33,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@ed473b4dafba053c63a453d2d88a89df3b3e18b3 # v1
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
+1 -1
View File
@@ -14,4 +14,4 @@ jobs:
actions: read
contents: read
security-events: write
uses: spring-io/github-actions/.github/workflows/codeql-analysis.yml@e415dadd0910c901e7a7fabd67bbb355b2324500 # 1
uses: spring-io/github-actions/.github/workflows/codeql-analysis.yml@1
@@ -17,7 +17,7 @@ permissions:
jobs:
build:
name: Build
uses: spring-io/spring-security-release-tools/.github/workflows/build.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
uses: spring-io/spring-security-release-tools/.github/workflows/build.yml@v1
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
@@ -29,23 +29,30 @@ jobs:
secrets: inherit
deploy-artifacts:
name: Deploy Artifacts
needs: [ build ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
needs: [ build]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@v1
with:
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
default-publish-milestones-central: true
secrets: inherit
deploy-docs:
name: Deploy Docs
needs: [ build ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-docs.yml@v1
with:
should-deploy-docs: ${{ needs.build.outputs.should-deploy-artifacts }}
secrets: inherit
deploy-schema:
name: Deploy Schema
needs: [ build ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
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-schema ]
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
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 }}
@@ -61,6 +68,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
-76
View File
@@ -1,76 +0,0 @@
name: Defer Issues
on:
workflow_dispatch:
permissions:
contents: read
jobs:
defer-issues:
name: Defer Issues
runs-on: ubuntu-latest
if: github.repository_owner == 'spring-projects'
permissions:
issues: write
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Compute Version
id: compute-version
uses: spring-io/spring-release-actions/compute-version@a1f321783a0769dd2aea4fad6c2ae2f95a52b885 # 0.0.5
- name: Get Today's Release Version
id: todays-release
uses: spring-io/spring-release-actions/get-todays-release-version@a1f321783a0769dd2aea4fad6c2ae2f95a52b885 # 0.0.5
with:
snapshot-version: ${{ steps.compute-version.outputs.version }}
milestone-repository: ${{ github.repository }}
milestone-token: ${{ secrets.GITHUB_TOKEN }}
- name: Compute Next Version
id: next-version
uses: spring-io/spring-release-actions/compute-next-version@a1f321783a0769dd2aea4fad6c2ae2f95a52b885 # 0.0.5
with:
version: ${{ steps.todays-release.outputs.release-version }}
- name: Schedule Next Milestone
uses: spring-io/spring-release-actions/schedule-milestone@a1f321783a0769dd2aea4fad6c2ae2f95a52b885 # 0.0.5
with:
version: ${{ steps.next-version.outputs.version }}
version-date: ${{ steps.next-version.outputs.version-date }}
repository: ${{ github.repository }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: Move Open Issues to Next Milestone
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CURRENT_MILESTONE: ${{ steps.todays-release.outputs.release-version }}
NEXT_MILESTONE: ${{ steps.next-version.outputs.version }}
run: |
current_milestone_number=$(gh api repos/${{ github.repository }}/milestones \
--jq ".[] | select(.title == \"$CURRENT_MILESTONE\") | .number")
if [ -z "$current_milestone_number" ]; then
echo "No milestone found for $CURRENT_MILESTONE"
exit 0
fi
next_milestone_number=$(gh api repos/${{ github.repository }}/milestones \
--jq ".[] | select(.title == \"$NEXT_MILESTONE\") | .number")
if [ -z "$next_milestone_number" ]; then
echo "No milestone found for $NEXT_MILESTONE"
exit 1
fi
echo "Moving open issues from milestone '$CURRENT_MILESTONE' (#$current_milestone_number) to '$NEXT_MILESTONE' (#$next_milestone_number)"
page=1
while true; do
issues=$(gh api "repos/${{ github.repository }}/issues?milestone=$current_milestone_number&state=open&per_page=100&page=$page" \
--jq '.[].number')
if [ -z "$issues" ]; then
break
fi
for issue in $issues; do
echo "Moving issue/PR #$issue to milestone $NEXT_MILESTONE"
gh api repos/${{ github.repository }}/issues/$issue \
--method PATCH \
--field milestone=$next_milestone_number \
--silent
done
page=$((page + 1))
done
echo "Done."
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
if: github.repository_owner == 'spring-projects'
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@v4
with:
ref: docs-build
fetch-depth: 1
+20 -6
View File
@@ -2,10 +2,6 @@ name: Finalize Release
on:
workflow_dispatch: # Manual trigger
inputs:
version:
description: The Spring Security release to finalize (e.g. 7.0.0-RC2)
required: true
env:
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
@@ -14,14 +10,32 @@ permissions:
contents: read
jobs:
project-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.project-version.outputs.version }}
steps:
- id: project-version
run: echo "version=$(grep '^version=' gradle.properties | cut -d'=' -f2)" >> $GITHUB_OUTPUT
perform-release:
name: Perform Release
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@ed473b4dafba053c63a453d2d88a89df3b3e18b3 # v1
needs: [ project-version ]
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@v1
with:
should-perform-release: true
project-version: ${{ inputs.version }}
project-version: ${{ needs.project-version.outputs.version }}
milestone-repo-url: https://repo1.maven.org/maven2
release-repo-url: https://repo1.maven.org/maven2
artifact-path: org/springframework/security/spring-security-core
slack-announcing-id: spring-security-announcing
secrets: inherit
send-notification:
name: Send Notification
needs: [ perform-release ]
if: ${{ !success() }}
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
@@ -19,14 +19,14 @@ jobs:
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@0f481fcb613427c0f801b606911222b5b6f3083a # v5.5.0
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
uses: gradle/gradle-build-action@v2
- name: Upgrade Wrappers
run: ./gradlew clean upgradeGradleWrapperAll --continue -Porg.gradle.java.installations.auto-download=false
env:
@@ -30,6 +30,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
+6 -6
View File
@@ -11,9 +11,9 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.repository == 'spring-projects/spring-security' }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@v4
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@c8668747d7c264864c8c7f7026d0d277d14a78dc # v2.0.6
uses: spring-io/spring-gradle-build-action@v2
with:
java-version: '17'
distribution: 'temurin'
@@ -24,9 +24,9 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.repository == 'spring-projects/spring-security' }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- uses: actions/checkout@v4
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@c8668747d7c264864c8c7f7026d0d277d14a78dc # v2.0.6
uses: spring-io/spring-gradle-build-action@v2
with:
java-version: '17'
distribution: 'temurin'
@@ -34,7 +34,7 @@ jobs:
run: ./gradlew -PbuildSrc.skipTests=true :spring-security-docs:antora
- name: Upload Docs
id: upload
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v4
with:
name: docs
path: docs/build/site
@@ -46,6 +46,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Dispatch
@@ -16,9 +16,9 @@ jobs:
name: Update on Supported Branches
strategy:
matrix:
branch: [ '6.4.x', '6.5.x', 'main' ]
branch: [ '5.8.x', '6.2.x', '6.3.x', 'main' ]
steps:
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf # v0.0.22
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@e28269199d1d27975cf7f65e16d6095c555b3cd0
name: Update
with:
docs-branch: ${{ matrix.branch }}
@@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
name: Update on docs-build
steps:
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf # v0.0.22
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@e28269199d1d27975cf7f65e16d6095c555b3cd0
name: Update
with:
docs-branch: 'docs-build'
@@ -9,7 +9,7 @@ permissions:
jobs:
update-scheduled-release-version:
name: Update Scheduled Release Version
uses: spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
uses: spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml@v1
secrets: inherit
send-notification:
name: Send Notification
@@ -18,6 +18,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
-21
View File
@@ -68,27 +68,6 @@ The https://github.com/spring-projects/spring-security/tree/docs-build[playbook
Discover more commands with `./gradlew tasks`.
=== IDE setup (IntelliJ)
No special steps are needed to open Spring Security in IntelliJ.
=== IDE setup (Eclipse and VS Code)
To work in Eclipse or VS Code, first generate Eclipse metadata so you can import the project into Eclipse or VS Code:
[indent=0]
----
./gradlew cleanEclipse eclipse
----
If you have not built the project yet, run `./gradlew publishToMavenLocal` first so dependencies are resolved.
*VS Code:* Open the repository root as a folder. The repository includes `.vscode/settings.json` which disables automatic Gradle import so that the generated Eclipse metadata (`.classpath`, `.project`) is used. Do not use the Gradle for Java extension to import the project.
*Eclipse:* File → Import → General → Existing Projects into Workspace, then select the repository root.
The build uses a custom Eclipse plugin to work around Gradle dependency cycles that confuse IDE metadata generation. You may see Eclipse warnings about `xml-apis` from some test dependencies; those are excluded in the build and can be ignored.
== 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.
@@ -39,7 +39,7 @@ interface EvaluationContextPostProcessor<I> {
* that was passed in.
* @param context the original {@link EvaluationContext}
* @param invocation the security invocation object (i.e. Message)
* @return the updated context.
* @return the upated context.
*/
EvaluationContext postProcess(EvaluationContext context, I invocation);
@@ -35,7 +35,6 @@ import org.springframework.security.web.FilterInvocation;
*/
@Deprecated
@NullUnmarked
@SuppressWarnings("serial")
class WebExpressionConfigAttribute implements ConfigAttribute, EvaluationContextPostProcessor<FilterInvocation> {
private final Expression authorizeExpression;
@@ -20,7 +20,7 @@ import org.springframework.security.acls.model.Acl;
/**
* Strategy used by {@link AclImpl} to determine whether a principal is permitted to call
* administrative methods on the <code>AclImpl</code>.
* adminstrative methods on the <code>AclImpl</code>.
*
* @author Ben Alex
*/
@@ -42,7 +42,7 @@ public class GrantedAuthoritySid implements Sid {
public GrantedAuthoritySid(GrantedAuthority grantedAuthority) {
Assert.notNull(grantedAuthority, "GrantedAuthority required");
Assert.notNull(grantedAuthority.getAuthority(),
"This Sid is only compatible with GrantedAuthority that provide a non-null getAuthority()");
"This Sid is only compatible with GrantedAuthoritys that provide a non-null getAuthority()");
this.grantedAuthority = grantedAuthority.getAuthority();
}
@@ -160,7 +160,7 @@ public class JdbcAclService implements AclService {
this.findChildrenSql = DEFAULT_SELECT_ACL_WITH_PARENT_SQL_WITH_CLASS_ID_TYPE;
}
else {
log.debug("Find children statement has already been overridden, so not overriding the default");
log.debug("Find children statement has already been overridden, so not overridding the default");
}
}
}
@@ -50,7 +50,7 @@ import org.springframework.util.Assert;
* The default settings are for HSQLDB. If you are using a different database you will
* probably need to set the {@link #setSidIdentityQuery(String) sidIdentityQuery} and
* {@link #setClassIdentityQuery(String) classIdentityQuery} properties appropriately. The
* other queries, SQL inserts and updates can also be customized to accommodate schema
* other queries, SQL inserts and updates can also be customized to accomodate schema
* variations, but must produce results consistent with those expected by the defaults.
* <p>
* See the appendix of the Spring Security reference manual for more information on the
@@ -471,7 +471,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
this.insertClass = DEFAULT_INSERT_INTO_ACL_CLASS_WITH_ID;
}
else {
log.debug("Insert class statement has already been overridden, so not overriding the default");
log.debug("Insert class statement has already been overridden, so not overridding the default");
}
}
}
+4 -4
View File
@@ -2,12 +2,12 @@ apply plugin: 'io.spring.convention.spring-module'
apply plugin: 'io.freefair.aspectj'
compileAspectj {
sourceCompatibility = "17"
targetCompatibility = "17"
sourceCompatibility "17"
targetCompatibility "17"
}
compileTestAspectj {
sourceCompatibility = "17"
targetCompatibility = "17"
sourceCompatibility "17"
targetCompatibility "17"
}
dependencies {
+37 -10
View File
@@ -10,7 +10,7 @@ buildscript {
classpath libs.com.netflix.nebula.nebula.project.plugin
}
repositories {
maven { url='https://plugins.gradle.org/m2/' }
maven { url 'https://plugins.gradle.org/m2/' }
}
}
@@ -35,30 +35,57 @@ ext.milestoneBuild = !(snapshotBuild || releaseBuild)
repositories {
mavenCentral()
maven { url = "https://repo.spring.io/milestone" }
maven { url "https://repo.spring.io/milestone" }
}
springRelease {
weekOfMonth = 3
dayOfWeek = 1
referenceDocUrl = "https://docs.spring.io/spring-security/reference/{version}/index.html"
apiDocUrl = "https://docs.spring.io/spring-security/reference/{version}/api/java/index.html"
apiDocUrl = "https://docs.spring.io/spring-security/site/docs/{version}/api/"
replaceSnapshotVersionInReferenceDocUrl = true
}
def toolchainVersion() {
if (project.hasProperty('testToolchain')) {
return project.property('testToolchain').toString().toInteger()
}
return 17
}
subprojects {
java {
toolchain {
languageVersion = JavaLanguageVersion.of(toolchainVersion())
}
}
kotlin {
jvmToolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
tasks.withType(JavaCompile).configureEach {
options.encoding = "UTF-8"
options.compilerArgs.add("-parameters")
options.release.set(17)
}
}
allprojects {
if (!['spring-security-bom', 'spring-security-docs'].contains(project.name)) {
apply plugin: 'io.spring.javaformat'
apply plugin: 'checkstyle'
pluginManager.withPlugin("io.spring.convention.checkstyle") {
dependencies {
checkstyle libs.io.spring.javaformat.spring.javaformat.checkstyle
pluginManager.withPlugin("io.spring.convention.checkstyle", { plugin ->
configure(plugin) {
dependencies {
checkstyle libs.io.spring.javaformat.spring.javaformat.checkstyle
}
checkstyle {
toolVersion = '8.34'
}
}
checkstyle {
toolVersion = '8.34'
}
}
})
if (project.name.contains('sample')) {
tasks.whenTaskAdded { task ->
+1 -1
View File
@@ -12,7 +12,7 @@ java {
repositories {
gradlePluginPortal()
mavenCentral()
maven { url = 'https://repo.spring.io/snapshot' }
maven { url 'https://repo.spring.io/snapshot' }
}
sourceSets {
Binary file not shown.
+5
View File
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored Executable
+240
View File
@@ -0,0 +1,240 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original 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.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"
+91
View File
@@ -0,0 +1,91 @@
@rem
@rem Copyright 2004-present the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@@ -81,8 +81,8 @@ class ArtifactoryPlugin implements Plugin<Project> {
repository {
repoKey = isSnapshot ? snapshotRepository : isMilestone ? milestoneRepository : releaseRepository
if(project.hasProperty('artifactoryUsername')) {
username = project.artifactoryUsername
password = project.artifactoryPassword
username = artifactoryUsername
password = artifactoryPassword
}
}
}
@@ -0,0 +1,82 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package io.spring.gradle.convention
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.tasks.bundling.Zip
import org.gradle.api.Plugin
import org.gradle.api.Project
public class DeployDocsPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getPluginManager().apply('org.hidetake.ssh')
project.ssh.settings {
knownHosts = allowAnyHosts
}
project.remotes {
docs {
role 'docs'
if (project.hasProperty('deployDocsHost')) {
host = project.findProperty('deployDocsHost')
} else {
host = 'docs.af.pivotal.io'
}
retryCount = 5 // retry 5 times (default is 0)
retryWaitSec = 10 // wait 10 seconds between retries (default is 0)
user = project.findProperty('deployDocsSshUsername')
if (project.hasProperty('deployDocsSshKeyPath')) {
identity = project.file(project.findProperty('deployDocsSshKeyPath'))
} else if (project.hasProperty('deployDocsSshKey')) {
identity = project.findProperty('deployDocsSshKey')
}
if(project.hasProperty('deployDocsSshPassphrase')) {
passphrase = project.findProperty('deployDocsSshPassphrase')
}
}
}
project.task('deployDocs') {
dependsOn 'docsZip'
doFirst {
project.ssh.run {
session(project.remotes.docs) {
def now = System.currentTimeMillis()
def name = project.rootProject.name
def version = project.rootProject.version
def tempPath = "/tmp/${name}-${now}-docs/".replaceAll(' ', '_')
execute "mkdir -p $tempPath"
project.tasks.docsZip.outputs.each { o ->
put from: o.files, into: tempPath
}
execute "unzip $tempPath*.zip -d $tempPath"
def extractPath = "/var/www/domains/spring.io/docs/htdocs/autorepo/docs/${name}/${version}/"
execute "rm -rf $extractPath"
execute "mkdir -p $extractPath"
execute "mv $tempPath/docs/* $extractPath"
execute "chmod -R g+w $extractPath"
}
}
}
}
}
}
@@ -17,6 +17,7 @@ public class DocsPlugin implements Plugin<Project> {
PluginManager pluginManager = project.getPluginManager();
pluginManager.apply(BasePlugin);
pluginManager.apply(DeployDocsPlugin);
pluginManager.apply(JavadocApiPlugin);
Task docsZip = project.tasks.create('docsZip', Zip) {
@@ -31,12 +32,12 @@ public class DocsPlugin implements Plugin<Project> {
into 'api'
}
into 'docs'
duplicatesStrategy = 'exclude'
duplicatesStrategy 'exclude'
}
Task docs = project.tasks.create("docs") {
group = 'Documentation'
description = 'An aggregator task to generate all the documentation'
description 'An aggregator task to generate all the documentation'
dependsOn docsZip
}
project.tasks.assemble.dependsOn docs
@@ -90,7 +90,7 @@ public class IntegrationTestPlugin implements Plugin<Project> {
project.plugins.withType(IdeaPlugin) {
project.idea {
module {
testSources.from(project.file('src/integration-test/java'))
testSourceDirs += project.file('src/integration-test/java')
scopes.TEST.plus += [ project.configurations.integrationTestCompileClasspath ]
}
}
@@ -105,7 +105,7 @@ public class IntegrationTestPlugin implements Plugin<Project> {
project.plugins.withType(IdeaPlugin) {
project.idea {
module {
testSources.from(project.file('src/integration-test/groovy'))
testSourceDirs += project.file('src/integration-test/groovy')
}
}
}
@@ -26,7 +26,7 @@ import org.gradle.api.Action;
import org.gradle.api.JavaVersion
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.javadoc.Javadoc;
import org.slf4j.Logger;
@@ -71,7 +71,7 @@ public class JavadocApiPlugin implements Plugin<Project> {
}
api.setMaxMemory("1024m");
api.setDestinationDir(project.layout.getBuildDirectory().dir("api").get().getAsFile());
api.setDestinationDir(new File(project.getBuildDir(), "api"));
project.getPluginManager().apply("io.spring.convention.javadoc-options");
}
@@ -99,7 +99,7 @@ public class JavadocApiPlugin implements Plugin<Project> {
public void execute(SpringModulePlugin plugin) {
logger.info("Added sources for {}", project);
JavaPluginExtension java = project.getExtensions().getByType(JavaPluginExtension.class);
JavaPluginConvention java = project.getConvention().getPlugin(JavaPluginConvention.class);
SourceSet mainSourceSet = java.getSourceSets().getByName("main");
api.setSource(api.getSource().plus(mainSourceSet.getAllJava()));
@@ -40,7 +40,7 @@ public class SchemaZipPlugin implements Plugin<Project> {
throw new IllegalStateException("Could not find schema file for resource name " + schemaResourceName + " in src/main/resources")
}
schemaZip.into (shortName) {
duplicatesStrategy = 'exclude'
duplicatesStrategy 'exclude'
from xsdFile.path
}
versionlessXsd.getInputFiles().from(xsdFile.path)
@@ -35,7 +35,6 @@ class SpringModulePlugin extends AbstractSpringJavaPlugin {
pluginManager.apply(SpringMavenPlugin.class);
pluginManager.apply(CheckClasspathForProhibitedDependenciesPlugin.class);
pluginManager.apply("io.spring.convention.jacoco");
pluginManager.apply("java-toolchain");
def deployArtifacts = project.task("deployArtifacts")
deployArtifacts.group = 'Deploy tasks'
@@ -1,36 +0,0 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
def toolchainVersion() {
if (project.hasProperty('testToolchain')) {
return project.property('testToolchain').toString().toInteger()
}
return 17
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(toolchainVersion())
}
}
tasks.withType(JavaCompile).configureEach {
options.encoding = "UTF-8"
options.compilerArgs.add("-parameters")
options.release = 17
}
pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
kotlin {
jvmToolchain {
languageVersion = JavaLanguageVersion.of(toolchainVersion())
}
}
tasks.withType(KotlinCompile).configureEach {
compilerOptions {
javaParameters = true
jvmTarget.set(JvmTarget.JVM_17)
}
}
}
+11 -1
View File
@@ -30,6 +30,16 @@ ossrh: {
}
}
},
docs: {
stage('Deploy Docs') {
node {
checkout scm
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
sh "./gradlew deployDocs -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace"
}
}
}
},
schema: {
stage('Deploy Schema') {
node {
@@ -39,4 +49,4 @@ schema: {
}
}
}
}
}
@@ -48,7 +48,6 @@ import org.springframework.security.jackson.SecurityJacksonModules;
* @since 7.0
* @see SecurityJacksonModules
*/
@SuppressWarnings("serial")
public class CasJacksonModule extends SecurityJacksonModule {
public CasJacksonModule() {
@@ -326,7 +326,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
/**
* Use this {@code RequestMatcher} to match proxy receptor requests. Without setting
* this matcher, {@link CasAuthenticationFilter} will not capture any proxy receptor
* requests.
* requets.
* @param proxyReceptorMatcher the {@link RequestMatcher} to use
* @since 6.5
*/
@@ -383,8 +383,8 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
}
/**
* Indicates if the request is eligible to process a service ticket. This method
* exists for readability.
* Indicates if the request is elgible to process a service ticket. This method exists
* for readability.
* @param request
* @param response
* @return
@@ -396,7 +396,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
}
/**
* Indicates if the request is eligible to process a proxy ticket.
* Indicates if the request is elgible to process a proxy ticket.
* @param request
* @return
*/
@@ -419,7 +419,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
}
/**
* Indicates if the request is eligible to be processed as the proxy receptor.
* Indicates if the request is elgible to be processed as the proxy receptor.
* @param request
* @return
*/
+2 -2
View File
@@ -144,14 +144,14 @@ tasks.named('processResources', ProcessResources).configure {
into 'org/springframework/security/config/'
}
from(rncToXsd) {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
duplicatesStrategy DuplicatesStrategy.EXCLUDE
into 'org/springframework/security/config/'
}
}
tasks.named('sourcesJar', Jar).configure {
from(rncToXsd) {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
duplicatesStrategy DuplicatesStrategy.EXCLUDE
into 'org/springframework/security/config/'
}
}
@@ -31,7 +31,6 @@ 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.Disabled;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriverException;
@@ -56,7 +55,6 @@ import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.util.StringUtils;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@@ -69,7 +67,7 @@ import static org.assertj.core.api.Assertions.assertThat;
*
* @author Daniel Garnier-Moiroux
*/
@Disabled
@org.junit.jupiter.api.Disabled
class WebAuthnWebDriverTests {
private String baseUrl;
@@ -84,8 +82,6 @@ class WebAuthnWebDriverTests {
private static final String PASSWORD = "password";
private String authenticatorId = null;
@BeforeAll
static void startChromeDriverService() throws Exception {
driverService = new ChromeDriverService.Builder().usingAnyFreePort().build();
@@ -148,7 +144,7 @@ class WebAuthnWebDriverTests {
@Test
void loginWhenNoValidAuthenticatorCredentialsThenRejects() {
createVirtualAuthenticator(true);
this.getAndWait("/", "/login");
this.driver.get(this.baseUrl);
this.driver.findElement(signinWithPasskeyButton()).click();
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/login?error"));
}
@@ -157,7 +153,7 @@ class WebAuthnWebDriverTests {
void registerWhenNoLabelThenRejects() {
login();
this.getAndWait("/webauthn/register");
this.driver.get(this.baseUrl + "/webauthn/register");
this.driver.findElement(registerPasskeyButton()).click();
assertHasAlertStartingWith("error", "Error: Passkey Label is required");
@@ -167,7 +163,7 @@ class WebAuthnWebDriverTests {
void registerWhenAuthenticatorNoUserVerificationThenRejects() {
createVirtualAuthenticator(false);
login();
this.getAndWait("/webauthn/register");
this.driver.get(this.baseUrl + "/webauthn/register");
this.driver.findElement(passkeyLabel()).sendKeys("Virtual authenticator");
this.driver.findElement(registerPasskeyButton()).click();
@@ -182,8 +178,7 @@ class WebAuthnWebDriverTests {
* <li>Step 1: Log in with username / password</li>
* <li>Step 2: Register a credential from the virtual authenticator</li>
* <li>Step 3: Log out</li>
* <li>Step 4: Log in with the authenticator (no allowCredentials)</li>
* <li>Step 5: Log in again with the same authenticator (with allowCredentials)</li>
* <li>Step 4: Log in with the authenticator</li>
* </ul>
*/
@Test
@@ -195,7 +190,7 @@ class WebAuthnWebDriverTests {
login();
// Step 2: register a credential from the virtual authenticator
this.getAndWait("/webauthn/register");
this.driver.get(this.baseUrl + "/webauthn/register");
this.driver.findElement(passkeyLabel()).sendKeys("Virtual authenticator");
this.driver.findElement(registerPasskeyButton()).click();
@@ -217,58 +212,9 @@ class WebAuthnWebDriverTests {
logout();
// Step 4: log in with the virtual authenticator
this.getAndWait("/webauthn/register", "/login");
this.driver.get(this.baseUrl + "/webauthn/register");
this.driver.findElement(signinWithPasskeyButton()).click();
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/webauthn/register?continue"));
// Step 5: authenticate while being already logged in
// This simulates some use-cases with MFA. Since the user is already logged in,
// the "allowCredentials" property is populated
this.getAndWait("/login");
this.driver.findElement(signinWithPasskeyButton()).click();
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/"));
}
@Test
void registerWhenAuthenticatorAlreadyRegisteredThenRejects() {
createVirtualAuthenticator(true);
login();
registerAuthenticator("Virtual authenticator");
// Cannot re-register the same authenticator because excludeCredentials
// is not empty and contains the given authenticator
this.driver.findElement(passkeyLabel()).sendKeys("Same authenticator");
this.driver.findElement(registerPasskeyButton()).click();
await(() -> assertHasAlertStartingWith("error", "Registration failed"));
}
@Test
void registerSecondAuthenticatorThenSucceeds() {
createVirtualAuthenticator(true);
login();
registerAuthenticator("Virtual authenticator");
this.getAndWait("/webauthn/register");
List<WebElement> passkeyRows = this.driver.findElements(passkeyTableRows());
assertThat(passkeyRows).hasSize(1)
.first()
.extracting((row) -> row.findElement(firstCell()))
.extracting(WebElement::getText)
.isEqualTo("Virtual authenticator");
// Create second authenticator and register
removeAuthenticator();
createVirtualAuthenticator(true);
registerAuthenticator("Second virtual authenticator");
this.getAndWait("/webauthn/register");
passkeyRows = this.driver.findElements(passkeyTableRows());
assertThat(passkeyRows).hasSize(2)
.extracting((row) -> row.findElement(firstCell()))
.extracting(WebElement::getText)
.contains("Second virtual authenticator");
}
/**
@@ -285,14 +231,11 @@ class WebAuthnWebDriverTests {
* "https://chromedevtools.github.io/devtools-protocol/tot/WebAuthn/">https://chromedevtools.github.io/devtools-protocol/tot/WebAuthn/</a>
*/
private void createVirtualAuthenticator(boolean userIsVerified) {
if (StringUtils.hasText(this.authenticatorId)) {
throw new IllegalStateException("Authenticator already exists, please remove it before re-creating one");
}
HasCdp cdpDriver = (HasCdp) this.driver;
cdpDriver.executeCdpCommand("WebAuthn.enable", Map.of("enableUI", false));
// this.driver.addVirtualAuthenticator(createVirtualAuthenticatorOptions());
//@formatter:off
Map<String, Object> cmdResponse = cdpDriver.executeCdpCommand("WebAuthn.addVirtualAuthenticator",
cdpDriver.executeCdpCommand("WebAuthn.addVirtualAuthenticator",
Map.of(
"options",
Map.of(
@@ -305,38 +248,21 @@ class WebAuthnWebDriverTests {
)
));
//@formatter:on
this.authenticatorId = cmdResponse.get("authenticatorId").toString();
}
private void removeAuthenticator() {
HasCdp cdpDriver = (HasCdp) this.driver;
cdpDriver.executeCdpCommand("WebAuthn.removeVirtualAuthenticator",
Map.of("authenticatorId", this.authenticatorId));
this.authenticatorId = null;
}
private void login() {
this.getAndWait("/", "/login");
this.driver.get(this.baseUrl);
this.driver.findElement(usernameField()).sendKeys(USERNAME);
this.driver.findElement(passwordField()).sendKeys(PASSWORD);
this.driver.findElement(signinWithUsernamePasswordButton()).click();
// Ensure login has completed
await(() -> assertThat(this.driver.getCurrentUrl()).doesNotContain("/login"));
}
private void logout() {
this.getAndWait("/logout");
this.driver.get(this.baseUrl + "/logout");
this.driver.findElement(logoutButton()).click();
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/login?logout"));
}
private void registerAuthenticator(String passkeyName) {
this.getAndWait("/webauthn/register");
this.driver.findElement(passkeyLabel()).sendKeys(passkeyName);
this.driver.findElement(registerPasskeyButton()).click();
await(() -> assertThat(this.driver.getCurrentUrl()).endsWith("/webauthn/register?success"));
}
private AbstractStringAssert<?> assertHasAlertStartingWith(String alertType, String alertMessage) {
WebElement alert = this.driver.findElement(new By.ById(alertType));
assertThat(alert.isDisplayed())
@@ -363,15 +289,6 @@ class WebAuthnWebDriverTests {
});
}
private void getAndWait(String endpoint) {
this.getAndWait(endpoint, endpoint);
}
private void getAndWait(String endpoint, String redirectUrl) {
this.driver.get(this.baseUrl + endpoint);
this.await(() -> assertThat(this.driver.getCurrentUrl()).endsWith(redirectUrl));
}
private static By.ById passkeyLabel() {
return new By.ById("label");
}
@@ -408,10 +325,6 @@ class WebAuthnWebDriverTests {
return new By.ByCssSelector("button");
}
private static By.ByCssSelector deletePasskeyButton() {
return new By.ByCssSelector("table > tbody > tr > button");
}
/**
* The configuration for WebAuthN tests. It accesses the Server's current port, so we
* can configurer WebAuthnConfigurer#allowedOrigin
@@ -177,7 +177,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
}
/**
* Gets a shared Object. Note that object hierarchies are not considered.
* Gets a shared Object. Note that object heirarchies are not considered.
* @param sharedType the type of the shared Object
* @return the shared Object or null if it is not found
*/
@@ -360,7 +360,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
/**
* Subclasses must implement this method to build the object that is being returned.
* @return the Object to be built or null if the implementation allows it
* @return the Object to be buit or null if the implementation allows it
*/
protected abstract O performBuild();
@@ -414,13 +414,13 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
private enum BuildState {
/**
* This is the state before the {@link SecurityBuilder#build()} is invoked
* This is the state before the {@link Builder#build()} is invoked
*/
UNBUILT(0),
/**
* The state from when {@link SecurityBuilder#build()} is first invoked until all
* the {@link SecurityConfigurer#init(SecurityBuilder)} methods have been invoked.
* The state from when {@link Builder#build()} is first invoked until all the
* {@link SecurityConfigurer#init(SecurityBuilder)} methods have been invoked.
*/
INITIALIZING(1),
@@ -42,8 +42,7 @@ final class MethodSecuritySelector implements ImportSelector {
.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);
private static final boolean isWebPresent = ClassUtils
.isPresent("org.springframework.web.servlet.DispatcherServlet", null)
&& ClassUtils.isPresent("org.springframework.security.web.util.ThrowableAnalyzer", null);
.isPresent("org.springframework.web.servlet.DispatcherServlet", null);
private static final boolean isObservabilityPresent = ClassUtils
.isPresent("io.micrometer.observation.ObservationRegistry", null);
@@ -82,7 +82,7 @@ public interface HttpSecurityBuilder<H extends HttpSecurityBuilder<H>>
<C> void setSharedObject(Class<C> sharedType, C object);
/**
* Gets a shared Object. Note that object hierarchies are not considered.
* Gets a shared Object. Note that object heirarchies are not considered.
* @param sharedType the type of the shared Object
* @return the shared Object or null if it is not found
*/
@@ -133,7 +133,7 @@ final class FilterOrderRegistration {
/**
* Register a {@link Filter} with its specific position. If the {@link Filter} was
* already registered before, the position previously defined is not going to be
* overridden
* overriden
* @param filter the {@link Filter} to register
* @param position the position to associate with the {@link Filter}
*/
@@ -2035,9 +2035,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
*/
public HttpSecurity securityMatcher(String... patterns) {
List<RequestMatcher> matchers = new ArrayList<>();
ApplicationContext context = getSharedObject(ApplicationContext.class);
PathPatternRequestMatcher.Builder builder = context.getBeanProvider(PathPatternRequestMatcher.Builder.class)
.getIfUnique(() -> getSharedObject(PathPatternRequestMatcher.Builder.class));
PathPatternRequestMatcher.Builder builder = getSharedObject(PathPatternRequestMatcher.Builder.class);
for (String pattern : patterns) {
matchers.add(builder.matcher(pattern));
}
@@ -2054,6 +2052,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
* http
* // ...
* .webAuthn((webAuthn) -&gt; webAuthn
* .rpName("Spring Security Relying Party")
* .rpId("example.com")
* .allowedOrigins("https://example.com")
* );
@@ -305,7 +305,7 @@ public final class LogoutConfigurer<H extends HttpSecurityBuilder<H>>
}
/**
* Gets the logoutSuccessUrl or null if a
* Gets the logoutSuccesUrl or null if a
* {@link #logoutSuccessHandler(LogoutSuccessHandler)} was configured.
* @return the logoutSuccessUrl
*/
@@ -146,7 +146,7 @@ public final class SessionManagementConfigurer<H extends HttpSecurityBuilder<H>>
/**
* This should not use RequestAttributeSecurityContextRepository since that is
* stateless and session management is about state management.
* stateless and sesison management is about state management.
*/
private SecurityContextRepository sessionManagementSecurityContextRepository = new HttpSessionSecurityContextRepository();
@@ -39,7 +39,6 @@ import org.springframework.security.web.webauthn.api.PublicKeyCredentialRpEntity
import org.springframework.security.web.webauthn.authentication.PublicKeyCredentialRequestOptionsFilter;
import org.springframework.security.web.webauthn.authentication.WebAuthnAuthenticationFilter;
import org.springframework.security.web.webauthn.authentication.WebAuthnAuthenticationProvider;
import org.springframework.security.web.webauthn.management.CredentialRecordOwnerAuthorizationManager;
import org.springframework.security.web.webauthn.management.MapPublicKeyCredentialUserEntityRepository;
import org.springframework.security.web.webauthn.management.MapUserCredentialRepository;
import org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository;
@@ -178,11 +177,8 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
WebAuthnAuthenticationFilter webAuthnAuthnFilter = new WebAuthnAuthenticationFilter();
webAuthnAuthnFilter.setAuthenticationManager(
new ProviderManager(new WebAuthnAuthenticationProvider(rpOperations, userDetailsService)));
webAuthnAuthnFilter = postProcess(webAuthnAuthnFilter);
WebAuthnRegistrationFilter webAuthnRegistrationFilter = new WebAuthnRegistrationFilter(userCredentials,
rpOperations);
webAuthnRegistrationFilter.setDeleteCredentialAuthorizationManager(
new CredentialRecordOwnerAuthorizationManager(userCredentials, userEntities));
PublicKeyCredentialCreationOptionsFilter creationOptionsFilter = new PublicKeyCredentialCreationOptionsFilter(
rpOperations);
if (creationOptionsRepository != null) {
@@ -260,10 +256,9 @@ public class WebAuthnConfigurer<H extends HttpSecurityBuilder<H>>
PublicKeyCredentialUserEntityRepository userEntities, UserCredentialRepository userCredentials) {
Optional<WebAuthnRelyingPartyOperations> webauthnOperationsBean = getBeanOrNull(
WebAuthnRelyingPartyOperations.class);
String rpName = (this.rpName != null) ? this.rpName : this.rpId;
return webauthnOperationsBean
.orElseGet(() -> new Webauthn4JRelyingPartyOperations(userEntities, userCredentials,
PublicKeyCredentialRpEntity.builder().id(this.rpId).name(rpName).build(), this.allowedOrigins));
return webauthnOperationsBean.orElseGet(() -> new Webauthn4JRelyingPartyOperations(userEntities,
userCredentials, PublicKeyCredentialRpEntity.builder().id(this.rpId).name(this.rpName).build(),
this.allowedOrigins));
}
}
@@ -16,12 +16,10 @@
package org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import jakarta.servlet.Filter;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.http.HttpMethod;
@@ -38,12 +36,10 @@ import org.springframework.security.oauth2.server.authorization.authentication.O
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationValidator;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.web.OAuth2AuthorizationEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationCodeRequestAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2AuthorizationConsentAuthenticationConverter;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
@@ -54,7 +50,6 @@ import org.springframework.security.web.servlet.util.matcher.PathPatternRequestM
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
@@ -88,8 +83,6 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
private Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationCodeRequestAuthenticationValidator;
private Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> authorizationCodeRequestAuthenticationValidatorComposite;
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
/**
@@ -255,16 +248,8 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
authenticationProviders.addAll(0, this.authenticationProviders);
}
this.authenticationProvidersConsumer.accept(authenticationProviders);
authenticationProviders.forEach((authenticationProvider) -> {
httpSecurity.authenticationProvider(postProcess(authenticationProvider));
if (authenticationProvider instanceof OAuth2AuthorizationCodeRequestAuthenticationProvider) {
Method method = ReflectionUtils.findMethod(OAuth2AuthorizationCodeRequestAuthenticationProvider.class,
"getAuthenticationValidatorComposite");
ReflectionUtils.makeAccessible(method);
this.authorizationCodeRequestAuthenticationValidatorComposite = (Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext>) ReflectionUtils
.invokeMethod(method, authenticationProvider);
}
});
authenticationProviders.forEach(
(authenticationProvider) -> httpSecurity.authenticationProvider(postProcess(authenticationProvider)));
}
@Override
@@ -297,18 +282,7 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C
if (this.sessionAuthenticationStrategy != null) {
authorizationEndpointFilter.setSessionAuthenticationStrategy(this.sessionAuthenticationStrategy);
}
httpSecurity.addFilterAfter(postProcess(authorizationEndpointFilter), AuthorizationFilter.class);
// Create and add
// OAuth2AuthorizationEndpointFilter.OAuth2AuthorizationCodeRequestValidatingFilter
Method method = ReflectionUtils.findMethod(OAuth2AuthorizationEndpointFilter.class,
"createAuthorizationCodeRequestValidatingFilter", RegisteredClientRepository.class, Consumer.class);
ReflectionUtils.makeAccessible(method);
RegisteredClientRepository registeredClientRepository = OAuth2ConfigurerUtils
.getRegisteredClientRepository(httpSecurity);
Filter authorizationCodeRequestValidatingFilter = (Filter) ReflectionUtils.invokeMethod(method,
authorizationEndpointFilter, registeredClientRepository,
this.authorizationCodeRequestAuthenticationValidatorComposite);
httpSecurity.addFilterBefore(postProcess(authorizationCodeRequestValidatingFilter),
httpSecurity.addFilterBefore(postProcess(authorizationEndpointFilter),
AbstractPreAuthenticatedProcessingFilter.class);
}
@@ -16,9 +16,14 @@
package org.springframework.security.config.annotation.web.configurers.oauth2.server.authorization;
import java.util.Map;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -40,6 +45,7 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Utility methods for the OAuth 2.0 Configurers.
@@ -201,16 +207,41 @@ final class OAuth2ConfigurerUtils {
}
static <T> T getBean(HttpSecurity httpSecurity, Class<T> type) {
return httpSecurity.getSharedObject(ApplicationContext.class).getBeanProvider(type).getObject();
return httpSecurity.getSharedObject(ApplicationContext.class).getBean(type);
}
@SuppressWarnings("unchecked")
static <T> T getBean(HttpSecurity httpSecurity, ResolvableType type) {
ApplicationContext context = httpSecurity.getSharedObject(ApplicationContext.class);
String[] names = context.getBeanNamesForType(type);
if (names.length == 1) {
return (T) context.getBean(names[0]);
}
if (names.length > 1) {
throw new NoUniqueBeanDefinitionException(type, names);
}
throw new NoSuchBeanDefinitionException(type);
}
static <T> T getOptionalBean(HttpSecurity httpSecurity, Class<T> type) {
return httpSecurity.getSharedObject(ApplicationContext.class).getBeanProvider(type).getIfUnique();
Map<String, T> beansMap = BeanFactoryUtils
.beansOfTypeIncludingAncestors(httpSecurity.getSharedObject(ApplicationContext.class), type);
if (beansMap.size() > 1) {
throw new NoUniqueBeanDefinitionException(type, beansMap.size(),
"Expected single matching bean of type '" + type.getName() + "' but found " + beansMap.size() + ": "
+ StringUtils.collectionToCommaDelimitedString(beansMap.keySet()));
}
return (!beansMap.isEmpty() ? beansMap.values().iterator().next() : null);
}
@SuppressWarnings("unchecked")
static <T> T getOptionalBean(HttpSecurity httpSecurity, ResolvableType type) {
return (T) httpSecurity.getSharedObject(ApplicationContext.class).getBeanProvider(type).getIfUnique();
ApplicationContext context = httpSecurity.getSharedObject(ApplicationContext.class);
String[] names = context.getBeanNamesForType(type);
if (names.length > 1) {
throw new NoUniqueBeanDefinitionException(type, names);
}
return (names.length == 1) ? (T) context.getBean(names[0]) : null;
}
}
@@ -40,11 +40,11 @@ import org.springframework.security.oauth2.server.authorization.settings.Authori
import org.springframework.security.oauth2.server.authorization.web.OAuth2DeviceVerificationEndpointFilter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceAuthorizationConsentAuthenticationConverter;
import org.springframework.security.oauth2.server.authorization.web.authentication.OAuth2DeviceVerificationAuthenticationConverter;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.authentication.AuthenticationConverter;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.DelegatingAuthenticationConverter;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
@@ -279,7 +279,8 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA
if (StringUtils.hasText(this.consentPage)) {
deviceVerificationEndpointFilter.setConsentPage(this.consentPage);
}
builder.addFilterAfter(postProcess(deviceVerificationEndpointFilter), AuthorizationFilter.class);
builder.addFilterBefore(postProcess(deviceVerificationEndpointFilter),
AbstractPreAuthenticatedProcessingFilter.class);
}
@Override
@@ -95,7 +95,7 @@ public class Saml2MetadataConfigurer<H extends HttpSecurityBuilder<H>>
* If there is no {@code registrationId} and your
* {@link RelyingPartyRegistrationRepository} is {code Iterable}, the metadata
* endpoint will try and show all relying parties' metadata in a single
* {@code <md:EntitiesDescriptor} element.
* {@code <md:EntitiesDecriptor} element.
*
* <p>
* If you need a more sophisticated lookup strategy than these, use
@@ -167,7 +167,7 @@ class ServerHttpSecurityConfiguration {
}
/**
* Applies all {@code Customizer<ServerHttpSecurity>} Beans to
* Applies all {@code Custmizer<ServerHttpSecurity>} Beans to
* {@link ServerHttpSecurity}.
* @param context the {@link ApplicationContext}
* @param http the {@link ServerHttpSecurity}
@@ -255,9 +255,7 @@ class ServerHttpSecurityConfiguration {
if (this.passwordEncoder != null) {
manager.setPasswordEncoder(this.passwordEncoder);
}
if (this.userDetailsPasswordService != null) {
manager.setUserDetailsPasswordService(this.userDetailsPasswordService);
}
manager.setUserDetailsPasswordService(this.userDetailsPasswordService);
manager.setCompromisedPasswordChecker(this.compromisedPasswordChecker);
return this.postProcessor.postProcess(manager);
}
@@ -538,7 +538,7 @@ final class AuthenticationConfigBuilder {
}
injectAuthenticationDetailsSource(x509Elt, filterBuilder);
filter = (RootBeanDefinition) filterBuilder.getBeanDefinition();
createPreauthEntryPoint(x509Elt);
createPrauthEntryPoint(x509Elt);
createX509Provider();
}
this.x509Filter = filter;
@@ -562,7 +562,7 @@ final class AuthenticationConfigBuilder {
this.x509ProviderRef = new RuntimeBeanReference(this.pc.getReaderContext().registerWithGeneratedName(provider));
}
private void createPreauthEntryPoint(Element source) {
private void createPrauthEntryPoint(Element source) {
if (this.preAuthEntryPoint == null) {
this.preAuthEntryPoint = new RootBeanDefinition(Http403ForbiddenEntryPoint.class);
this.preAuthEntryPoint.setSource(this.pc.extractSource(source));
@@ -595,7 +595,7 @@ final class AuthenticationConfigBuilder {
adsBldr.addPropertyValue("mappableRolesRetriever", mappableRolesRetriever);
filterBuilder.addPropertyValue("authenticationDetailsSource", adsBldr.getBeanDefinition());
filter = (RootBeanDefinition) filterBuilder.getBeanDefinition();
createPreauthEntryPoint(jeeElt);
createPrauthEntryPoint(jeeElt);
createJeeProvider();
}
this.jeeFilter = filter;
@@ -70,7 +70,7 @@ public final class PathPatternRequestMatcherFactoryBean
@Override
public void afterPropertiesSet() throws Exception {
if (this.basePath != null) {
this.builder = this.builder.basePath(this.basePath);
this.builder.basePath(this.basePath);
}
}
@@ -165,20 +165,20 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
}
private RootBeanDefinition getRootBeanDefinition(String mode) {
if (isUnboundIdEnabled(mode)) {
if (isUnboundidEnabled(mode)) {
return new RootBeanDefinition(UNBOUNDID_CONTAINER_CLASSNAME, null, null);
}
throw new IllegalStateException("Embedded LDAP server is not provided");
}
private String resolveBeanId(String mode) {
if (isUnboundIdEnabled(mode)) {
if (isUnboundidEnabled(mode)) {
return BeanIds.EMBEDDED_UNBOUNDID;
}
return null;
}
private boolean isUnboundIdEnabled(String mode) {
private boolean isUnboundidEnabled(String mode) {
return "unboundid".equals(mode) || unboundIdPresent;
}
@@ -12,8 +12,8 @@ base64 =
## Whether a string should be base64 encoded
attribute base64 {xsd:boolean}
request-matcher =
## Defines the strategy use for matching incoming requests. Currently the options are 'path' (for PathPatternRequestMatcher), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.
attribute request-matcher {"path" | "regex" | "ciRegex"}
## Defines the strategy use for matching incoming requests. Currently the options are 'mvc' (for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions and 'ciRegex' for case-insensitive regular expressions.
attribute request-matcher {"mvc" | "ant" | "regex" | "ciRegex"}
port =
## Specifies an IP port number. Used to configure an embedded LDAP server, for example.
attribute port { xsd:nonNegativeInteger }
@@ -27,14 +27,15 @@
<xs:attributeGroup name="request-matcher">
<xs:attribute name="request-matcher" use="required">
<xs:annotation>
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'path'
(for PathPatternRequestMatcher), 'regex' for regular expressions and 'ciRegex' for
case-insensitive regular expressions.
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'
(for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions
and 'ciRegex' for case-insensitive regular expressions.
</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="path"/>
<xs:enumeration value="mvc"/>
<xs:enumeration value="ant"/>
<xs:enumeration value="regex"/>
<xs:enumeration value="ciRegex"/>
</xs:restriction>
@@ -1305,14 +1306,15 @@
</xs:attribute>
<xs:attribute name="request-matcher">
<xs:annotation>
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'path'
(for PathPatternRequestMatcher), 'regex' for regular expressions and 'ciRegex' for
case-insensitive regular expressions.
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'
(for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions
and 'ciRegex' for case-insensitive regular expressions.
</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="path"/>
<xs:enumeration value="mvc"/>
<xs:enumeration value="ant"/>
<xs:enumeration value="regex"/>
<xs:enumeration value="ciRegex"/>
</xs:restriction>
@@ -2472,14 +2474,15 @@
<xs:attributeGroup name="filter-chain-map.attlist">
<xs:attribute name="request-matcher">
<xs:annotation>
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'path'
(for PathPatternRequestMatcher), 'regex' for regular expressions and 'ciRegex' for
case-insensitive regular expressions.
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'
(for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions
and 'ciRegex' for case-insensitive regular expressions.
</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="path"/>
<xs:enumeration value="mvc"/>
<xs:enumeration value="ant"/>
<xs:enumeration value="regex"/>
<xs:enumeration value="ciRegex"/>
</xs:restriction>
@@ -2577,14 +2580,15 @@
</xs:attribute>
<xs:attribute name="request-matcher">
<xs:annotation>
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'path'
(for PathPatternRequestMatcher), 'regex' for regular expressions and 'ciRegex' for
case-insensitive regular expressions.
<xs:documentation>Defines the strategy use for matching incoming requests. Currently the options are 'mvc'
(for Spring MVC matcher), 'ant' (for ant path patterns), 'regex' for regular expressions
and 'ciRegex' for case-insensitive regular expressions.
</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:token">
<xs:enumeration value="path"/>
<xs:enumeration value="mvc"/>
<xs:enumeration value="ant"/>
<xs:enumeration value="regex"/>
<xs:enumeration value="ciRegex"/>
</xs:restriction>
@@ -20,7 +20,6 @@ import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.security.Principal;
import java.time.Duration;
import java.time.Instant;
import java.util.Collection;
import java.util.Date;
@@ -86,9 +85,6 @@ import org.springframework.security.authentication.password.CompromisedPasswordE
import org.springframework.security.authorization.AuthorityAuthorizationDecision;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationDeniedException;
import org.springframework.security.authorization.FactorAuthorizationDecision;
import org.springframework.security.authorization.RequiredFactor;
import org.springframework.security.authorization.RequiredFactorError;
import org.springframework.security.authorization.event.AuthorizationEvent;
import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
import org.springframework.security.cas.authentication.CasAssertionAuthenticationToken;
@@ -165,7 +161,6 @@ import org.springframework.security.oauth2.jwt.JwtException;
import org.springframework.security.oauth2.jwt.JwtValidationException;
import org.springframework.security.oauth2.jwt.TestJwts;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationCode;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsent;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadata;
import org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;
@@ -173,22 +168,15 @@ import org.springframework.security.oauth2.server.authorization.OAuth2TokenIntro
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AccessTokenAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationConsentAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationGrantAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientCredentialsAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientRegistrationAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationConsentAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceAuthorizationRequestAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceCodeAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2DeviceVerificationAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2PushedAuthorizationRequestAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2RefreshTokenAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeActor;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenExchangeCompositeAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenIntrospectionAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2TokenRevocationAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
@@ -202,7 +190,6 @@ import org.springframework.security.oauth2.server.authorization.settings.Authori
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenClaimNames;
import org.springframework.security.oauth2.server.resource.BearerTokenError;
import org.springframework.security.oauth2.server.resource.BearerTokenErrors;
import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException;
@@ -262,7 +249,6 @@ import org.springframework.security.web.webauthn.api.AuthenticationExtensionsCli
import org.springframework.security.web.webauthn.api.AuthenticationExtensionsClientOutputs;
import org.springframework.security.web.webauthn.api.AuthenticatorAssertionResponse;
import org.springframework.security.web.webauthn.api.AuthenticatorAttachment;
import org.springframework.security.web.webauthn.api.AuthenticatorAttestationResponse;
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
import org.springframework.security.web.webauthn.api.Bytes;
import org.springframework.security.web.webauthn.api.CredProtectAuthenticationExtensionsClientInput;
@@ -277,7 +263,6 @@ import org.springframework.security.web.webauthn.api.PublicKeyCredentialRequestO
import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialUserEntity;
import org.springframework.security.web.webauthn.api.TestAuthenticationAssertionResponses;
import org.springframework.security.web.webauthn.api.TestAuthenticatorAttestationResponses;
import org.springframework.security.web.webauthn.api.TestBytes;
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialRequestOptions;
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialUserEntities;
@@ -442,8 +427,6 @@ final class SerializationSamples {
generatorByClassName.put(RegisteredClient.class, (r) -> registeredClient);
generatorByClassName.put(OAuth2Authorization.class, (r) -> authorization);
generatorByClassName.put(OAuth2Authorization.Token.class, (r) -> authorization.getAccessToken());
generatorByClassName.put(OAuth2AuthorizationCode.class,
(r) -> new OAuth2AuthorizationCode("code", Instant.now(), Instant.now().plusSeconds(300)));
generatorByClassName.put(OAuth2AuthorizationConsent.class,
(r) -> OAuth2AuthorizationConsent.withId("registeredClientId", "principalName")
.scope("scope1")
@@ -469,58 +452,6 @@ final class SerializationSamples {
authenticationToken.setDetails(details);
return authenticationToken;
});
generatorByClassName.put(
org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken.class,
(r) -> {
org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken token = new org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationToken(
"code", principal, "https://localhost/callback", Map.of("custom_param", "custom_value"));
token.setDetails(details);
return token;
});
generatorByClassName.put(OAuth2AuthorizationCodeRequestAuthenticationException.class, (r) -> {
OAuth2AuthorizationCodeRequestAuthenticationToken authToken = new OAuth2AuthorizationCodeRequestAuthenticationToken(
"https://localhost/authorize", "clientId", principal, "https://localhost/callback", "state",
authorizationRequest.getScopes(), authorizationRequest.getAdditionalParameters());
return new OAuth2AuthorizationCodeRequestAuthenticationException(
new OAuth2Error("invalid_request", "Missing required parameter", "https://example.com/error"),
authToken);
});
generatorByClassName.put(OAuth2ClientCredentialsAuthenticationToken.class, (r) -> {
OAuth2ClientCredentialsAuthenticationToken token = new OAuth2ClientCredentialsAuthenticationToken(principal,
Set.of("scope1", "scope2"), Map.of("custom_param", "custom_value"));
token.setDetails(details);
return token;
});
generatorByClassName.put(OAuth2DeviceCodeAuthenticationToken.class, (r) -> {
OAuth2DeviceCodeAuthenticationToken token = new OAuth2DeviceCodeAuthenticationToken("device-code",
principal, Map.of("custom_param", "custom_value"));
token.setDetails(details);
return token;
});
generatorByClassName.put(OAuth2RefreshTokenAuthenticationToken.class, (r) -> {
OAuth2RefreshTokenAuthenticationToken token = new OAuth2RefreshTokenAuthenticationToken("refresh-token",
principal, Set.of("scope1", "scope2"), Map.of("custom_param", "custom_value"));
token.setDetails(details);
return token;
});
generatorByClassName.put(OAuth2TokenExchangeAuthenticationToken.class, (r) -> {
OAuth2TokenExchangeAuthenticationToken token = new OAuth2TokenExchangeAuthenticationToken(
"urn:ietf:params:oauth:token-type:access_token", "subject-token",
"urn:ietf:params:oauth:token-type:jwt", principal, "actor-token",
"urn:ietf:params:oauth:token-type:jwt", Set.of("https://resource.example.com"), Set.of("audience"),
Set.of("scope1"), Map.of("custom_param", "custom_value"));
token.setDetails(details);
return token;
});
OAuth2TokenExchangeActor actor = new OAuth2TokenExchangeActor(Map.of(OAuth2TokenClaimNames.ISS,
"https://issuer.example.com", OAuth2TokenClaimNames.SUB, "actor-subject"));
generatorByClassName.put(OAuth2TokenExchangeActor.class, (r) -> actor);
generatorByClassName.put(OAuth2TokenExchangeCompositeAuthenticationToken.class, (r) -> {
AbstractAuthenticationToken token = new OAuth2TokenExchangeCompositeAuthenticationToken(authentication,
List.of(actor));
token.setDetails(details);
return token;
});
generatorByClassName.put(OAuth2AuthorizationConsentAuthenticationToken.class, (r) -> {
OAuth2AuthorizationConsentAuthenticationToken authenticationToken = new OAuth2AuthorizationConsentAuthenticationToken(
"authorizationUri", "clientId", principal, "state", authorizationRequest.getScopes(),
@@ -737,12 +668,6 @@ final class SerializationSamples {
generatorByClassName.put(AuthorizationDecision.class, (r) -> new AuthorizationDecision(true));
generatorByClassName.put(AuthorityAuthorizationDecision.class,
(r) -> new AuthorityAuthorizationDecision(true, AuthorityUtils.createAuthorityList("ROLE_USER")));
RequiredFactor factor = RequiredFactor.withAuthority("authority").validDuration(Duration.ofSeconds(5)).build();
generatorByClassName.put(RequiredFactor.class, (r) -> factor);
RequiredFactorError error = RequiredFactorError.createMissing(factor);
generatorByClassName.put(RequiredFactorError.class, (r) -> error);
generatorByClassName.put(FactorAuthorizationDecision.class,
(r) -> new FactorAuthorizationDecision(List.of(error)));
generatorByClassName.put(CycleInRoleHierarchyException.class, (r) -> new CycleInRoleHierarchyException());
generatorByClassName.put(AuthorizationEvent.class,
(r) -> new AuthorizationEvent(new SerializableSupplier<>(authentication), "source",
@@ -933,8 +858,6 @@ final class SerializationSamples {
generatorByClassName.put(CredentialPropertiesOutput.class, (o) -> credentialOutput);
generatorByClassName.put(ImmutableAuthenticationExtensionsClientOutputs.class, (o) -> outputs);
generatorByClassName.put(AuthenticatorAssertionResponse.class, (r) -> response);
generatorByClassName.put(AuthenticatorAttestationResponse.class,
(r) -> TestAuthenticatorAttestationResponses.createAuthenticatorAttestationResponse().build());
generatorByClassName.put(RelyingPartyAuthenticationRequest.class, (r) -> authRequest);
generatorByClassName.put(PublicKeyCredential.class, (r) -> credential);
generatorByClassName.put(WebAuthnAuthenticationRequestToken.class, (r) -> requestToken);
@@ -33,10 +33,10 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import org.apache.commons.lang3.ObjectUtils;
@@ -207,7 +207,10 @@ class SpringSecurityCoreVersionSerializableTests {
boolean hasSerialVersion = Stream.of(clazz.getDeclaredFields())
.map(Field::getName)
.anyMatch((n) -> n.equals("serialVersionUID"));
if (!hasSerialVersion && !hasSuppressSerialInSource(clazz)) {
SuppressWarnings suppressWarnings = clazz.getAnnotation(SuppressWarnings.class);
boolean hasSerialIgnore = suppressWarnings == null
|| Arrays.asList(suppressWarnings.value()).contains("Serial");
if (!hasSerialVersion && !hasSerialIgnore) {
classes.add(clazz);
continue;
}
@@ -246,62 +249,11 @@ class SpringSecurityCoreVersionSerializableTests {
return classes.stream();
}
private static boolean hasSuppressSerialInSource(Class<?> clazz) {
try {
Class<?> fileClass = clazz;
while (fileClass.getEnclosingClass() != null) {
fileClass = fileClass.getEnclosingClass();
}
var codeSource = fileClass.getProtectionDomain().getCodeSource();
if (codeSource == null) {
return false;
}
Path sourceFile = findSourceFile(Path.of(codeSource.getLocation().toURI()), fileClass);
if (sourceFile == null) {
return false;
}
return hasSuppressSerialAnnotation(Files.readAllLines(sourceFile), clazz.getSimpleName());
}
catch (Exception ex) {
return false;
}
}
private static Path findSourceFile(Path start, Class<?> clazz) {
String relativePath = clazz.getName().replace('.', '/') + ".java";
Path dir = start;
for (int i = 0; i < 10 && dir != null; i++) {
for (String sourceRoot : List.of("src/main/java", "src/test/java")) {
Path candidate = dir.resolve(sourceRoot).resolve(relativePath);
if (Files.exists(candidate)) {
return candidate;
}
}
dir = dir.getParent();
}
return null;
}
private static boolean hasSuppressSerialAnnotation(List<String> lines, String simpleClassName) {
Pattern classDeclaration = Pattern
.compile("\\b(?:class|interface|enum|record)\\s+" + Pattern.quote(simpleClassName) + "\\b");
for (int i = 0; i < lines.size(); i++) {
if (classDeclaration.matcher(lines.get(i)).find()) {
for (int j = Math.max(0, i - 5); j < i; j++) {
String line = lines.get(j);
if (line.contains("@SuppressWarnings") && line.contains("\"serial\"")) {
return true;
}
}
}
}
return false;
}
private static String getCurrentVersion() {
String version = System.getProperty("springSecurityVersion");
String[] parts = version.split("\\.");
return parts[0] + "." + parts[1] + ".x";
parts[2] = "x";
return String.join(".", parts);
}
private static String getPreviousVersion() {
@@ -314,7 +266,8 @@ class SpringSecurityCoreVersionSerializableTests {
parts[0] = String.valueOf(Integer.parseInt(parts[0]) - 1);
parts[1] = "5"; // FIXME: this should not be hard coded
}
return parts[0] + "." + parts[1] + ".x";
parts[2] = "x";
return String.join(".", parts);
}
}
@@ -37,7 +37,6 @@ import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
import org.springframework.security.core.userdetails.PasswordEncodedUser;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@@ -111,21 +110,6 @@ public class WebSecurityTests {
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
}
// gh-19128
@Test
public void ignoringWhenBuilderBeanWithBasePathThenHonorsBasePath() throws Exception {
loadConfig(IgnoringBuilderBeanConfig.class);
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
setup();
this.request.setServletPath("");
this.request.setRequestURI("/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
}
public void loadConfig(Class<?>... configs) {
this.context = new AnnotationConfigWebApplicationContext();
this.context.register(configs);
@@ -217,52 +201,6 @@ public class WebSecurityTests {
}
// gh-19128
@EnableWebSecurity
@Configuration
@EnableWebMvc
static class IgnoringBuilderBeanConfig {
@Bean
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
bean.setBasePath("/spring");
return bean;
}
@Bean
WebSecurityCustomizer webSecurityCustomizer() {
return (web) -> web.ignoring().requestMatchers("/path");
}
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic(withDefaults())
.authorizeHttpRequests((requests) -> requests
.anyRequest().denyAll());
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager(PasswordEncodedUser.user());
}
@RestController
static class PathController {
@RequestMapping("/path")
String path() {
return "path";
}
}
}
@Configuration
@EnableWebSecurity
static class RequestRejectedHandlerConfig {
@@ -452,18 +452,6 @@ public class AuthorizeHttpRequestsConfigurerTests {
this.mvc.perform(requestWithAdmin).andExpect(status().isOk());
}
@Test
public void requestMatchersWhenBuilderBeanWithBasePathAndRawStringThenHonorsBasePath() throws Exception {
this.spring.register(RequestMatchersRawStringServletPathConfig.class, BasicController.class).autowire();
// @formatter:off
MockHttpServletRequestBuilder matchedByBasePath = get("/spring/path")
.servletPath("/spring")
.with(user("user").roles("USER"));
// @formatter:on
this.mvc.perform(matchedByBasePath).andExpect(status().isForbidden());
this.mvc.perform(get("/path").with(user("user").roles("USER"))).andExpect(status().isOk());
}
@Test
public void getWhenAnyRequestAuthenticatedConfiguredAndNoUserThenRespondsWithUnauthorized() throws Exception {
this.spring.register(AuthenticatedConfig.class, BasicController.class).autowire();
@@ -1352,7 +1340,7 @@ public class AuthorizeHttpRequestsConfigurerTests {
static class ServletPathConfig {
@Bean
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean requesMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
bean.setBasePath("/spring");
return bean;
@@ -1371,32 +1359,6 @@ public class AuthorizeHttpRequestsConfigurerTests {
}
@Configuration
@EnableWebMvc
@EnableWebSecurity
static class RequestMatchersRawStringServletPathConfig {
@Bean
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
bean.setBasePath("/spring");
return bean;
}
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
return http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/path").hasRole("ADMIN")
.anyRequest().permitAll()
)
.build();
// @formatter:on
}
}
@Configuration
@EnableWebSecurity
static class AuthenticatedConfig {
@@ -40,7 +40,6 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.config.users.AuthenticationTestConfiguration;
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
import org.springframework.security.core.authority.FactorGrantedAuthority;
import org.springframework.security.core.context.SecurityContextChangedListener;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
@@ -154,17 +153,6 @@ public class FormLoginConfigurerTests {
// @formatter:on
}
// gh-19128
@Test
public void loginWhenBuilderBeanWithBasePathThenLoginProcessingUrlIgnoresBasePath() throws Exception {
this.spring.register(FormLoginBuilderBeanConfig.class).autowire();
// @formatter:off
this.mockMvc.perform(formLogin().user("invalid"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("/login?error"));
// @formatter:on
}
@Test
public void loginWhenFormLoginConfiguredThenHasDefaultSuccessUrl() throws Exception {
this.spring.register(FormLoginConfig.class).autowire();
@@ -531,37 +519,6 @@ public class FormLoginConfigurerTests {
}
// gh-19128
@Configuration
@EnableWebSecurity
@EnableWebMvc
static class FormLoginBuilderBeanConfig {
@Bean
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
bean.setBasePath("/spring");
return bean;
}
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((requests) -> requests
.anyRequest().authenticated())
.formLogin(withDefaults());
// @formatter:on
return http.build();
}
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager(PasswordEncodedUser.user());
}
}
@Configuration
@EnableWebSecurity
static class FormLoginInLambdaConfig {
@@ -125,34 +125,6 @@ public class HttpSecuritySecurityMatchersTests {
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@Test
public void securityMatcherWhenBuilderBeanWithBasePathThenHonorsBasePath() throws Exception {
loadConfig(SecurityMatcherBuilderBeanConfig.class);
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("");
this.request.setRequestURI("/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@Test
public void securityMatchersWhenBuilderBeanWithBasePathAndRawStringsThenHonorsBasePath() throws Exception {
loadConfig(SecurityMatchersBuilderBeanConfig.class);
this.request.setServletPath("/spring");
this.request.setRequestURI("/spring/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_UNAUTHORIZED);
setup();
this.request.setServletPath("");
this.request.setRequestURI("/path");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.chain);
assertThat(this.response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@Test
public void securityMatchersWhenMultiMvcMatcherInLambdaThenAllPathsAreDenied() throws Exception {
loadConfig(MultiMvcMatcherInLambdaConfig.class);
@@ -458,83 +430,6 @@ public class HttpSecuritySecurityMatchersTests {
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
@Import(UsersConfig.class)
static class SecurityMatcherBuilderBeanConfig {
@Bean
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
bean.setBasePath("/spring");
return bean;
}
@Bean
SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
// @formatter:off
http
.securityMatcher("/path")
.httpBasic(withDefaults())
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().denyAll());
// @formatter:on
return http.build();
}
@RestController
static class PathController {
@RequestMapping("/path")
String path() {
return "path";
}
}
}
@EnableWebSecurity
@Configuration
@EnableWebMvc
@Import(UsersConfig.class)
static class SecurityMatchersBuilderBeanConfig {
@Bean
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
bean.setBasePath("/spring");
return bean;
}
@Bean
SecurityFilterChain appSecurity(HttpSecurity http) throws Exception {
// @formatter:off
http
.securityMatchers((matchers) -> matchers
.requestMatchers("/path")
)
.httpBasic(withDefaults())
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().denyAll()
);
// @formatter:on
return http.build();
}
@RestController
static class PathController {
@RequestMapping("/path")
String path() {
return "path";
}
}
}
@Configuration
static class UsersConfig {
@@ -35,7 +35,6 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.userdetails.PasswordEncodedUser;
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -128,17 +127,6 @@ public class LogoutConfigurerTests {
// @formatter:on
}
// gh-19128
@Test
public void logoutWhenBuilderBeanWithBasePathThenLogoutUrlIgnoresBasePath() throws Exception {
this.spring.register(LogoutBuilderBeanConfig.class).autowire();
// @formatter:off
this.mvc.perform(post("/logout").with(csrf()))
.andExpect(status().isFound())
.andExpect(redirectedUrl("/login?logout"));
// @formatter:on
}
// SEC-2311
@Test
public void logoutWhenGetRequestAndCsrfDisabledThenRedirectsToLogin() throws Exception {
@@ -536,29 +524,6 @@ public class LogoutConfigurerTests {
}
// gh-19128
@Configuration
@EnableWebSecurity
static class LogoutBuilderBeanConfig {
@Bean
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
bean.setBasePath("/spring");
return bean;
}
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.logout(withDefaults());
// @formatter:on
return http.build();
}
}
@Configuration
@EnableWebSecurity
static class CsrfDisabledConfig {
@@ -26,7 +26,6 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.test.web.servlet.MockMvc;
@@ -67,16 +66,6 @@ public class PasswordManagementConfigurerTests {
.andExpect(redirectedUrl("/custom-change-password-page"));
}
// gh-19128
@Test
public void changePasswordWhenBuilderBeanWithBasePathThenChangePasswordUrlIgnoresBasePath() throws Exception {
this.spring.register(PasswordManagementBuilderBeanConfig.class).autowire();
this.mvc.perform(get("/.well-known/change-password"))
.andExpect(status().isFound())
.andExpect(redirectedUrl("/change-password"));
}
@Test
public void whenSettingNullChangePasswordPage() {
PasswordManagementConfigurer configurer = new PasswordManagementConfigurer();
@@ -113,29 +102,6 @@ public class PasswordManagementConfigurerTests {
}
// gh-19128
@Configuration
@EnableWebSecurity
static class PasswordManagementBuilderBeanConfig {
@Bean
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
bean.setBasePath("/spring");
return bean;
}
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
return http
.passwordManagement(withDefaults())
.build();
// @formatter:on
}
}
@Configuration
@EnableWebSecurity
static class PasswordManagementWithCustomChangePasswordPageConfig {
@@ -34,7 +34,6 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.test.web.servlet.RequestCacheResultMatcher;
@@ -186,21 +185,6 @@ public class RequestCacheConfigurerTests {
this.mvc.perform(formLogin(session)).andExpect(redirectedUrl("/"));
}
// gh-19128
@Test
public void getWhenBuilderBeanWithBasePathThenSavedRequestMatcherIgnoresBasePath() throws Exception {
this.spring.register(RequestCacheBuilderBeanConfig.class, DefaultSecurityConfig.class).autowire();
MockHttpServletRequestBuilder request = get("/messages").header(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
// @formatter:off
MockHttpSession session = (MockHttpSession) this.mvc.perform(request)
.andExpect(redirectedUrl("/login"))
.andReturn()
.getRequest()
.getSession();
// @formatter:on
this.mvc.perform(formLogin(session)).andExpect(RequestCacheResultMatcher.redirectToCachedRequest());
}
@Test
public void getWhenBookmarkedRequestIsAllMediaTypeThenPostAuthenticationRemembers() throws Exception {
this.spring.register(RequestCacheDefaultsConfig.class, DefaultSecurityConfig.class).autowire();
@@ -417,31 +401,6 @@ public class RequestCacheConfigurerTests {
}
// gh-19128
@Configuration
@EnableWebSecurity
static class RequestCacheBuilderBeanConfig {
@Bean
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
bean.setBasePath("/spring");
return bean;
}
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((requests) -> requests
.anyRequest().authenticated())
.formLogin(withDefaults());
// @formatter:on
return http.build();
}
}
@Configuration
@EnableWebSecurity
static class RequestCacheDisabledConfig {
@@ -19,13 +19,10 @@ package org.springframework.security.config.annotation.web.configurers;
import java.nio.charset.StandardCharsets;
import java.util.List;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
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.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpOutputMessage;
@@ -43,16 +40,8 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.FilterChainProxy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.ui.DefaultResourcesFilter;
import org.springframework.security.web.webauthn.api.Bytes;
import org.springframework.security.web.webauthn.api.ImmutablePublicKeyCredentialUserEntity;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialCreationOptions;
import org.springframework.security.web.webauthn.api.TestCredentialRecords;
import org.springframework.security.web.webauthn.api.TestPublicKeyCredentialCreationOptions;
import org.springframework.security.web.webauthn.authentication.WebAuthnAuthenticationFilter;
import org.springframework.security.web.webauthn.management.MapPublicKeyCredentialUserEntityRepository;
import org.springframework.security.web.webauthn.management.MapUserCredentialRepository;
import org.springframework.security.web.webauthn.management.PublicKeyCredentialUserEntityRepository;
import org.springframework.security.web.webauthn.management.UserCredentialRepository;
import org.springframework.security.web.webauthn.management.WebAuthnRelyingPartyOperations;
import org.springframework.security.web.webauthn.registration.HttpSessionPublicKeyCredentialCreationOptionsRepository;
import org.springframework.test.web.servlet.MockMvc;
@@ -63,9 +52,6 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willAnswer;
import static org.mockito.Mockito.mock;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.authentication;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@@ -102,14 +88,6 @@ public class WebAuthnConfigurerTests {
.andExpect(content().string(containsString("body {")));
}
// gh-18128
@Test
public void webAuthnAuthenticationFilterIsPostProcessed() throws Exception {
this.spring.register(DefaultWebauthnConfiguration.class, PostProcessorConfiguration.class).autowire();
PostProcessorConfiguration postProcess = this.spring.getContext().getBean(PostProcessorConfiguration.class);
assertThat(postProcess.webauthnFilter).isNotNull();
}
@Test
public void webauthnWhenNoFormLoginAndDefaultRegistrationPageConfiguredThenServesJavascript() throws Exception {
this.spring.register(NoFormLoginAndDefaultRegistrationPageConfiguration.class).autowire();
@@ -149,42 +127,6 @@ public class WebAuthnConfigurerTests {
.hasSize(1);
}
@Test
void webauthnWhenConfiguredDefaultsRpNameToRpId() throws Exception {
ObjectMapper mapper = new ObjectMapper();
this.spring.register(DefaultWebauthnConfiguration.class).autowire();
String response = this.mvc
.perform(post("/webauthn/register/options").with(csrf())
.with(authentication(new TestingAuthenticationToken("test", "ignored", "ROLE_user"))))
.andExpect(status().is2xxSuccessful())
.andReturn()
.getResponse()
.getContentAsString();
JsonNode parsedResponse = mapper.readTree(response);
assertThat(parsedResponse.get("rp").get("id").asText()).isEqualTo("example.com");
assertThat(parsedResponse.get("rp").get("name").asText()).isEqualTo("example.com");
}
@Test
void webauthnWhenRpNameConfiguredUsesRpName() throws Exception {
ObjectMapper mapper = new ObjectMapper();
this.spring.register(CustomRpNameWebauthnConfiguration.class).autowire();
String response = this.mvc
.perform(post("/webauthn/register/options").with(csrf())
.with(authentication(new TestingAuthenticationToken("test", "ignored", "ROLE_user"))))
.andExpect(status().is2xxSuccessful())
.andReturn()
.getResponse()
.getContentAsString();
JsonNode parsedResponse = mapper.readTree(response);
assertThat(parsedResponse.get("rp").get("id").asText()).isEqualTo("example.com");
assertThat(parsedResponse.get("rp").get("name").asText()).isEqualTo("Test RP Name");
}
@Test
public void webauthnWhenConfiguredAndFormLoginThenDoesServesJavascript() throws Exception {
this.spring.register(FormLoginAndNoDefaultRegistrationPageConfiguration.class).autowire();
@@ -265,24 +207,6 @@ public class WebAuthnConfigurerTests {
.andExpect(content().string(expectedBody));
}
@Test
void webauthnWhenDeleteAndCredentialBelongsToUserThenNoContent() throws Exception {
this.spring.register(DeleteCredentialConfiguration.class).autowire();
this.mvc
.perform(delete("/webauthn/register/" + DeleteCredentialConfiguration.CREDENTIAL_ID_BASE64URL)
.with(authentication(new TestingAuthenticationToken("user", "password", "ROLE_USER"))))
.andExpect(status().isNoContent());
}
@Test
void webauthnWhenDeleteAndCredentialBelongsToDifferentUserThenForbidden() throws Exception {
this.spring.register(DeleteCredentialConfiguration.class).autowire();
this.mvc
.perform(delete("/webauthn/register/" + DeleteCredentialConfiguration.CREDENTIAL_ID_BASE64URL)
.with(authentication(new TestingAuthenticationToken("other-user", "password", "ROLE_USER"))))
.andExpect(status().isForbidden());
}
@Configuration
@EnableWebSecurity
static class ConfigCredentialCreationOptionsRepository {
@@ -365,26 +289,6 @@ public class WebAuthnConfigurerTests {
}
@Configuration(proxyBeanMethods = false)
static class PostProcessorConfiguration {
WebAuthnAuthenticationFilter webauthnFilter;
@Bean
BeanPostProcessor beanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof WebAuthnAuthenticationFilter filter) {
PostProcessorConfiguration.this.webauthnFilter = filter;
}
return bean;
}
};
}
}
@Configuration
@EnableWebSecurity
static class DefaultWebauthnConfiguration {
@@ -400,7 +304,8 @@ public class WebAuthnConfigurerTests {
http
.formLogin(Customizer.withDefaults())
.webAuthn((authn) -> authn
.rpId("example.com")
.rpId("spring.io")
.rpName("spring")
);
// @formatter:on
return http.build();
@@ -408,24 +313,6 @@ public class WebAuthnConfigurerTests {
}
@Configuration
@EnableWebSecurity
static class CustomRpNameWebauthnConfiguration {
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.formLogin(Customizer.withDefaults())
.webAuthn((webauthn) -> webauthn.rpId("example.com").rpName("Test RP Name"))
.build();
}
}
@Configuration
@EnableWebSecurity
static class NoFormLoginAndDefaultRegistrationPageConfiguration {
@@ -501,47 +388,4 @@ public class WebAuthnConfigurerTests {
}
@Configuration
@EnableWebSecurity
static class DeleteCredentialConfiguration {
static final String CREDENTIAL_ID_BASE64URL = "NauGCN7bZ5jEBwThcde51g";
static final Bytes USER_ENTITY_ID = Bytes.fromBase64("vKBFhsWT3gQnn-gHdT4VXIvjDkVXVYg5w8CLGHPunMM");
@Bean
UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
@Bean
WebAuthnRelyingPartyOperations webAuthnRelyingPartyOperations() {
return mock(WebAuthnRelyingPartyOperations.class);
}
@Bean
UserCredentialRepository userCredentialRepository() {
MapUserCredentialRepository repository = new MapUserCredentialRepository();
repository.save(TestCredentialRecords.userCredential().build());
return repository;
}
@Bean
PublicKeyCredentialUserEntityRepository userEntityRepository() {
MapPublicKeyCredentialUserEntityRepository repository = new MapPublicKeyCredentialUserEntityRepository();
repository.save(ImmutablePublicKeyCredentialUserEntity.builder()
.name("user")
.id(USER_ENTITY_ID)
.displayName("User")
.build());
return repository;
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http.csrf(AbstractHttpConfigurer::disable).webAuthn(Customizer.withDefaults()).build();
}
}
}
@@ -56,7 +56,6 @@ import org.springframework.security.config.annotation.web.configurers.oauth2.cli
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
import org.springframework.security.context.DelegatingApplicationListener;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
@@ -441,18 +440,6 @@ public class OAuth2LoginConfigurerTests {
then(redirectStrategy).should().sendRedirect(any(), any(), anyString());
}
// gh-19128
@Test
public void oauth2LoginWhenBuilderBeanWithBasePathThenLoginProcessingUrlIgnoresBasePath() throws Exception {
loadConfig(OAuth2LoginBuilderBeanConfig.class);
String requestUri = "/login/oauth2/code/google";
this.request = get(requestUri).build();
this.request.setParameter("code", "code123");
this.request.setParameter("state", "state123");
this.springSecurityFilterChain.doFilter(this.request, this.response, this.filterChain);
assertThat(this.response.getRedirectedUrl()).endsWith("/login?error");
}
// gh-5347
@Test
public void oauth2LoginWithOneClientConfiguredThenRedirectForAuthorization() throws Exception {
@@ -801,31 +788,6 @@ public class OAuth2LoginConfigurerTests {
}
// gh-19128
@Configuration
@EnableWebSecurity
static class OAuth2LoginBuilderBeanConfig extends CommonSecurityFilterChainConfig {
@Bean
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
bean.setBasePath("/spring");
return bean;
}
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.oauth2Login((login) -> login
.clientRegistrationRepository(
new InMemoryClientRegistrationRepository(GOOGLE_CLIENT_REGISTRATION)));
// @formatter:on
return super.configureFilterChain(http);
}
}
@Configuration
@EnableWebSecurity
static class OAuth2LoginConfigFormLogin extends CommonSecurityFilterChainConfig {
@@ -307,8 +307,8 @@ public class OAuth2AuthorizationCodeGrantTests {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
this.mvc
.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)
.queryParams(getAuthorizationRequestParameters(registeredClient)))
.perform(
get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).params(getAuthorizationRequestParameters(registeredClient)))
.andExpect(status().isBadRequest())
.andReturn();
}
@@ -851,31 +851,21 @@ public class OAuth2AuthorizationCodeGrantTests {
this.spring.register(AuthorizationServerConfigurationCustomAuthorizationEndpoint.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build();
this.registeredClientRepository.save(registeredClient);
TestingAuthenticationToken principal = new TestingAuthenticationToken("principalName", "password");
Map<String, Object> additionalParameters = new HashMap<>();
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE);
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256");
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = new OAuth2AuthorizationCodeRequestAuthenticationToken(
"https://provider.com/oauth2/authorize", registeredClient.getClientId(), principal,
registeredClient.getRedirectUris().iterator().next(), STATE_URL_UNENCODED, registeredClient.getScopes(),
additionalParameters);
OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode("code", Instant.now(),
Instant.now().plus(5, ChronoUnit.MINUTES));
OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken(
"https://provider.com/oauth2/authorize", registeredClient.getClientId(), principal, authorizationCode,
registeredClient.getRedirectUris().iterator().next(), STATE_URL_UNENCODED,
registeredClient.getScopes());
given(authorizationRequestConverter.convert(any())).willReturn(authorizationCodeRequestAuthentication);
given(authorizationRequestConverter.convert(any())).willReturn(authorizationCodeRequestAuthenticationResult);
given(authorizationRequestAuthenticationProvider
.supports(eq(OAuth2AuthorizationCodeRequestAuthenticationToken.class))).willReturn(true);
given(authorizationRequestAuthenticationProvider.authenticate(any()))
.willReturn(authorizationCodeRequestAuthenticationResult);
this.mvc
.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI)
.queryParams(getAuthorizationRequestParameters(registeredClient))
.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).params(getAuthorizationRequestParameters(registeredClient))
.with(user("user")))
.andExpect(status().isOk());
@@ -890,7 +880,8 @@ public class OAuth2AuthorizationCodeGrantTests {
|| converter instanceof OAuth2AuthorizationCodeRequestAuthenticationConverter
|| converter instanceof OAuth2AuthorizationConsentAuthenticationConverter);
verify(authorizationRequestAuthenticationProvider).authenticate(eq(authorizationCodeRequestAuthentication));
verify(authorizationRequestAuthenticationProvider)
.authenticate(eq(authorizationCodeRequestAuthenticationResult));
@SuppressWarnings("unchecked")
ArgumentCaptor<List<AuthenticationProvider>> authenticationProvidersCaptor = ArgumentCaptor
@@ -45,7 +45,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpHeaders;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -159,8 +158,6 @@ public class OAuth2ClientCredentialsGrantTests {
private static AuthenticationFailureHandler authenticationFailureHandler;
private static PasswordEncoder passwordEncoder;
public final SpringTestContext spring = new SpringTestContext(this);
@Autowired
@@ -186,9 +183,6 @@ public class OAuth2ClientCredentialsGrantTests {
authenticationProvidersConsumer = mock(Consumer.class);
authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class);
authenticationFailureHandler = mock(AuthenticationFailureHandler.class);
passwordEncoder = mock(PasswordEncoder.class);
given(passwordEncoder.matches(any(), any())).willReturn(true);
given(passwordEncoder.upgradeEncoding(any())).willReturn(false);
db = new EmbeddedDatabaseBuilder().generateUniqueName(true)
.setType(EmbeddedDatabaseType.HSQL)
.setScriptEncoding("UTF-8")
@@ -502,26 +496,6 @@ public class OAuth2ClientCredentialsGrantTests {
.andExpect(jsonPath("$.token_type").value(OAuth2AccessToken.TokenType.DPOP.getValue()));
}
@Test
public void requestWhenTokenRequestWithMultiplePasswordEncodersThenPrimaryPasswordEncoderUsed() throws Exception {
this.spring.register(AuthorizationServerConfigurationWithMultiplePasswordEncoders.class).autowire();
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();
this.registeredClientRepository.save(registeredClient);
this.mvc
.perform(post(DEFAULT_TOKEN_ENDPOINT_URI)
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, "scope1 scope2")
.header(HttpHeaders.AUTHORIZATION,
"Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret())))
.andExpect(status().isOk())
.andExpect(jsonPath("$.access_token").isNotEmpty())
.andExpect(jsonPath("$.scope").value("scope1 scope2"));
verify(passwordEncoder).matches(any(), any());
}
private static String generateDPoPProof(String tokenEndpointUri) {
// @formatter:off
Map<String, Object> publicJwk = TestJwks.DEFAULT_EC_JWK
@@ -684,16 +658,4 @@ public class OAuth2ClientCredentialsGrantTests {
}
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class AuthorizationServerConfigurationWithMultiplePasswordEncoders extends AuthorizationServerConfiguration {
@Primary
@Bean
PasswordEncoder primaryPasswordEncoder() {
return passwordEncoder;
}
}
}
@@ -79,7 +79,6 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
import org.springframework.security.oauth2.server.authorization.OAuth2ClientRegistration;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientRegistrationAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientRegistrationAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2ClientRegistrationAuthenticationValidator;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository.RegisteredClientParametersMapper;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
@@ -412,102 +411,6 @@ public class OAuth2ClientRegistrationTests {
.isCloseTo(expectedSecretExpiryDate, allowedDelta);
}
@Test
public void requestWhenProtocolRelativeRedirectUriThenBadRequest() throws Exception {
this.spring.register(DefaultValidatorConfiguration.class).autowire();
assertThat(requestWhenInvalidClientMetadataThenBadRequest("""
{
"client_name": "client-name",
"redirect_uris": ["//client.example.com/path"],
"grant_types": ["authorization_code"]
}
""")).isEqualTo(HttpStatus.BAD_REQUEST.value());
}
@Test
public void requestWhenJavascriptSchemeRedirectUriThenBadRequest() throws Exception {
this.spring.register(DefaultValidatorConfiguration.class).autowire();
assertThat(requestWhenInvalidClientMetadataThenBadRequest("""
{
"client_name": "client-name",
"redirect_uris": ["javascript:alert(document.cookie)"],
"grant_types": ["authorization_code"]
}
""")).isEqualTo(HttpStatus.BAD_REQUEST.value());
}
@Test
public void requestWhenDataSchemeRedirectUriThenBadRequest() throws Exception {
this.spring.register(DefaultValidatorConfiguration.class).autowire();
assertThat(requestWhenInvalidClientMetadataThenBadRequest("""
{
"client_name": "client-name",
"redirect_uris": ["data:text/html,<h1>content</h1>"],
"grant_types": ["authorization_code"]
}
""")).isEqualTo(HttpStatus.BAD_REQUEST.value());
}
@Test
public void requestWhenHttpJwkSetUriThenBadRequest() throws Exception {
this.spring.register(DefaultValidatorConfiguration.class).autowire();
assertThat(requestWhenInvalidClientMetadataThenBadRequest("""
{
"client_name": "client-name",
"redirect_uris": ["https://client.example.com"],
"grant_types": ["authorization_code"],
"jwks_uri": "http://169.254.169.254/keys",
"token_endpoint_auth_method": "private_key_jwt"
}
""")).isEqualTo(HttpStatus.BAD_REQUEST.value());
}
@Test
public void requestWhenArbitraryScopeThenBadRequest() throws Exception {
this.spring.register(DefaultValidatorConfiguration.class).autowire();
assertThat(requestWhenInvalidClientMetadataThenBadRequest("""
{
"client_name": "client-name",
"redirect_uris": ["https://client.example.com"],
"grant_types": ["client_credentials"],
"scope": "read write"
}
""")).isEqualTo(HttpStatus.BAD_REQUEST.value());
}
private int requestWhenInvalidClientMetadataThenBadRequest(String json) throws Exception {
String clientRegistrationScope = "client.create";
// @formatter:off
RegisteredClient clientRegistrar = RegisteredClient.withId("client-registrar-" + System.nanoTime())
.clientId("client-registrar-" + System.nanoTime())
.clientSecret("{noop}secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.scope(clientRegistrationScope)
.build();
// @formatter:on
this.registeredClientRepository.save(clientRegistrar);
MvcResult tokenResult = this.mvc
.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, clientRegistrationScope)
.with(httpBasic(clientRegistrar.getClientId(), "secret")))
.andExpect(status().isOk())
.andReturn();
OAuth2AccessToken accessToken = readAccessTokenResponse(tokenResult.getResponse()).getAccessToken();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setBearerAuth(accessToken.getTokenValue());
MvcResult registerResult = this.mvc
.perform(post(ISSUER.concat(DEFAULT_OAUTH2_CLIENT_REGISTRATION_ENDPOINT_URI)).headers(httpHeaders)
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andReturn();
return registerResult.getResponse().getStatus();
}
private OAuth2ClientRegistration registerClient(OAuth2ClientRegistration clientRegistration) throws Exception {
// ***** (1) Obtain the "initial" access token used for registering the client
@@ -593,17 +496,6 @@ public class OAuth2ClientRegistrationTests {
return clientRegistrationHttpMessageConverter.read(OAuth2ClientRegistration.class, httpResponse);
}
private static Consumer<List<AuthenticationProvider>> scopePermissiveValidatorCustomizer() {
return (authenticationProviders) -> authenticationProviders.forEach((authenticationProvider) -> {
if (authenticationProvider instanceof OAuth2ClientRegistrationAuthenticationProvider provider) {
provider.setAuthenticationValidator(
OAuth2ClientRegistrationAuthenticationValidator.DEFAULT_REDIRECT_URI_VALIDATOR
.andThen(OAuth2ClientRegistrationAuthenticationValidator.DEFAULT_JWK_SET_URI_VALIDATOR)
.andThen(OAuth2ClientRegistrationAuthenticationValidator.SIMPLE_SCOPE_VALIDATOR));
}
});
}
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class CustomClientRegistrationConfiguration extends AuthorizationServerConfiguration {
@@ -620,7 +512,7 @@ public class OAuth2ClientRegistrationTests {
.clientRegistrationRequestConverter(authenticationConverter)
.clientRegistrationRequestConverters(authenticationConvertersConsumer)
.authenticationProvider(authenticationProvider)
.authenticationProviders(scopePermissiveValidatorCustomizer().andThen(authenticationProvidersConsumer))
.authenticationProviders(authenticationProvidersConsumer)
.clientRegistrationResponseHandler(authenticationSuccessHandler)
.errorResponseHandler(authenticationFailureHandler)
)
@@ -647,7 +539,7 @@ public class OAuth2ClientRegistrationTests {
authorizationServer
.clientRegistrationEndpoint((clientRegistration) ->
clientRegistration
.authenticationProviders(scopePermissiveValidatorCustomizer().andThen(configureClientRegistrationConverters()))
.authenticationProviders(configureClientRegistrationConverters())
)
)
.authorizeHttpRequests((authorize) ->
@@ -685,7 +577,7 @@ public class OAuth2ClientRegistrationTests {
authorizationServer
.clientRegistrationEndpoint((clientRegistration) ->
clientRegistration
.authenticationProviders(scopePermissiveValidatorCustomizer().andThen(configureClientRegistrationConverters()))
.authenticationProviders(configureClientRegistrationConverters())
)
)
.authorizeHttpRequests((authorize) ->
@@ -722,7 +614,6 @@ public class OAuth2ClientRegistrationTests {
.clientRegistrationEndpoint((clientRegistration) ->
clientRegistration
.openRegistrationAllowed(true)
.authenticationProviders(scopePermissiveValidatorCustomizer())
)
)
.authorizeHttpRequests((authorize) ->
@@ -736,30 +627,6 @@ public class OAuth2ClientRegistrationTests {
}
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class DefaultValidatorConfiguration extends AuthorizationServerConfiguration {
// Override with Customizer.withDefaults() so the default (strict)
// OAuth2ClientRegistrationAuthenticationValidator is in effect.
// @formatter:off
@Bean
@Override
SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
http
.oauth2AuthorizationServer((authorizationServer) ->
authorizationServer
.clientRegistrationEndpoint(Customizer.withDefaults())
)
.authorizeHttpRequests((authorize) ->
authorize.anyRequest().authenticated()
);
return http.build();
}
// @formatter:on
}
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class AuthorizationServerConfiguration {
@@ -770,10 +637,7 @@ public class OAuth2ClientRegistrationTests {
http
.oauth2AuthorizationServer((authorizationServer) ->
authorizationServer
.clientRegistrationEndpoint((clientRegistration) ->
clientRegistration
.authenticationProviders(scopePermissiveValidatorCustomizer())
)
.clientRegistrationEndpoint(Customizer.withDefaults())
)
.authorizeHttpRequests((authorize) ->
authorize.anyRequest().authenticated()
@@ -359,7 +359,7 @@ public class OAuth2DeviceCodeGrantTests {
}
@Test
public void requestWhenDeviceAuthorizationConsentRequestUnauthenticatedThenUnauthorized() throws Exception {
public void requestWhenDeviceAuthorizationConsentRequestUnauthenticatedThenBadRequest() throws Exception {
this.spring.register(AuthorizationServerConfiguration.class).autowire();
// @formatter:off
@@ -392,7 +392,7 @@ public class OAuth2DeviceCodeGrantTests {
// @formatter:off
this.mvc.perform(post(DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI)
.params(parameters))
.andExpect(status().isUnauthorized());
.andExpect(status().isBadRequest());
// @formatter:on
}
@@ -97,7 +97,6 @@ import org.springframework.security.oauth2.server.authorization.oidc.OidcClientR
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientConfigurationAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationProvider;
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.oidc.authentication.OidcClientRegistrationAuthenticationValidator;
import org.springframework.security.oauth2.server.authorization.oidc.converter.OidcClientRegistrationRegisteredClientConverter;
import org.springframework.security.oauth2.server.authorization.oidc.converter.RegisteredClientOidcClientRegistrationConverter;
import org.springframework.security.oauth2.server.authorization.oidc.http.converter.OidcClientRegistrationHttpMessageConverter;
@@ -546,129 +545,6 @@ public class OidcClientRegistrationTests {
.isCloseTo(expectedSecretExpiryDate, allowedDelta);
}
@Test
public void requestWhenProtocolRelativeRedirectUriThenBadRequest() throws Exception {
this.spring.register(DefaultValidatorConfiguration.class).autowire();
assertThat(requestWhenInvalidClientMetadataThenBadRequest("""
{
"client_name": "client-name",
"redirect_uris": ["//client.example.com/path"],
"grant_types": ["authorization_code"]
}
""")).isEqualTo(HttpStatus.BAD_REQUEST.value());
}
@Test
public void requestWhenJavascriptSchemeRedirectUriThenBadRequest() throws Exception {
this.spring.register(DefaultValidatorConfiguration.class).autowire();
assertThat(requestWhenInvalidClientMetadataThenBadRequest("""
{
"client_name": "client-name",
"redirect_uris": ["javascript:alert(document.cookie)"],
"grant_types": ["authorization_code"]
}
""")).isEqualTo(HttpStatus.BAD_REQUEST.value());
}
@Test
public void requestWhenDataSchemeRedirectUriThenBadRequest() throws Exception {
this.spring.register(DefaultValidatorConfiguration.class).autowire();
assertThat(requestWhenInvalidClientMetadataThenBadRequest("""
{
"client_name": "client-name",
"redirect_uris": ["data:text/html,<h1>content</h1>"],
"grant_types": ["authorization_code"]
}
""")).isEqualTo(HttpStatus.BAD_REQUEST.value());
}
@Test
public void requestWhenJavascriptSchemePostLogoutRedirectUriThenBadRequest() throws Exception {
this.spring.register(DefaultValidatorConfiguration.class).autowire();
assertThat(requestWhenInvalidClientMetadataThenBadRequest("""
{
"client_name": "client-name",
"redirect_uris": ["https://client.example.com"],
"post_logout_redirect_uris": ["javascript:alert(document.cookie)"],
"grant_types": ["authorization_code"]
}
""")).isEqualTo(HttpStatus.BAD_REQUEST.value());
}
@Test
public void requestWhenDataSchemePostLogoutRedirectUriThenBadRequest() throws Exception {
this.spring.register(DefaultValidatorConfiguration.class).autowire();
assertThat(requestWhenInvalidClientMetadataThenBadRequest("""
{
"client_name": "client-name",
"redirect_uris": ["https://client.example.com"],
"post_logout_redirect_uris": ["data:text/html,<h1>content</h1>"],
"grant_types": ["authorization_code"]
}
""")).isEqualTo(HttpStatus.BAD_REQUEST.value());
}
@Test
public void requestWhenHttpJwkSetUriThenBadRequest() throws Exception {
this.spring.register(DefaultValidatorConfiguration.class).autowire();
assertThat(requestWhenInvalidClientMetadataThenBadRequest("""
{
"client_name": "client-name",
"redirect_uris": ["https://client.example.com"],
"grant_types": ["authorization_code"],
"jwks_uri": "http://169.254.169.254/keys",
"token_endpoint_auth_method": "private_key_jwt"
}
""")).isEqualTo(HttpStatus.BAD_REQUEST.value());
}
@Test
public void requestWhenArbitraryScopeThenBadRequest() throws Exception {
this.spring.register(DefaultValidatorConfiguration.class).autowire();
assertThat(requestWhenInvalidClientMetadataThenBadRequest("""
{
"client_name": "client-name",
"redirect_uris": ["https://client.example.com"],
"grant_types": ["authorization_code"],
"scope": "read write"
}
""")).isEqualTo(HttpStatus.BAD_REQUEST.value());
}
private int requestWhenInvalidClientMetadataThenBadRequest(String json) throws Exception {
String clientRegistrationScope = "client.create";
String clientId = "client-registrar-" + System.nanoTime();
// @formatter:off
RegisteredClient clientRegistrar = RegisteredClient.withId(clientId)
.clientId(clientId)
.clientSecret("{noop}secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.scope(clientRegistrationScope)
.build();
// @formatter:on
this.registeredClientRepository.save(clientRegistrar);
MvcResult tokenResult = this.mvc
.perform(post(ISSUER.concat(DEFAULT_TOKEN_ENDPOINT_URI))
.param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())
.param(OAuth2ParameterNames.SCOPE, clientRegistrationScope)
.with(httpBasic(clientId, "secret")))
.andExpect(status().isOk())
.andReturn();
OAuth2AccessToken accessToken = readAccessTokenResponse(tokenResult.getResponse()).getAccessToken();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setBearerAuth(accessToken.getTokenValue());
MvcResult registerResult = this.mvc
.perform(post(ISSUER.concat(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI)).headers(httpHeaders)
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andReturn();
return registerResult.getResponse().getStatus();
}
private OidcClientRegistration registerClient(OidcClientRegistration clientRegistration) throws Exception {
// ***** (1) Obtain the "initial" access token used for registering the client
@@ -766,18 +642,6 @@ public class OidcClientRegistrationTests {
return clientRegistrationHttpMessageConverter.read(OidcClientRegistration.class, httpResponse);
}
private static Consumer<List<AuthenticationProvider>> scopePermissiveValidatorCustomizer() {
return (authenticationProviders) -> authenticationProviders.forEach((authenticationProvider) -> {
if (authenticationProvider instanceof OidcClientRegistrationAuthenticationProvider provider) {
provider.setAuthenticationValidator(
OidcClientRegistrationAuthenticationValidator.DEFAULT_REDIRECT_URI_VALIDATOR.andThen(
OidcClientRegistrationAuthenticationValidator.DEFAULT_POST_LOGOUT_REDIRECT_URI_VALIDATOR)
.andThen(OidcClientRegistrationAuthenticationValidator.DEFAULT_JWK_SET_URI_VALIDATOR)
.andThen(OidcClientRegistrationAuthenticationValidator.SIMPLE_SCOPE_VALIDATOR));
}
});
}
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class CustomClientRegistrationConfiguration extends AuthorizationServerConfiguration {
@@ -796,7 +660,7 @@ public class OidcClientRegistrationTests {
.clientRegistrationRequestConverter(authenticationConverter)
.clientRegistrationRequestConverters(authenticationConvertersConsumer)
.authenticationProvider(authenticationProvider)
.authenticationProviders(scopePermissiveValidatorCustomizer().andThen(authenticationProvidersConsumer))
.authenticationProviders(authenticationProvidersConsumer)
.clientRegistrationResponseHandler(authenticationSuccessHandler)
.errorResponseHandler(authenticationFailureHandler)
)
@@ -826,7 +690,7 @@ public class OidcClientRegistrationTests {
oidc
.clientRegistrationEndpoint((clientRegistration) ->
clientRegistration
.authenticationProviders(scopePermissiveValidatorCustomizer().andThen(configureClientRegistrationConverters()))
.authenticationProviders(configureClientRegistrationConverters())
)
)
)
@@ -867,7 +731,7 @@ public class OidcClientRegistrationTests {
oidc
.clientRegistrationEndpoint((clientRegistration) ->
clientRegistration
.authenticationProviders(scopePermissiveValidatorCustomizer().andThen(configureClientRegistrationConverters()))
.authenticationProviders(configureClientRegistrationConverters())
)
)
)
@@ -891,33 +755,6 @@ public class OidcClientRegistrationTests {
}
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class DefaultValidatorConfiguration extends AuthorizationServerConfiguration {
// Override with Customizer.withDefaults() so the default (strict)
// OidcClientRegistrationAuthenticationValidator is in effect.
// @formatter:off
@Bean
@Override
SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
http
.oauth2AuthorizationServer((authorizationServer) ->
authorizationServer
.oidc((oidc) ->
oidc
.clientRegistrationEndpoint(Customizer.withDefaults())
)
)
.authorizeHttpRequests((authorize) ->
authorize.anyRequest().authenticated()
);
return http.build();
}
// @formatter:on
}
@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
static class AuthorizationServerConfiguration {
@@ -930,10 +767,7 @@ public class OidcClientRegistrationTests {
authorizationServer
.oidc((oidc) ->
oidc
.clientRegistrationEndpoint((clientRegistration) ->
clientRegistration
.authenticationProviders(scopePermissiveValidatorCustomizer())
)
.clientRegistrationEndpoint(Customizer.withDefaults())
)
)
.authorizeHttpRequests((authorize) ->
@@ -60,7 +60,6 @@ import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.FactorGrantedAuthority;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
@@ -211,8 +210,7 @@ public class OidcTests {
registeredClient);
MvcResult mvcResult = this.mvc
.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)
.with(user("user").roles("A", "B")
.authorities(FactorGrantedAuthority.fromAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY))))
.with(user("user").roles("A", "B")))
.andExpect(status().is3xxRedirection())
.andReturn();
String redirectedUrl = mvcResult.getResponse().getRedirectedUrl();
@@ -272,8 +270,7 @@ public class OidcTests {
registeredClient);
MvcResult mvcResult = this.mvc
.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)
.with(user("user").roles("A", "B")
.authorities(FactorGrantedAuthority.fromAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY))))
.with(user("user").roles("A", "B")))
.andExpect(status().is3xxRedirection())
.andReturn();
String redirectedUrl = mvcResult.getResponse().getRedirectedUrl();
@@ -338,8 +335,7 @@ public class OidcTests {
registeredClient);
MvcResult mvcResult = this.mvc
.perform(get(issuer.concat(DEFAULT_AUTHORIZATION_ENDPOINT_URI)).queryParams(authorizationRequestParameters)
.with(user("user")
.authorities(FactorGrantedAuthority.fromAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY))))
.with(user("user")))
.andExpect(status().is3xxRedirection())
.andReturn();
@@ -392,8 +388,7 @@ public class OidcTests {
registeredClient1);
MvcResult mvcResult = this.mvc
.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)
.with(user("user1")
.authorities(FactorGrantedAuthority.fromAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY))))
.with(user("user1")))
.andExpect(status().is3xxRedirection())
.andReturn();
@@ -429,8 +424,7 @@ public class OidcTests {
authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient2);
mvcResult = this.mvc
.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)
.with(user("user2")
.authorities(FactorGrantedAuthority.fromAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY))))
.with(user("user2")))
.andExpect(status().is3xxRedirection())
.andReturn();
@@ -503,8 +497,7 @@ public class OidcTests {
registeredClient);
MvcResult mvcResult = this.mvc
.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)
.with(user("user")
.authorities(FactorGrantedAuthority.fromAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY))))
.with(user("user")))
.andExpect(status().is3xxRedirection())
.andReturn();
String redirectedUrl = mvcResult.getResponse().getRedirectedUrl();
@@ -544,8 +537,7 @@ public class OidcTests {
registeredClient);
MvcResult mvcResult = this.mvc
.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters)
.with(user("user")
.authorities(FactorGrantedAuthority.fromAuthority(FactorGrantedAuthority.PASSWORD_AUTHORITY))))
.with(user("user")))
.andExpect(status().is3xxRedirection())
.andReturn();
String redirectedUrl = mvcResult.getResponse().getRedirectedUrl();
@@ -40,7 +40,6 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
import org.springframework.security.core.userdetails.PasswordEncodedUser;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@@ -99,19 +98,6 @@ public class OneTimeTokenLoginConfigurerTests {
.andExpectAll(status().isFound(), redirectedUrl("/"), authenticated());
}
// gh-19128
@Test
void oneTimeTokenWhenBuilderBeanWithBasePathThenGenerateAndLoginUrlsIgnoreBasePath() throws Exception {
this.spring.register(OneTimeTokenBuilderBeanConfig.class).autowire();
this.mvc.perform(post("/ott/generate").param("username", "user").with(csrf()))
.andExpectAll(status().isFound(), redirectedUrl("/login/ott"));
String token = getLastToken().getTokenValue();
this.mvc.perform(post("/login/ott").param("token", token).with(csrf()))
.andExpectAll(status().isFound(), redirectedUrl("/"), authenticated());
}
@Test
void oneTimeTokenWhenDifferentAuthenticationUrlsThenCanAuthenticate() throws Exception {
this.spring.register(OneTimeTokenDifferentUrlsConfig.class).autowire();
@@ -310,41 +296,6 @@ public class OneTimeTokenLoginConfigurerTests {
}
// gh-19128
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@Import(UserDetailsServiceConfig.class)
static class OneTimeTokenBuilderBeanConfig {
@Bean
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
bean.setBasePath("/spring");
return bean;
}
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http,
OneTimeTokenGenerationSuccessHandler ottSuccessHandler) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.oneTimeTokenLogin((ott) -> ott
.tokenGenerationSuccessHandler(ottSuccessHandler)
);
// @formatter:on
return http.build();
}
@Bean
TestOneTimeTokenGenerationSuccessHandler ottSuccessHandler() {
return new TestOneTimeTokenGenerationSuccessHandler();
}
}
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
@Import(UserDetailsServiceConfig.class)
@@ -57,7 +57,6 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
@@ -245,14 +244,6 @@ public class Saml2LoginConfigurerTests {
performSaml2Login("ROLE_AUTH_MANAGER");
}
// gh-19128
@Test
public void saml2LoginWhenBuilderBeanWithBasePathThenAuthenticateUriIgnoresBasePath() throws Exception {
this.spring.register(Saml2LoginBuilderBeanConfig.class).autowire();
MvcResult result = this.mvc.perform(get("/saml2/authenticate/registration-id")).andReturn();
assertThat(result.getResponse().getRedirectedUrl()).contains("SAMLRequest");
}
@Test
public void authenticationRequestWhenAuthenticationRequestResolverBeanThenUses() throws Exception {
this.spring.register(CustomAuthenticationRequestResolverBean.class).autowire();
@@ -531,33 +522,6 @@ public class Saml2LoginConfigurerTests {
}
// gh-19128
@Configuration
@EnableWebSecurity
@Import(Saml2LoginConfigBeans.class)
static class Saml2LoginBuilderBeanConfig {
@Bean
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
bean.setBasePath("/spring");
return bean;
}
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authz) -> authz
.anyRequest().authenticated()
)
.saml2Login(Customizer.withDefaults());
// @formatter:on
return http.build();
}
}
@Configuration
@EnableWebSecurity
@Import(Saml2LoginConfigBeans.class)
@@ -49,7 +49,6 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.saml2.core.Saml2Utils;
@@ -197,17 +196,6 @@ public class Saml2LogoutConfigurerTests {
verify(logoutHandler).logout(any(), any(), any());
}
// gh-19128
@Test
public void saml2LogoutWhenBuilderBeanWithBasePathThenLogoutUrlIgnoresBasePath() throws Exception {
this.spring.register(Saml2LogoutBuilderBeanConfig.class).autowire();
MvcResult result = this.mvc.perform(post("/logout").with(authentication(this.user)).with(csrf()))
.andExpect(status().isFound())
.andReturn();
String location = result.getResponse().getHeader("Location");
assertThat(location).startsWith("https://ap.example.org/logout/saml2/request");
}
@Test
public void saml2LogoutWhenUnauthenticatedThenEntryPoint() throws Exception {
this.spring.register(Saml2LogoutDefaultsConfig.class).autowire();
@@ -589,32 +577,6 @@ public class Saml2LogoutConfigurerTests {
}
// gh-19128
@Configuration
@EnableWebSecurity
@Import(Saml2LoginConfigBeans.class)
static class Saml2LogoutBuilderBeanConfig {
@Bean
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
bean.setBasePath("/spring");
return bean;
}
@Bean
SecurityFilterChain web(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
.saml2Login(withDefaults())
.saml2Logout(withDefaults());
// @formatter:on
return http.build();
}
}
@Configuration
@EnableWebSecurity
@Import(Saml2LoginConfigBeans.class)
@@ -30,7 +30,6 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
import org.springframework.security.saml2.provider.service.metadata.OpenSaml5MetadataResolver;
import org.springframework.security.saml2.provider.service.metadata.RequestMatcherMetadataResponseResolver;
import org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponse;
@@ -125,15 +124,6 @@ public class Saml2MetadataConfigurerTests {
this.mvc.perform(get("/saml2/metadata")).andExpect(status().isForbidden());
}
// gh-19128
@Test
void saml2MetadataWhenBuilderBeanWithBasePathThenMetadataUrlIgnoresBasePath() throws Exception {
this.spring.register(MetadataBuilderBeanConfig.class).autowire();
this.mvc.perform(get("/saml/metadata"))
.andExpect(status().isOk())
.andExpect(content().string(containsString("md:EntityDescriptor")));
}
@EnableWebSecurity
@Configuration
@Import(RelyingPartyRegistrationConfig.class)
@@ -151,31 +141,6 @@ public class Saml2MetadataConfigurerTests {
}
// gh-19128
@EnableWebSecurity
@Configuration
@Import(RelyingPartyRegistrationConfig.class)
static class MetadataBuilderBeanConfig {
@Bean
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
bean.setBasePath("/spring");
return bean;
}
@Bean
SecurityFilterChain filters(HttpSecurity http) throws Exception {
// @formatter:off
http
.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
.saml2Metadata((saml2) -> saml2.metadataUrl("/saml/metadata"));
return http.build();
// @formatter:on
}
}
@EnableWebSecurity
@Configuration
@Import(RelyingPartyRegistrationConfig.class)
@@ -107,7 +107,7 @@ public class ServerHttpSecurityConfigurationTests {
}
@Test
public void loadConfigWhenReactiveUserAuthenticationServiceConfiguredThenServerHttpSecurityExists() {
public void loadConfigWhenReactiveUserDetailsServiceConfiguredThenServerHttpSecurityExists() {
this.spring
.register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class,
WebFluxSecurityConfiguration.class)
@@ -116,16 +116,6 @@ public class ServerHttpSecurityConfigurationTests {
assertThat(serverHttpSecurity).isNotNull();
}
@Test
public void loadConfigWhenOnlyReactiveUserDetailsServiceConfiguredThenServerHttpSecurityExists() {
this.spring
.register(ServerHttpSecurityConfiguration.class, ReactiveUserDetailsServiceOnlyTestConfiguration.class,
WebFluxSecurityConfiguration.class)
.autowire();
ServerHttpSecurity serverHttpSecurity = this.spring.getContext().getBean(ServerHttpSecurity.class);
assertThat(serverHttpSecurity).isNotNull();
}
@Test
public void loadConfigWhenProxyingEnabledAndSubclassThenServerHttpSecurityExists() {
this.spring
@@ -591,14 +581,4 @@ public class ServerHttpSecurityConfigurationTests {
}
@Configuration(proxyBeanMethods = false)
static class ReactiveUserDetailsServiceOnlyTestConfiguration {
@Bean
static ReactiveUserDetailsService userDetailsService() {
return (username) -> Mono.just(PasswordEncodedUser.user());
}
}
}
@@ -28,17 +28,14 @@ import jakarta.servlet.http.HttpSession;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.session.SessionLimit;
import org.springframework.security.web.header.HeaderWriterFilter;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
@@ -153,16 +150,6 @@ public class HttpHeadersConfigTests {
// @formatter:on
}
@Test
public void requestWhenHeadersEagerlyConfiguredThenHeadersAreWritten() throws Exception {
this.spring.configLocations(this.xml("HeadersEagerlyConfigured")).autowire();
// @formatter:off
this.mvc.perform(get("/").secure(true))
.andExpect(status().isOk())
.andExpect(includesDefaults());
// @formatter:on
}
@Test
public void requestWhenFrameOptionsConfiguredThenIncludesHeader() throws Exception {
Map<String, String> headers = new HashMap<>(defaultHeaders);
@@ -968,18 +955,6 @@ public class HttpHeadersConfigTests {
}
public static class EagerHeadersBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof HeaderWriterFilter headerWriterFilter) {
headerWriterFilter.setShouldWriteHeadersEagerly(true);
}
return bean;
}
}
public static class CustomSessionLimit implements SessionLimit {
@Override
@@ -314,78 +314,6 @@ public class InterceptUrlConfigTests {
.autowire());
}
@Test
public void requestWhenUsingDefaultMatcherAndServletPathThenAuthorizesRequestsAccordingly() throws Exception {
this.spring.configLocations(this.xml("DefaultMatcherServletPath")).autowire();
// @formatter:off
this.mvc.perform(get("/spring/path").with(userCredentials()))
.andExpect(status().isForbidden());
this.mvc.perform(get("/path").with(userCredentials()))
.andExpect(status().isOk());
// @formatter:on
}
@Test
public void requestWhenUsingDefaultMatcherAndServletPathAndAuthorizationManagerThenAuthorizesRequestsAccordingly()
throws Exception {
this.spring.configLocations(this.xml("DefaultMatcherServletPathAuthorizationManager")).autowire();
// @formatter:off
this.mvc.perform(get("/spring/path").with(userCredentials()))
.andExpect(status().isForbidden());
this.mvc.perform(get("/path").with(userCredentials()))
.andExpect(status().isOk());
// @formatter:on
assertThat(this.spring.getContext().getBean(AuthorizationManager.class)).isNotNull();
}
@Test
public void requestWhenUsingRegexMatcherThenAuthorizesRequestsAccordingly() throws Exception {
this.spring.configLocations(this.xml("RegexMatcher")).autowire();
// @formatter:off
this.mvc.perform(get("/path").with(userCredentials()))
.andExpect(status().isForbidden());
this.mvc.perform(get("/other").with(userCredentials()))
.andExpect(status().isNotFound());
// @formatter:on
}
@Test
public void requestWhenUsingRegexMatcherAndAuthorizationManagerThenAuthorizesRequestsAccordingly()
throws Exception {
this.spring.configLocations(this.xml("RegexMatcherAuthorizationManager")).autowire();
// @formatter:off
this.mvc.perform(get("/path").with(userCredentials()))
.andExpect(status().isForbidden());
this.mvc.perform(get("/other").with(userCredentials()))
.andExpect(status().isNotFound());
// @formatter:on
assertThat(this.spring.getContext().getBean(AuthorizationManager.class)).isNotNull();
}
@Test
public void requestWhenUsingCiRegexMatcherThenAuthorizesRequestsAccordingly() throws Exception {
this.spring.configLocations(this.xml("CiRegexMatcher")).autowire();
// @formatter:off
this.mvc.perform(get("/path").with(userCredentials()))
.andExpect(status().isForbidden());
this.mvc.perform(get("/PATH").with(userCredentials()))
.andExpect(status().isForbidden());
// @formatter:on
}
@Test
public void requestWhenUsingCiRegexMatcherAndAuthorizationManagerThenAuthorizesRequestsAccordingly()
throws Exception {
this.spring.configLocations(this.xml("CiRegexMatcherAuthorizationManager")).autowire();
// @formatter:off
this.mvc.perform(get("/path").with(userCredentials()))
.andExpect(status().isForbidden());
this.mvc.perform(get("/PATH").with(userCredentials()))
.andExpect(status().isForbidden());
// @formatter:on
assertThat(this.spring.getContext().getBean(AuthorizationManager.class)).isNotNull();
}
@Test
public void requestWhenUsingFilterAllDispatcherTypesAndAuthorizationManagerThenAuthorizesRequestsAccordingly()
throws Exception {
@@ -275,7 +275,7 @@ class ServerJwtDslTests {
}
class NullConverter: Converter<Jwt, Mono<AbstractAuthenticationToken>> {
override fun convert(source: Jwt): Mono<AbstractAuthenticationToken> {
override fun convert(source: Jwt): Mono<AbstractAuthenticationToken>? {
return Mono.empty()
}
@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2004-present the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http auto-config="true">
<headers/>
<intercept-url pattern="/**" access="permitAll"/>
</http>
<b:bean class="org.springframework.security.config.http.HttpHeadersConfigTests.EagerHeadersBeanPostProcessor"/>
<b:bean name="simple" class="org.springframework.security.config.http.HttpHeadersConfigTests.SimpleController"/>
<b:import resource="userservice.xml"/>
</b:beans>
@@ -24,7 +24,7 @@
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http request-matcher="path" use-authorization-manager="false">
<http request-matcher="ant" use-authorization-manager="false">
<intercept-url pattern="/path" access="denyAll" servlet-path="/spring"/>
<http-basic/>
</http>
@@ -24,7 +24,7 @@
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http request-matcher="path">
<http request-matcher="ant">
<intercept-url pattern="/path" access="denyAll" servlet-path="/spring"/>
<http-basic/>
</http>
@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2004-present the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http request-matcher="ciRegex" use-authorization-manager="false">
<intercept-url pattern="\A/PATH\Z" access="denyAll"/>
<intercept-url pattern="\A/.*\Z" access="permitAll"/>
<http-basic/>
</http>
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
<b:import resource="userservice.xml"/>
</b:beans>
@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2004-present the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http request-matcher="ciRegex">
<intercept-url pattern="\A/PATH\Z" access="denyAll"/>
<intercept-url pattern="\A/.*\Z" access="permitAll"/>
<http-basic/>
</http>
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
<b:import resource="userservice.xml"/>
</b:beans>
@@ -26,11 +26,8 @@
<http use-authorization-manager="false">
<intercept-url pattern="/path" access="denyAll" servlet-path="/spring"/>
<intercept-url pattern="/**" access="permitAll"/>
<http-basic/>
</http>
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
<b:import resource="userservice.xml"/>
</b:beans>
@@ -26,11 +26,8 @@
<http>
<intercept-url pattern="/path" access="denyAll" servlet-path="/spring"/>
<intercept-url pattern="/**" access="permitAll"/>
<http-basic/>
</http>
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
<b:import resource="userservice.xml"/>
</b:beans>
@@ -27,7 +27,7 @@
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<http auto-config="true" request-matcher="path" use-authorization-manager="false">
<http auto-config="true" request-matcher="mvc" use-authorization-manager="false">
<intercept-url pattern="/path" access="denyAll"/>
<http-basic/>
</http>
@@ -27,7 +27,7 @@
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<http auto-config="true" request-matcher="path">
<http auto-config="true" request-matcher="mvc">
<intercept-url pattern="/path" access="denyAll"/>
<http-basic/>
</http>
@@ -27,7 +27,7 @@
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<http auto-config="true" request-matcher="path" use-authorization-manager="false">
<http auto-config="true" request-matcher="mvc" use-authorization-manager="false">
<intercept-url pattern="/path" access="denyAll" servlet-path="/spring"/>
<http-basic/>
</http>
@@ -27,7 +27,7 @@
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<http auto-config="true" request-matcher="path">
<http auto-config="true" request-matcher="mvc">
<intercept-url pattern="/path" access="denyAll" servlet-path="/spring"/>
<http-basic/>
</http>
@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2004-present the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http request-matcher="regex" use-authorization-manager="false">
<intercept-url pattern="\A/path\Z" access="denyAll"/>
<intercept-url pattern="\A/.*\Z" access="permitAll"/>
<http-basic/>
</http>
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
<b:import resource="userservice.xml"/>
</b:beans>
@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2004-present the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<b:beans xmlns:b="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/security
https://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<http request-matcher="regex">
<intercept-url pattern="\A/path\Z" access="denyAll"/>
<intercept-url pattern="\A/.*\Z" access="permitAll"/>
<http-basic/>
</http>
<b:bean name="path" class="org.springframework.security.config.http.InterceptUrlConfigTests.PathController"/>
<b:import resource="userservice.xml"/>
</b:beans>

Some files were not shown because too many files have changed in this diff Show More