1
0
mirror of synced 2026-07-10 13:20:02 +00:00

Compare commits

...

95 Commits

Author SHA1 Message Date
github-actions[bot] 5b759a3e43 Release 6.0.1 2022-12-19 15:22:50 +00:00
Marcus Da Coregio f1824f8a5d Merge branch '5.8.x' into 6.0.x
Closes gh-12435
2022-12-19 10:45:25 -03:00
Guillaume Husta 36d83f863a Fix Javadoc since tag for class ExpressionAuthorizationDecision
Closes gh-12411
2022-12-19 10:44:36 -03:00
Marcus Da Coregio 334354f6dd Update org.springframework to 6.0.3
Closes gh-12426
2022-12-16 14:00:06 -03:00
Marcus Da Coregio 97a7b4627c Update slf4j-api to 2.0.6
Closes gh-12425
2022-12-16 14:00:05 -03:00
Marcus Da Coregio bf0ddd8ad6 Update org.jetbrains.kotlin to 1.7.22
Closes gh-12424
2022-12-16 14:00:05 -03:00
Marcus Da Coregio ad22ef71d3 Update hibernate-core to 6.1.6.Final
Closes gh-12423
2022-12-16 14:00:05 -03:00
Marcus Da Coregio 8a30e08349 Update org.eclipse.jetty to 11.0.13
Closes gh-12422
2022-12-16 14:00:05 -03:00
Marcus Da Coregio 4507072b69 Update httpclient to 4.5.14
Closes gh-12421
2022-12-16 14:00:05 -03:00
Marcus Da Coregio e1d61aff62 Update io.projectreactor to 2022.0.1
Closes gh-12419
2022-12-16 14:00:01 -03:00
Marcus Da Coregio c1fc51c2c0 Update mockk to 1.13.3
Closes gh-12418
2022-12-16 13:54:56 -03:00
Marcus Da Coregio 38571d7ce9 Update micrometer-observation to 1.10.2
Closes gh-12417
2022-12-16 13:54:53 -03:00
Marcus Da Coregio 9d18e9f616 Update unboundid-ldapsdk to 6.0.7
Closes gh-12416
2022-12-16 13:54:50 -03:00
Marcus Da Coregio a3c3b627e4 Update jackson-bom to 2.14.1
Closes gh-12413
2022-12-16 13:54:41 -03:00
Marcus Da Coregio 6e1909ec72 Update logback-classic to 1.4.5
Closes gh-12412
2022-12-16 13:54:38 -03:00
Steve Riesenberg d0f338c92a Merge branch '5.8.x' into 6.0.x
Closes gh-12409
2022-12-15 15:53:00 -06:00
Steve Riesenberg 5818201e9e Merge branch '5.7.x' into 5.8.x
Closes gh-12408
2022-12-15 15:52:35 -06:00
Steve Riesenberg 2461d00f4d Merge branch '5.6.x' into 5.7.x
Closes gh-12407
2022-12-15 15:52:02 -06:00
Dayan Kodippily eec6729125 Fix DefaultLdapAuthoritiesPopulator NPE
Closes gh-12090
2022-12-15 15:51:07 -06:00
Dan Allen 6bb36f1a54 sync and refactor docs build
- add 6.0.x to release scheduler workflow
- upgrade Antora to 3.2.0-alpha.2
- remove Antora patch to support linked worktrees
- upgrade Antora Collector to latest alpha
- remove dispatch.sh script (handled by gh run workflow)
- consistently use gh run workflow to trigger workflows
- remove step to publish generated docs (no longer in use)
2022-12-15 15:21:07 -06:00
Steve Riesenberg ed657a8fac Polish gh-12280
Apply editing changes from gh-9668
2022-12-15 14:18:24 -06:00
Wilson-Emmanuel edd1915d1b Corrected errors on the ACLS document
Closes gh-12270
2022-12-15 14:16:55 -06:00
Marcus Da Coregio 582c9c6438 Merge branch '5.8.x' into 6.0.x 2022-12-15 15:24:01 -03:00
Marcus Da Coregio ae22b7537e Merge branch '5.7.x' into 5.8.x 2022-12-15 15:23:42 -03:00
Marcus Da Coregio fc9c2513d8 Merge branch '5.6.x' into 5.7.x
Closes gh-dry-run
2022-12-15 15:23:13 -03:00
Marcus Da Coregio f75d35f5a4 Update org.jetbrains.kotlin to 1.7.22
Closes gh-12405
2022-12-15 15:22:08 -03:00
Marcus Da Coregio 195695fb57 Update org.eclipse.jetty to 9.4.50.v20221201
Closes gh-12404
2022-12-15 15:22:05 -03:00
Marcus Da Coregio 143944619b Update httpclient to 4.5.14
Closes gh-12403
2022-12-15 15:22:02 -03:00
Marcus Da Coregio aaa611c5b0 Update io.projectreactor to 2020.0.26
Closes gh-12401
2022-12-15 15:21:55 -03:00
Marcus Da Coregio d2659ddf82 Update mockk to 1.13.3
Closes gh-12400
2022-12-15 15:21:52 -03:00
Marcus Da Coregio 1f3c4707f3 Update org.springframework.data to 2021.2.6
Closes gh-12399
2022-12-15 15:11:54 -03:00
Marcus Da Coregio 579fc565ae Update org.springframework to 5.3.24
Closes gh-12398
2022-12-15 15:11:49 -03:00
Marcus Da Coregio 5cf23585c0 Update hibernate-entitymanager to 5.6.14.Final
Closes gh-12397
2022-12-15 15:11:41 -03:00
Marcus Da Coregio 50d793eab2 Update org.eclipse.jetty to 9.4.50.v20221201
Closes gh-12396
2022-12-15 15:11:36 -03:00
Marcus Da Coregio c715df687a Update httpclient to 4.5.14
Closes gh-12395
2022-12-15 15:11:30 -03:00
Marcus Da Coregio 4ddecdbe36 Update io.projectreactor to 2020.0.26
Closes gh-12393
2022-12-15 15:11:22 -03:00
Marcus Da Coregio 94c8830f76 Update jackson-bom to 2.13.4.20221013
Closes gh-12391
2022-12-15 15:11:12 -03:00
Marcus Da Coregio 885f1d3165 Update org.springframework.data to 2021.1.10
Closes gh-12390
2022-12-15 14:59:27 -03:00
Marcus Da Coregio e414d2bd5c Update org.springframework to 5.3.24
Closes gh-12389
2022-12-15 14:59:27 -03:00
Marcus Da Coregio dbcc3130ee Update hibernate-entitymanager to 5.6.14.Final
Closes gh-12388
2022-12-15 14:59:27 -03:00
Marcus Da Coregio ac9c398fea Update org.eclipse.jetty to 9.4.50.v20221201
Closes gh-12387
2022-12-15 14:59:26 -03:00
Marcus Da Coregio 7c428fb75e Update httpclient to 4.5.14
Closes gh-12386
2022-12-15 14:59:26 -03:00
Marcus Da Coregio aa1934ee95 Update io.projectreactor to 2020.0.26
Closes gh-12384
2022-12-15 14:59:26 -03:00
Marcus Da Coregio 58e8fe837f Update jackson-bom to 2.13.4.20221013
Closes gh-12381
2022-12-15 14:58:07 -03:00
Marcus Da Coregio 7080ea652f Add hints for ProxyFactoryBean AuthenticationManager
Closes gh-12367
2022-12-14 10:16:04 -03:00
Marcus Da Coregio 898c36287c Merge branch '5.8.x' into 6.0.x
Closes gh-12368
2022-12-12 16:55:14 -03:00
Marcus Da Coregio 99d6d21554 Apply SecurityContextHolderFilter to all dispatcher types
Closes gh-11962
2022-12-12 11:45:24 -08:00
Marcus Da Coregio 8ba7c0d00d Merge branch '5.8.x' into 6.0.x
Closes gh-12354
2022-12-07 10:23:38 -08:00
Marcus Da Coregio 88d50a531b Add EnableWebSecurity migration steps to 5.8 guide
Closes gh-12334
2022-12-07 10:22:54 -08:00
Marcus Da Coregio f1698ec188 Fix removed code by merge 2022-12-05 14:57:28 -08:00
Marcus Da Coregio 2fdf762726 Merge branch '5.8.x' into 6.0.x 2022-12-05 14:41:59 -08:00
Marcus Da Coregio 7aaa25b88e Merge branch '5.7.x' into 5.8.x 2022-12-05 14:40:54 -08:00
Marcus Da Coregio fc25b87967 Merge branch '5.6.x' into 5.7.x 2022-12-05 14:40:38 -08:00
Mitja Kotnik f39f215140 Replace javadoc with SecurityFilterChain bean definition 2022-12-05 14:40:05 -08:00
Sellami 626e53d121 Fix: Replace tenantRepository with tenants 2022-12-05 14:31:24 -08:00
Guillaume Husta a5464ed819 Fix typo in DefaultLoginPageConfigurer Javadoc
'isLogoutRequest' seems to have nothing to do here.
2022-12-05 14:31:15 -08:00
Marcus Da Coregio f487f859ed Merge branch '5.8.x' into 6.0.x 2022-12-05 14:13:33 -08:00
Marcus Da Coregio 7439d5d2de Revert "Fix typo"
This reverts commit 707f8286f8.
2022-12-05 14:13:14 -08:00
Marcus Da Coregio 4ee6149a4b Merge branch '5.8.x' into 6.0.x 2022-12-05 14:11:09 -08:00
heowc 707f8286f8 Fix typo 2022-12-05 14:09:41 -08:00
Marcus Da Coregio 42a00e2003 Merge branch '5.8.x' into 6.0.x
Closes gh-12341
2022-12-05 12:26:00 -08:00
Marcus Da Coregio d2b33a2583 Fix docs
Closes gh-11396
2022-12-05 12:25:26 -08:00
Josh Cummings 813179931a Disable Some R2dbc Tests
Issue gh-12339
2022-12-05 11:13:15 -07:00
Josh Cummings de933f5bee Merge branch '5.8.x' into 6.0.x 2022-12-02 14:14:02 -07:00
Michael Schneider 74e8fa10a2 Fix password encoder migration guide 2022-12-02 14:12:47 -07:00
Josh Cummings b22bc42bb0 Merge branch '5.8.x' into 6.0.x
Closes gh-12325
2022-11-30 14:50:51 -07:00
Josh Cummings 29c00905ce Merge branch '5.7.x' into 5.8.x
Closes gh-12324
2022-11-30 14:49:26 -07:00
Josh Cummings 667cab6cda Merge branch '5.6.x' into 5.7.x
Closes gh-12323
2022-11-30 14:38:16 -07:00
이경욱 52c7141aac Save Request Before Response Is Committed
Specifically important for cookie-based authorization request
repositories.

Closes gh-11602
2022-11-30 14:33:08 -07:00
Josh Cummings a76b1f7a51 Merge branch '5.8.x' into 6.0.x 2022-11-30 14:19:38 -07:00
Josh Cummings 68a344d238 Merge branch '5.7.x' into 5.8.x 2022-11-30 14:18:59 -07:00
Josh Cummings e23c1cf7a7 Merge branch '5.6.x' into 5.7.x 2022-11-30 14:18:12 -07:00
Josh Cummings 14a48ea939 Fix formatting
Issue gh-12143
2022-11-29 20:15:13 -07:00
Junsung Cho 709de43e89 Fix typo in JavaDoc
Closes gh-12143
2022-11-29 20:15:12 -07:00
Josh Cummings c60c10792c Fix Observability Opt-out Documentation Typo
Issue gh-12268
2022-11-29 16:24:57 -07:00
Josh Cummings 701f754e37 Cast FilterChainObservationContext Safely
Closes gh-12268
2022-11-29 16:24:56 -07:00
Marcus Da Coregio 162ed5c61c Merge branch '5.8.x' into 6.0.x
Closes gh-12302
2022-11-25 10:19:07 -03:00
Marc Becker 50da5b6498 Fix securityMatchers code sample
Closes gh-12296
2022-11-25 10:18:40 -03:00
Marcus Da Coregio 5955e89dff Merge branch '5.8.x' into 6.0.x
Closes gh-12299
2022-11-25 09:24:03 -03:00
Marcus Da Coregio 08afa1782c Merge branch '5.7.x' into 5.8.x
Closes gh-12298
2022-11-25 09:22:27 -03:00
Marcus Da Coregio c2e86ad067 Merge branch '5.6.x' into 5.7.x
Closes gh-12297
2022-11-25 09:22:10 -03:00
Kirk Lin 9c6d90490d ci: fix GitHub Actions Deprecating save-state and set-output commands
Closes gh-12032
2022-11-25 09:05:28 -03:00
Marcus Da Coregio 3e0e532ed7 Merge branch '5.8.x' into 6.0.x
Closes gh-12287
2022-11-24 08:48:27 -03:00
Marcus Da Coregio 5db7ac4ce3 Merge branch '5.7.x' into 5.8.x
Closes gh-12286
2022-11-24 08:48:05 -03:00
Marcus Da Coregio 9b3f834bff Merge branch '5.6.x' into 5.7.x
Closes gh-12285
2022-11-24 08:47:46 -03:00
Marcus Da Coregio 70bfc39418 Fix AuthorizationFilter diagram in docs
Closes gh-12274
2022-11-24 08:46:16 -03:00
Dan Allen 3f2bade4f1 sync and refactor docs build
- upgrade Antora to 3.2.0-alpha.2
- remove Antora patch to support linked worktrees
- upgrade Antora Collector to latest alpha
- remove dispatch.sh script (handled by gh run workflow)
- consistently use gh run workflow to trigger workflows
- remove step to publish generated docs (no longer in use)
2022-11-23 17:47:26 -06:00
Steve Riesenberg 2d567cdb09 Merge branch '5.8.x' into 6.0.x 2022-11-23 17:18:52 -06:00
Steve Riesenberg 87c074fc26 Merge branch '5.7.x' into 5.8.x 2022-11-23 17:14:56 -06:00
Steve Riesenberg 621889fa18 Merge branch '5.6.x' into 5.7.x 2022-11-23 17:12:30 -06:00
Dan Allen fe252f5057 sync docs build; upgrade Antora and Antora Collector to latest alpha 2022-11-21 17:26:51 -07:00
Steve Riesenberg d4afc68abc Merge branch '5.8.x'
Merged using the ours strategy.
2022-11-21 10:42:22 -06:00
github-actions[bot] 413c47311e Next development version 2022-11-21 16:04:03 +00:00
github-actions[bot] 457c45878c Next development version 2022-11-21 15:55:10 +00:00
github-actions[bot] b873b24413 Release 5.8.0 2022-11-21 15:23:19 +00:00
42 changed files with 643 additions and 227 deletions
-5
View File
@@ -1,5 +0,0 @@
REPOSITORY_REF="$1"
TOKEN="$2"
curl -H "Accept: application/vnd.github.everest-preview+json" -H "Authorization: token ${TOKEN}" --request POST --data '{"event_type": "request-build-reference"}' https://api.github.com/repos/${REPOSITORY_REF}/dispatches
echo "Requested Build for $REPOSITORY_REF"
-41
View File
@@ -1,41 +0,0 @@
name: Generate Antora Files and Request Build
on:
workflow_dispatch:
push:
branches-ignore:
- 'gh-pages'
tags: '**'
env:
GH_ACTIONS_REPO_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
if: ${{ github.repository == 'spring-projects/spring-security' }}
steps:
- name: Checkout Source
uses: actions/checkout@v2
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@v1
with:
java-version: '17'
distribution: 'temurin'
- name: Generate antora.yml
run: ./gradlew :spring-security-docs:generateAntora
- name: Extract Branch Name
id: extract_branch_name
run: echo "##[set-output name=generated_branch_name;]$(echo ${GITHUB_REPOSITORY}/${GITHUB_REF##*/})"
- name: Push generated antora files to the spring-generated-docs
uses: JamesIves/github-pages-deploy-action@4.1.4
with:
branch: ${{ steps.extract_branch_name.outputs.generated_branch_name }} # The branch the action should deploy to.
folder: "docs/build/generateAntora" # The folder the action should deploy.
repository-name: "spring-io/spring-generated-docs"
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
- name: Dispatch Build Request
run: ${GITHUB_WORKSPACE}/.github/actions/dispatch.sh 'spring-projects/spring-security' "$GH_ACTIONS_REPO_TOKEN"
@@ -31,15 +31,15 @@ jobs:
runjobs: ${{ steps.continue.outputs.runjobs }}
project_version: ${{ steps.continue.outputs.project_version }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- id: continue
name: Determine if should continue
run: |
# Run jobs if in upstream repository
echo "::set-output name=runjobs::true"
echo "runjobs=true" >>$GITHUB_OUTPUT
# Extract version from gradle.properties
version=$(cat gradle.properties | grep "version=" | awk -F'=' '{print $2}')
echo "::set-output name=project_version::$version"
echo "project_version=$version" >>$GITHUB_OUTPUT
build_jdk_17:
name: Build JDK 17
needs: [prerequisites]
@@ -49,9 +49,9 @@ jobs:
runs-on: ${{ matrix.os }}
if: needs.prerequisites.outputs.runjobs
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v2
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
@@ -71,7 +71,7 @@ jobs:
runs-on: ubuntu-latest
if: needs.prerequisites.outputs.runjobs
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@v1
with:
@@ -89,7 +89,7 @@ jobs:
runs-on: ubuntu-latest
if: needs.prerequisites.outputs.runjobs
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@v1
with:
@@ -113,7 +113,7 @@ jobs:
runs-on: ubuntu-latest
if: needs.prerequisites.outputs.runjobs
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@v1
with:
@@ -130,7 +130,7 @@ jobs:
needs: [build_jdk_17, snapshot_tests, check_samples, check_tangles]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@v1
with:
@@ -154,7 +154,7 @@ jobs:
needs: [build_jdk_17, snapshot_tests, check_samples, check_tangles]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@v1
with:
@@ -175,7 +175,7 @@ jobs:
needs: [build_jdk_17, snapshot_tests, check_samples, check_tangles]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@v1
with:
@@ -205,7 +205,7 @@ jobs:
TOKEN: ${{ github.token }}
VERSION: ${{ needs.prerequisites.outputs.project_version }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
- name: Set up gradle
@@ -286,7 +286,7 @@ jobs:
TOKEN: ${{ github.token }}
VERSION: ${{ needs.prerequisites.outputs.project_version }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@v1
with:
+3
View File
@@ -1,5 +1,8 @@
name: Deploy Docs
on:
push:
branches-ignore: [ gh-pages ]
tags: '**'
repository_dispatch:
types: request-build-reference # legacy
schedule:
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.repository == 'spring-projects/spring-security' }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@v1
with:
+3 -3
View File
@@ -6,15 +6,15 @@ on:
permissions: read-all
jobs:
build:
runs-on: ubuntu-latest
if: github.repository_owner == 'spring-projects'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
ref: docs-build
fetch-depth: 1
- name: Dispatch
run: gh workflow run --ref $(git rev-parse --abbrev-ref HEAD) rebuild-search-index.yml
env:
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
run: gh workflow run rebuild-search-index.yml -r $(git rev-parse --abbrev-ref HEAD)
+12 -20
View File
@@ -1,32 +1,24 @@
name: Release Scheduler
on:
schedule:
- cron: '15 15 * * MON' # Every Monday at 3:15pm UTC
- cron: '15 15 * * MON' # Every Monday at 3:15pm UTC
workflow_dispatch:
permissions:
contents: read
permissions: read-all
jobs:
dispatch_scheduled_releases:
name: Dispatch scheduled releases
if: ${{ github.repository == 'spring-projects/spring-security' }}
if: github.repository_owner == 'spring-projects'
strategy:
matrix:
#
# List of active maintenance branches.
#
branch:
- 'main'
- '5.8.x'
- '5.7.x'
- '5.6.x'
branch: [ main, 6.0.x, 5.8.x, 5.7.x, 5.6.x ]
runs-on: ubuntu-latest
env:
TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
steps:
- uses: actions/checkout@v2
- uses: spring-io/spring-gradle-build-action@v1
- name: Trigger release workflow
run: ./gradlew dispatchGitHubWorkflow -Pbranch=${{ matrix.branch }} -PworkflowId=update-scheduled-release-version.yml -PgitHubAccessToken=$TOKEN
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 1
- name: Dispatch
env:
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
run: gh workflow run update-scheduled-release-version.yml -r ${{ matrix.branch }}
@@ -23,7 +23,7 @@ jobs:
steps:
- id: checkout-source
name: Checkout Source Code
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
token: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
- name: Set up gradle
@@ -38,7 +38,7 @@ jobs:
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
./gradlew gitHubCheckNextVersionDueToday
echo "::set-output name=is_due_today::$(cat build/github/milestones/is-due-today)"
echo "is_due_today=$(cat build/github/milestones/is-due-today)" >>$GITHUB_OUTPUT
- id: check-open-issues
name: Check for open issues
if: steps.check-release-due.outputs.is_due_today == 'true'
@@ -47,7 +47,7 @@ jobs:
export GRADLE_ENTERPRISE_CACHE_PASSWORD="$GRADLE_ENTERPRISE_CACHE_PASSWORD"
export GRADLE_ENTERPRISE_ACCESS_KEY="$GRADLE_ENTERPRISE_SECRET_ACCESS_KEY"
./gradlew gitHubCheckMilestoneHasNoOpenIssues
echo "::set-output name=is_open_issues::$(cat build/github/milestones/is-open-issues)"
echo "is_open_issues=$(cat build/github/milestones/is-open-issues)" >>$GITHUB_OUTPUT
- id: validate-release-state
name: Validate State of Release
if: steps.check-release-due.outputs.is_due_today == 'true' && steps.check-open-issues.outputs.is_open_issues == 'true'
+1 -1
View File
@@ -87,7 +87,7 @@ dependencies {
implementation localGroovy()
implementation 'io.github.gradle-nexus:publish-plugin:1.1.0'
implementation 'io.projectreactor:reactor-core:3.5.0-M6'
implementation 'io.projectreactor:reactor-core:3.5.1'
implementation 'org.gretty:gretty:3.0.9'
implementation 'com.apollographql.apollo:apollo-runtime:2.4.5'
implementation 'com.github.ben-manes:gradle-versions-plugin:0.38.0'
+1
View File
@@ -93,6 +93,7 @@ dependencies {
testImplementation 'org.springframework:spring-jdbc'
testImplementation 'org.springframework:spring-orm'
testImplementation 'org.springframework:spring-tx'
testImplementation 'org.springframework:spring-core-test'
testImplementation ('org.springframework.data:spring-data-jpa') {
exclude group: 'org.aspectj', module: 'aspectjrt'
}
@@ -0,0 +1,62 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configuration;
import java.util.Set;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.hint.ProxyHints;
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor;
import org.springframework.beans.factory.aot.BeanRegistrationCode;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.util.ClassUtils;
/**
* AOT {@code BeanRegistrationAotProcessor} that detects beans that implement
* {@link AuthenticationManager} creates the required proxy hints.
*
* @author Marcus da Coregio
* @since 6.0.1
* @see AuthenticationConfiguration#getAuthenticationManager()
*/
class AuthenticationManagerBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor {
@Override
public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) {
Class<?> beanClass = registeredBean.getBeanClass();
Set<Class<?>> allInterfacesForClass = ClassUtils.getAllInterfacesForClassAsSet(beanClass);
if (allInterfacesForClass.contains(AuthenticationManager.class)) {
return new AuthenticationManagerBeanRegistrationAotContribution();
}
return null;
}
private static class AuthenticationManagerBeanRegistrationAotContribution
implements BeanRegistrationAotContribution {
@Override
public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) {
ProxyHints proxyHints = generationContext.getRuntimeHints().proxies();
proxyHints.registerJdkProxy(AopProxyUtils.completeJdkProxyInterfaces(AuthenticationManager.class));
}
}
}
@@ -50,7 +50,8 @@ import org.springframework.security.web.csrf.CsrfToken;
*
* <h2>Shared Objects Created</h2>
*
* No shared objects are created. isLogoutRequest
* No shared objects are created.
*
* <h2>Shared Objects Used</h2>
*
* The following shared objects are used:
@@ -48,8 +48,9 @@ import org.springframework.util.Assert;
* </p>
*
* <pre>
* protected void configure(HttpSecurity http) throws Exception {
* http.apply(new UrlAuthorizationConfigurer&lt;HttpSecurity&gt;()).getRegistry()
* &#64;Bean
* public SecurityFilterChain filterChain(HttpSecurity http, ApplicationContext context) throws Exception {
* http.apply(new UrlAuthorizationConfigurer&lt;HttpSecurity&gt;(context)).getRegistry()
* .requestMatchers(&quot;/users**&quot;, &quot;/sessions/**&quot;).hasRole(&quot;USER&quot;)
* .requestMatchers(&quot;/signup&quot;).hasRole(&quot;ANONYMOUS&quot;).anyRequest().hasRole(&quot;USER&quot;);
* }
@@ -0,0 +1,2 @@
org.springframework.beans.factory.aot.BeanRegistrationAotProcessor=\
org.springframework.security.config.annotation.authentication.configuration.AuthenticationManagerBeanRegistrationAotProcessor
@@ -0,0 +1,102 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.authentication.configuration;
import org.junit.jupiter.api.Test;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.framework.Advised;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.beans.factory.aot.BeanRegistrationAotContribution;
import org.springframework.beans.factory.aot.BeanRegistrationCode;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RegisteredBean;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.DecoratingProxy;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link AuthenticationManagerBeanRegistrationAotProcessor}
*
* @author Marcus da Coregio
*/
class AuthenticationManagerBeanRegistrationAotProcessorTests {
private final AuthenticationManagerBeanRegistrationAotProcessor processor = new AuthenticationManagerBeanRegistrationAotProcessor();
private final GenerationContext generationContext = new TestGenerationContext();
@Test
void shouldSkipWhenInterfaceNotImplemented() {
process(NoAuthenticationManager.class);
assertThat(this.generationContext.getRuntimeHints().proxies().jdkProxyHints()).isEmpty();
}
@Test
void shouldProcessWhenImplementsInterface() {
process(MyAuthenticationManager.class);
assertThat(RuntimeHintsPredicates.proxies().forInterfaces(AuthenticationManager.class, SpringProxy.class,
Advised.class, DecoratingProxy.class)).accepts(this.generationContext.getRuntimeHints());
}
@Test
void shouldProcessWhenSuperclassImplementsInterface() {
process(ChildAuthenticationManager.class);
assertThat(RuntimeHintsPredicates.proxies().forInterfaces(AuthenticationManager.class, SpringProxy.class,
Advised.class, DecoratingProxy.class)).accepts(this.generationContext.getRuntimeHints());
}
private void process(Class<?> beanClass) {
BeanRegistrationAotContribution contribution = createContribution(beanClass);
if (contribution != null) {
contribution.applyTo(this.generationContext, mock(BeanRegistrationCode.class));
}
}
@Nullable
private BeanRegistrationAotContribution createContribution(Class<?> beanClass) {
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition(beanClass.getName(), new RootBeanDefinition(beanClass));
return this.processor.processAheadOfTime(RegisteredBean.of(beanFactory, beanClass.getName()));
}
static class NoAuthenticationManager {
}
static class MyAuthenticationManager implements AuthenticationManager {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
return null;
}
}
static class ChildAuthenticationManager extends MyAuthenticationManager {
}
}
@@ -40,8 +40,9 @@ import org.springframework.util.Assert;
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
/**
* The plaintext password used to perform PasswordEncoder#matches(CharSequence,
* String)} on when the user is not found to avoid SEC-2056.
* The plaintext password used to perform
* {@link PasswordEncoder#matches(CharSequence, String)} on when the user is not found
* to avoid SEC-2056.
*/
private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";
@@ -22,7 +22,7 @@ import org.springframework.expression.Expression;
* Represents an {@link AuthorizationDecision} based on a {@link Expression}
*
* @author Marcus Da Coregio
* @since 5.6
* @since 5.8
*/
public class ExpressionAuthorizationDecision extends AuthorizationDecision {
+9 -9
View File
@@ -15,17 +15,17 @@ dependencies {
api platform("org.springframework.data:spring-data-bom:2022.0.0")
api platform("org.jetbrains.kotlin:kotlin-bom:$kotlinVersion")
api platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4")
api platform("com.fasterxml.jackson:jackson-bom:2.14.0")
api platform("com.fasterxml.jackson:jackson-bom:2.14.1")
constraints {
api "ch.qos.logback:logback-classic:1.4.4"
api "ch.qos.logback:logback-classic:1.4.5"
api "com.google.inject:guice:3.0"
api "com.nimbusds:nimbus-jose-jwt:9.24.4"
api "com.nimbusds:oauth2-oidc-sdk:9.43.1"
api "com.squareup.okhttp3:mockwebserver:3.14.9"
api "com.squareup.okhttp3:okhttp:3.14.9"
api "com.unboundid:unboundid-ldapsdk:6.0.6"
api "com.unboundid:unboundid-ldapsdk:6.0.7"
api "commons-collections:commons-collections:3.2.2"
api "io.mockk:mockk:1.13.2"
api "io.mockk:mockk:1.13.3"
api "io.micrometer:micrometer-observation:$micrometerVersion"
api "jakarta.annotation:jakarta.annotation-api:2.1.1"
api "jakarta.inject:jakarta.inject-api:2.0.1"
@@ -45,17 +45,17 @@ dependencies {
api "org.apache.directory.server:apacheds-protocol-shared:1.5.5"
api "org.apache.directory.server:apacheds-server-jndi:1.5.5"
api "org.apache.directory.shared:shared-ldap:0.9.15"
api "org.apache.httpcomponents:httpclient:4.5.13"
api "org.apache.httpcomponents:httpclient:4.5.14"
api "org.aspectj:aspectjrt:$aspectjVersion"
api "org.aspectj:aspectjweaver:$aspectjVersion"
api "org.assertj:assertj-core:3.23.1"
api "org.bouncycastle:bcpkix-jdk15on:1.70"
api "org.bouncycastle:bcprov-jdk15on:1.70"
api "org.eclipse.jetty:jetty-server:11.0.12"
api "org.eclipse.jetty:jetty-servlet:11.0.12"
api "org.eclipse.jetty:jetty-server:11.0.13"
api "org.eclipse.jetty:jetty-servlet:11.0.13"
api "jakarta.persistence:jakarta.persistence-api:3.1.0"
api "org.hamcrest:hamcrest:2.2"
api "org.hibernate.orm:hibernate-core:6.1.5.Final"
api "org.hibernate.orm:hibernate-core:6.1.6.Final"
api "org.hsqldb:hsqldb:2.7.1"
api "org.jasig.cas.client:cas-client-core:3.6.4"
api "org.opensaml:opensaml-core:$openSamlVersion"
@@ -67,7 +67,7 @@ dependencies {
api "org.seleniumhq.selenium:selenium-support:3.141.59"
api "org.skyscreamer:jsonassert:1.5.1"
api "org.slf4j:log4j-over-slf4j:1.7.36"
api "org.slf4j:slf4j-api:2.0.4"
api "org.slf4j:slf4j-api:2.0.6"
api "org.springframework.ldap:spring-ldap-core:3.0.0"
api "org.synchronoss.cloud:nio-multipart-parser:1.1.0"
api 'org.apache.maven.resolver:maven-resolver-connector-basic:1.8.2'
-53
View File
@@ -1,53 +0,0 @@
'use strict'
/* Copyright (c) 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { promises: fsp } = require('fs')
const ospath = require('path')
/**
* Rewrites local content sources to support the use of linked worktrees.
*
* @author Dan Allen <dan@opendevise.com>
*/
module.exports.register = function () {
this.once('playbookBuilt', async ({ playbook }) => {
const expandPath = this.require('@antora/expand-path-helper')
for (const contentSource of playbook.content.sources) {
const { url, branches } = contentSource
if (url.charAt() !== '.') continue
const absdir = expandPath(url, { dot: playbook.dir })
const gitfile = ospath.join(absdir, '.git')
if (await fsp.stat(gitfile).then((stat) => !stat.isDirectory(), () => false)) {
const worktreeGitdir = await fsp.readFile(gitfile, 'utf8')
.then((contents) => contents.trimRight().substr(8))
const worktreeBranch = await fsp.readFile(ospath.join(worktreeGitdir, 'HEAD'), 'utf8')
.then((contents) => contents.trimRight().replace(/^ref: (?:refs\/heads\/)?/, ''))
const reldir = ospath.relative(
playbook.dir,
await fsp.readFile(ospath.join(worktreeGitdir, 'commondir'), 'utf8')
.then((contents) => {
const gitdir = ospath.join(worktreeGitdir, contents.trimRight())
return ospath.basename(gitdir) === '.git' ? ospath.dirname(gitdir) : gitdir
})
)
contentSource.url = reldir ? `.${ospath.sep}${reldir}` : '.'
if (!branches) continue
contentSource.branches = (branches.constructor === Array ? branches : [branches])
.map((pattern) => pattern.replaceAll('HEAD', worktreeBranch))
}
}
})
}
-1
View File
@@ -1,7 +1,6 @@
# The purpose of this Antora playbook is to generate a preview of the docs in the current branch.
antora:
extensions:
- ./antora-linked-worktree-patch.js
- '@antora/collector-extension'
site:
title: Spring Security
Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 143 KiB

@@ -192,7 +192,7 @@ Instead, you can alter the provided `ObservationRegistry` with an `ObservationPr
----
@Bean
ObservationRegistryCustomizer<ObservationRegistry> noSpringSecurityObservations() {
ObservationPredicate predicate = (name, context) -> name.startsWith("spring.security.")
ObservationPredicate predicate = (name, context) -> !name.startsWith("spring.security.")
return (registry) -> registry.observationConfig().observationPredicate(predicate)
}
----
@@ -202,7 +202,7 @@ ObservationRegistryCustomizer<ObservationRegistry> noSpringSecurityObservations(
----
@Bean
fun noSpringSecurityObservations(): ObservationRegistryCustomizer<ObservationRegistry> {
ObservationPredicate predicate = (name: String, context: Observation.Context) -> name.startsWith("spring.security.")
ObservationPredicate predicate = (name: String, context: Observation.Context) -> !name.startsWith("spring.security.")
(registry: ObservationRegistry) -> registry.observationConfig().observationPredicate(predicate)
}
----
@@ -65,12 +65,12 @@ When used in the context of receiving a permission, an SID is generally called a
The only columns are the ID and the Java class name.
Thus, there is a single row for each unique Class for which we wish to store ACL permissions.
* Finally, `ACL_ENTRY` stores the individual permissions assigned to each recipient.
Columns include a foreign key to the ACL_OBJECT_IDENTITY, the recipient (which is a foreign key to ACL_SID), whether we audit or not, and the integer bit mask that represents the actual permission being granted or denied.
* `ACL_OBJECT_IDENTITY` stores information for each unique domain object instance in the system.
Columns include the ID, a foreign key to the ACL_CLASS table, a unique identifier so we know the ACL_CLASS instance for which we provide information, the parent, a foreign key to the ACL_SID table to represent the owner of the domain object instance, and whether we allow ACL entries to inherit from any parent ACL.
We have a single row for every domain object instance for which we store ACL permissions.
* Finally, ACL_ENTRY stores the individual permissions assigned to each recipient.
Columns include a foreign key to the ACL_OBJECT_IDENTITY, the recipient (i.e. a foreign key to ACL_SID), whether we'll be auditing or not, and the integer bit mask that represents the actual permission being granted or denied.
* Finally, `ACL_ENTRY` stores the individual permissions assigned to each recipient.
Columns include a foreign key to the `ACL_OBJECT_IDENTITY`, the recipient (i.e. a foreign key to ACL_SID), whether we'll be auditing or not, and the integer bit mask that represents the actual permission being granted or denied.
We have a single row for every recipient that receives a permission to work with a domain object.
@@ -48,12 +48,10 @@ image::{figures}/authorizationfilter.png[]
* image:{icondir}/number_1.png[] First, the `AuthorizationFilter` obtains an xref:servlet/authentication/architecture.adoc#servlet-authentication-authentication[Authentication] from the xref:servlet/authentication/architecture.adoc#servlet-authentication-securitycontextholder[SecurityContextHolder].
It wraps this in an `Supplier` in order to delay lookup.
* image:{icondir}/number_2.png[] Second, `AuthorizationFilter` creates a {security-api-url}org/springframework/security/web/FilterInvocation.html[`FilterInvocation`] from the `HttpServletRequest`, `HttpServletResponse`, and `FilterChain`.
// FIXME: link to FilterInvocation
* image:{icondir}/number_3.png[] Next, it passes the `Supplier<Authentication>` and `FilterInvocation` to the xref:servlet/architecture.adoc#authz-authorization-manager[`AuthorizationManager`].
** image:{icondir}/number_4.png[] If authorization is denied, an `AccessDeniedException` is thrown.
* image:{icondir}/number_2.png[] Second, it passes the `Supplier<Authentication>` and the `HttpServletRequest` to the xref:servlet/architecture.adoc#authz-authorization-manager[`AuthorizationManager`].
** image:{icondir}/number_3.png[] If authorization is denied, an `AccessDeniedException` is thrown.
In this case the xref:servlet/architecture.adoc#servlet-exceptiontranslationfilter[`ExceptionTranslationFilter`] handles the `AccessDeniedException`.
** image:{icondir}/number_5.png[] If access is granted, `AuthorizationFilter` continues with the xref:servlet/architecture.adoc#servlet-filters-review[FilterChain] which allows the application to process normally.
** image:{icondir}/number_4.png[] If access is granted, `AuthorizationFilter` continues with the xref:servlet/architecture.adoc#servlet-filters-review[FilterChain] which allows the application to process normally.
We can configure Spring Security to have different rules by adding more rules in order of precedence.
@@ -70,7 +68,8 @@ SecurityFilterChain web(HttpSecurity http) throws Exception {
.requestMatchers("/resources/**", "/signup", "/about").permitAll() // <2>
.requestMatchers("/admin/**").hasRole("ADMIN") // <3>
.requestMatchers("/db/**").access(new WebExpressionAuthorizationManager("hasRole('ADMIN') and hasRole('DBA')")) // <4>
.anyRequest().denyAll() // <5>
// .requestMatchers("/db/**").access(AuthorizationManagers.allOf(AuthorityAuthorizationManager.hasRole("ADMIN"), AuthorityAuthorizationManager.hasRole("DBA"))) // <5>
.anyRequest().denyAll() // <6>
);
return http.build();
@@ -85,7 +84,8 @@ Specifically, any user can access a request if the URL starts with "/resources/"
You will notice that since we are invoking the `hasRole` method we do not need to specify the "ROLE_" prefix.
<4> Any URL that starts with "/db/" requires the user to have both "ROLE_ADMIN" and "ROLE_DBA".
You will notice that since we are using the `hasRole` expression we do not need to specify the "ROLE_" prefix.
<5> Any URL that has not already been matched on is denied access.
<5> The same rule from 4, could be written by combining multiple `AuthorizationManager`.
<6> Any URL that has not already been matched on is denied access.
This is a good strategy if you do not want to accidentally forget to update your authorization rules.
You can take a bean-based approach by constructing your own xref:servlet/authorization/architecture.adoc#authz-delegate-authorization-manager[`RequestMatcherDelegatingAuthorizationManager`] like so:
@@ -118,7 +118,7 @@ AuthorizationManager<RequestAuthorizationContext> requestMatcherAuthorizationMan
RequestMatcher admin = mvcMatcherBuilder.pattern("/admin/**");
RequestMatcher db = mvcMatcherBuilder.pattern("/db/**");
RequestMatcher any = AnyRequestMatcher.INSTANCE;
AuthorizationManager<HttpRequestServlet> manager = RequestMatcherDelegatingAuthorizationManager.builder()
AuthorizationManager<HttpServletRequest> manager = RequestMatcherDelegatingAuthorizationManager.builder()
.add(permitAll, (context) -> new AuthorizationDecision(true))
.add(admin, AuthorityAuthorizationManager.hasRole("ADMIN"))
.add(db, AuthorityAuthorizationManager.hasRole("DBA"))
@@ -144,7 +144,7 @@ You could then refer to the method as follows:
----
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/user/**").access("@webSecurity.check(authentication,request)")
.requestMatchers("/user/**").access(new WebExpressionAuthorizationManager("@webSecurity.check(authentication,request)"))
...
)
----
@@ -197,7 +197,7 @@ Instead, you can alter the provided `ObservationRegistry` with an `ObservationPr
----
@Bean
ObservationRegistryCustomizer<ObservationRegistry> noSpringSecurityObservations() {
ObservationPredicate predicate = (name, context) -> name.startsWith("spring.security.")
ObservationPredicate predicate = (name, context) -> !name.startsWith("spring.security.");
return (registry) -> registry.observationConfig().observationPredicate(predicate)
}
----
@@ -207,7 +207,7 @@ ObservationRegistryCustomizer<ObservationRegistry> noSpringSecurityObservations(
----
@Bean
fun noSpringSecurityObservations(): ObservationRegistryCustomizer<ObservationRegistry> {
ObservationPredicate predicate = (name: String, context: Observation.Context) -> name.startsWith("spring.security.")
ObservationPredicate predicate = (name: String, context: Observation.Context) -> !name.startsWith("spring.security.")
(registry: ObservationRegistry) -> registry.observationConfig().observationPredicate(predicate)
}
----
@@ -248,7 +248,7 @@ public class TenantJWSKeySelector
}
private JWSKeySelector<SecurityContext> fromTenant(String tenant) {
return Optional.ofNullable(this.tenantRepository.findById(tenant)) <3>
return Optional.ofNullable(this.tenants.findById(tenant)) <3>
.map(t -> t.getAttrbute("jwks_uri"))
.map(this::fromUri)
.orElseThrow(() -> new IllegalArgumentException("unknown tenant"));
+2 -2
View File
@@ -6,7 +6,7 @@ apply plugin: 'io.spring.convention.docs'
apply plugin: 'java'
antora {
version = '~3.1'
version = '3.2.0-alpha.2'
playbook = file('local-antora-playbook.yml')
options = ['--clean', '--stacktrace']
environment = [
@@ -15,7 +15,7 @@ antora {
'ALGOLIA_INDEX_NAME': 'security-docs'
]
dependencies = [
'@antora/collector-extension': '1.0.0-alpha.2'
'@antora/collector-extension': '1.0.0-alpha.3'
]
}
+5 -5
View File
@@ -1,12 +1,12 @@
aspectjVersion=1.9.9.1
reactorVersion=2022.0.0
reactorVersion=2022.0.1
springJavaformatVersion=0.0.35
springBootVersion=2.4.2
springFrameworkVersion=6.0.0
micrometerVersion=1.10.1
springFrameworkVersion=6.0.3
micrometerVersion=1.10.2
openSamlVersion=4.1.1
version=6.0.0
kotlinVersion=1.7.21
version=6.0.1
kotlinVersion=1.7.22
samplesBranch=main
org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError
org.gradle.parallel=true
@@ -0,0 +1,105 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.ldap.userdetails;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ldap.core.ContextSource;
import org.springframework.ldap.core.DirContextAdapter;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Dayan Kodippily
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration(
classes = DefaultLdapAuthoritiesPopulatorGetGrantedAuthoritiesTests.ApacheDsContainerWithUndefinedGroupRoleAttributeConfig.class)
public class DefaultLdapAuthoritiesPopulatorGetGrantedAuthoritiesTests {
@Autowired
private DefaultSpringSecurityContextSource contextSource;
private DefaultLdapAuthoritiesPopulator populator;
@BeforeEach
public void setUp() {
this.populator = new DefaultLdapAuthoritiesPopulator(this.contextSource, "ou=groups");
this.populator.setIgnorePartialResultException(false);
}
@Test
public void groupSearchDoesNotAllowNullRoles() {
this.populator.setRolePrefix("ROLE_");
this.populator.setGroupRoleAttribute("ou");
this.populator.setSearchSubtree(true);
this.populator.setSearchSubtree(false);
this.populator.setConvertToUpperCase(true);
this.populator.setGroupSearchFilter("(member={0})");
DirContextAdapter ctx = new DirContextAdapter(
new DistinguishedName("uid=dayan,ou=people,dc=springframework,dc=org"));
Set<String> authorities = AuthorityUtils.authorityListToSet(this.populator.getGrantedAuthorities(ctx, "dayan"));
assertThat(authorities).as("Should have 1 role").hasSize(2);
assertThat(authorities.contains("ROLE_DEVELOPER")).isTrue();
assertThat(authorities.contains("ROLE_")).isTrue();
}
@Configuration
static class ApacheDsContainerWithUndefinedGroupRoleAttributeConfig implements DisposableBean {
private ApacheDSContainer container;
@Bean
ApacheDSContainer ldapContainer() throws Exception {
this.container = new ApacheDSContainer("dc=springframework,dc=org",
"classpath:test-server-with-undefined-group-role-attributes.ldif");
this.container.setPort(0);
return this.container;
}
@Bean
ContextSource contextSource(ApacheDSContainer ldapContainer) {
return new DefaultSpringSecurityContextSource(
"ldap://127.0.0.1:" + ldapContainer.getLocalPort() + "/dc=springframework,dc=org");
}
@Override
public void destroy() {
this.container.stop();
}
}
}
@@ -0,0 +1,41 @@
dn: ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: groups
dn: ou=people,dc=springframework,dc=org
objectclass: top
objectclass: organizationalUnit
ou: people
dn: uid=dayan,ou=people,dc=springframework,dc=org
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Dayan K
sn: Dayan
uid: dayan
userPassword: dayanspassword
dn: cn=managers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: managers
ou:
member: uid=dayan,ou=people,dc=springframework,dc=org
dn: cn=researchers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: researchers
member: uid=dayan,ou=people,dc=springframework,dc=org
dn: cn=developers,ou=groups,dc=springframework,dc=org
objectclass: top
objectclass: groupOfNames
cn: developers
ou: developer
member: uid=dayan,ou=people,dc=springframework,dc=org
@@ -37,6 +37,7 @@ import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* The default strategy for obtaining user role information from the directory.
@@ -169,7 +170,14 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
logger.info("Will perform group search from the context source base since groupSearchBase is empty.");
}
this.authorityMapper = (record) -> {
String role = record.get(this.groupRoleAttribute).get(0);
List<String> roles = record.get(this.groupRoleAttribute);
if (CollectionUtils.isEmpty(roles)) {
return null;
}
String role = roles.get(0);
if (role == null) {
return null;
}
if (this.convertToUpperCase) {
role = role.toUpperCase();
}
@@ -225,7 +233,10 @@ public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator
new String[] { this.groupRoleAttribute });
logger.debug(LogMessage.of(() -> "Found roles from search " + userRoles));
for (Map<String, List<String>> role : userRoles) {
authorities.add(this.authorityMapper.apply(role));
GrantedAuthority authority = this.authorityMapper.apply(role);
if (authority != null) {
authorities.add(authority);
}
}
return authorities;
}
@@ -195,8 +195,8 @@ public class OAuth2AuthorizationRequestRedirectFilter extends OncePerRequestFilt
if (authorizationRequest == null) {
throw authzEx;
}
this.sendRedirectForAuthorization(request, response, authorizationRequest);
this.requestCache.saveRequest(request, response);
this.sendRedirectForAuthorization(request, response, authorizationRequest);
}
catch (Exception failed) {
this.unsuccessfulRedirectForAuthorization(request, response, failed);
@@ -25,6 +25,7 @@ import io.r2dbc.h2.H2ConnectionFactory;
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.Result;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -114,12 +115,14 @@ public class R2dbcReactiveOAuth2AuthorizedClientServiceTests {
}
@Test
@Disabled // until https://github.com/reactor/reactor-core/issues/3307 is resolved
public void loadAuthorizedClientWhenDoesNotExistThenReturnNull() {
this.authorizedClientService.loadAuthorizedClient("registration-not-found", "principalName")
.as(StepVerifier::create).expectNextCount(0).verifyComplete();
}
@Test
@Disabled // until https://github.com/reactor/reactor-core/issues/3307 is resolved
public void loadAuthorizedClientWhenExistsThenReturnAuthorizedClient() {
Authentication principal = createPrincipal();
OAuth2AuthorizedClient expected = createAuthorizedClient(principal, this.clientRegistration);
@@ -150,6 +153,7 @@ public class R2dbcReactiveOAuth2AuthorizedClientServiceTests {
}
@Test
@Disabled // until https://github.com/reactor/reactor-core/issues/3307 is resolved
public void loadAuthorizedClientWhenExistsButNotFoundInClientRegistrationRepositoryThenThrowDataRetrievalFailureException() {
given(this.clientRegistrationRepository.findByRegistrationId(any())).willReturn(Mono.empty());
Authentication principal = createPrincipal();
@@ -186,6 +190,7 @@ public class R2dbcReactiveOAuth2AuthorizedClientServiceTests {
}
@Test
@Disabled // until https://github.com/reactor/reactor-core/issues/3307 is resolved
public void saveAuthorizedClientWhenSaveThenLoadReturnsSaved() {
Authentication principal = createPrincipal();
final OAuth2AuthorizedClient expected = createAuthorizedClient(principal, this.clientRegistration);
@@ -244,6 +249,7 @@ public class R2dbcReactiveOAuth2AuthorizedClientServiceTests {
}
@Test
@Disabled // until https://github.com/reactor/reactor-core/issues/3307 is resolved
public void saveAuthorizedClientWhenSaveClientWithExistingPrimaryKeyThenUpdate() {
// Given a saved authorized client
Authentication principal = createPrincipal();
@@ -297,6 +303,7 @@ public class R2dbcReactiveOAuth2AuthorizedClientServiceTests {
}
@Test
@Disabled // until https://github.com/reactor/reactor-core/issues/3307 is resolved
public void removeAuthorizedClientWhenExistsThenRemoved() {
Authentication principal = createPrincipal();
OAuth2AuthorizedClient authorizedClient = createAuthorizedClient(principal, this.clientRegistration);
@@ -50,6 +50,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.willAnswer;
import static org.mockito.BDDMockito.willThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -325,4 +326,22 @@ public class OAuth2AuthorizationRequestRedirectFilterTests {
+ "redirect_uri=http://localhost/login/oauth2/code/registration-id");
}
// gh-11602
@Test
public void doFilterWhenNotAuthorizationRequestAndClientAuthorizationRequiredExceptionThrownThenSaveRequestBeforeCommitted()
throws Exception {
String requestUri = "/path";
MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri);
request.setServletPath(requestUri);
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain filterChain = mock(FilterChain.class);
willAnswer((invocation) -> assertThat((invocation.<HttpServletResponse>getArgument(1)).isCommitted()).isFalse())
.given(this.requestCache).saveRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));
willThrow(new ClientAuthorizationRequiredException(this.registration1.getRegistrationId())).given(filterChain)
.doFilter(any(ServletRequest.class), any(ServletResponse.class));
this.filter.doFilter(request, response, filterChain);
assertThat(response.isCommitted()).isTrue();
}
}
@@ -177,17 +177,19 @@ public final class ObservationFilterChainDecorator implements FilterChainProxy.F
private void wrapFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
AroundFilterObservation parent = observation((HttpServletRequest) request);
FilterChainObservationContext parentBefore = (FilterChainObservationContext) parent.before().getContext();
parentBefore.setChainSize(this.size);
parentBefore.setFilterName(this.name);
parentBefore.setChainPosition(this.position);
if (parent.before().getContext() instanceof FilterChainObservationContext parentBefore) {
parentBefore.setChainSize(this.size);
parentBefore.setFilterName(this.name);
parentBefore.setChainPosition(this.position);
}
parent.before().event(Observation.Event.of(this.name + " before"));
this.filter.doFilter(request, response, chain);
parent.start();
FilterChainObservationContext parentAfter = (FilterChainObservationContext) parent.after().getContext();
parentAfter.setChainSize(this.size);
parentAfter.setFilterName(this.name);
parentAfter.setChainPosition(this.size - this.position + 1);
if (parent.after().getContext() instanceof FilterChainObservationContext parentAfter) {
parentAfter.setChainSize(this.size);
parentAfter.setFilterName(this.name);
parentAfter.setChainPosition(this.size - this.position + 1);
}
parent.after().event(Observation.Event.of(this.name + " after"));
}
@@ -21,6 +21,8 @@ import java.util.function.Supplier;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -28,7 +30,7 @@ import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.filter.GenericFilterBean;
/**
* A {@link jakarta.servlet.Filter} that uses the {@link SecurityContextRepository} to
@@ -40,17 +42,18 @@ import org.springframework.web.filter.OncePerRequestFilter;
* mechanisms to choose individually if authentication should be persisted.
*
* @author Rob Winch
* @author Marcus da Coregio
* @since 5.7
*/
public class SecurityContextHolderFilter extends OncePerRequestFilter {
public class SecurityContextHolderFilter extends GenericFilterBean {
private static final String FILTER_APPLIED = SecurityContextHolderFilter.class.getName() + ".APPLIED";
private final SecurityContextRepository securityContextRepository;
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy();
private boolean shouldNotFilterErrorDispatch;
/**
* Creates a new instance.
* @param securityContextRepository the repository to use. Cannot be null.
@@ -61,23 +64,29 @@ public class SecurityContextHolderFilter extends OncePerRequestFilter {
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
}
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
if (request.getAttribute(FILTER_APPLIED) != null) {
chain.doFilter(request, response);
return;
}
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
Supplier<SecurityContext> deferredContext = this.securityContextRepository.loadDeferredContext(request);
try {
this.securityContextHolderStrategy.setDeferredContext(deferredContext);
filterChain.doFilter(request, response);
chain.doFilter(request, response);
}
finally {
this.securityContextHolderStrategy.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
@Override
protected boolean shouldNotFilterErrorDispatch() {
return this.shouldNotFilterErrorDispatch;
}
/**
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
@@ -89,13 +98,4 @@ public class SecurityContextHolderFilter extends OncePerRequestFilter {
this.securityContextHolderStrategy = securityContextHolderStrategy;
}
/**
* Disables {@link SecurityContextHolderFilter} for error dispatch.
* @param shouldNotFilterErrorDispatch if the Filter should be disabled for error
* dispatch. Default is false.
*/
public void setShouldNotFilterErrorDispatch(boolean shouldNotFilterErrorDispatch) {
this.shouldNotFilterErrorDispatch = shouldNotFilterErrorDispatch;
}
}
@@ -196,18 +196,18 @@ public final class ObservationWebFilterChainDecorator implements WebFilterChainP
private Mono<Void> wrapFilter(ServerWebExchange exchange, WebFilterChain chain) {
AroundWebFilterObservation parent = observation(exchange);
WebFilterChainObservationContext parentBefore = (WebFilterChainObservationContext) parent.before()
.getContext();
parentBefore.setChainSize(this.size);
parentBefore.setFilterName(this.name);
parentBefore.setChainPosition(this.position);
if (parent.before().getContext() instanceof WebFilterChainObservationContext parentBefore) {
parentBefore.setChainSize(this.size);
parentBefore.setFilterName(this.name);
parentBefore.setChainPosition(this.position);
}
return this.filter.filter(exchange, chain).doOnSuccess((result) -> {
parent.start();
WebFilterChainObservationContext parentAfter = (WebFilterChainObservationContext) parent.after()
.getContext();
parentAfter.setChainSize(this.size);
parentAfter.setFilterName(this.name);
parentAfter.setChainPosition(this.size - this.position + 1);
if (parent.after().getContext() instanceof WebFilterChainObservationContext parentAfter) {
parentAfter.setChainSize(this.size);
parentAfter.setFilterName(this.name);
parentAfter.setChainPosition(this.size - this.position + 1);
}
});
}
@@ -0,0 +1,64 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationRegistry;
import jakarta.servlet.FilterChain;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
/**
* Tests for {@link ObservationFilterChainDecorator}
*/
public class ObservationFilterChainDecoratorTests {
@Test
void decorateWhenDefaultsThenObserves() throws Exception {
ObservationHandler<?> handler = mock(ObservationHandler.class);
given(handler.supportsContext(any())).willReturn(true);
ObservationRegistry registry = ObservationRegistry.create();
registry.observationConfig().observationHandler(handler);
ObservationFilterChainDecorator decorator = new ObservationFilterChainDecorator(registry);
FilterChain chain = mock(FilterChain.class);
FilterChain decorated = decorator.decorate(chain);
decorated.doFilter(new MockHttpServletRequest("GET", "/"), new MockHttpServletResponse());
verify(handler).onStart(any());
}
@Test
void decorateWhenNoopThenDoesNotObserve() throws Exception {
ObservationHandler<?> handler = mock(ObservationHandler.class);
given(handler.supportsContext(any())).willReturn(true);
ObservationRegistry registry = ObservationRegistry.NOOP;
registry.observationConfig().observationHandler(handler);
ObservationFilterChainDecorator decorator = new ObservationFilterChainDecorator(registry);
FilterChain chain = mock(FilterChain.class);
FilterChain decorated = decorator.decorate(chain);
decorated.doFilter(new MockHttpServletRequest("GET", "/"), new MockHttpServletResponse());
verifyNoInteractions(handler);
}
}
@@ -18,6 +18,7 @@ package org.springframework.security.web.context;
import java.util.function.Supplier;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -25,11 +26,15 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
@@ -39,11 +44,17 @@ import org.springframework.security.core.context.SecurityContextImpl;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
@ExtendWith(MockitoExtension.class)
class SecurityContextHolderFilterTests {
private static final String FILTER_APPLIED = "org.springframework.security.web.context.SecurityContextHolderFilter.APPLIED";
@Mock
private SecurityContextRepository repository;
@@ -104,14 +115,38 @@ class SecurityContextHolderFilterTests {
}
@Test
void shouldNotFilterErrorDispatchWhenDefault() {
assertThat(this.filter.shouldNotFilterErrorDispatch()).isFalse();
void doFilterWhenFilterAppliedThenDoNothing() throws Exception {
given(this.request.getAttribute(FILTER_APPLIED)).willReturn(true);
this.filter.doFilter(this.request, this.response, new MockFilterChain());
verify(this.request, times(1)).getAttribute(FILTER_APPLIED);
verifyNoInteractions(this.repository, this.response);
}
@Test
void shouldNotFilterErrorDispatchWhenOverridden() {
this.filter.setShouldNotFilterErrorDispatch(true);
assertThat(this.filter.shouldNotFilterErrorDispatch()).isTrue();
void doFilterWhenNotAppliedThenSetsAndRemovesAttribute() throws Exception {
given(this.repository.loadDeferredContext(this.requestArg.capture())).willReturn(
new SupplierDeferredSecurityContext(SecurityContextHolder::createEmptyContext, this.strategy));
this.filter.doFilter(this.request, this.response, new MockFilterChain());
InOrder inOrder = inOrder(this.request, this.repository);
inOrder.verify(this.request).setAttribute(FILTER_APPLIED, true);
inOrder.verify(this.repository).loadDeferredContext(this.request);
inOrder.verify(this.request).removeAttribute(FILTER_APPLIED);
}
@ParameterizedTest
@EnumSource(DispatcherType.class)
void doFilterWhenAnyDispatcherTypeThenFilter(DispatcherType dispatcherType) throws Exception {
lenient().when(this.request.getDispatcherType()).thenReturn(dispatcherType);
Authentication authentication = TestAuthentication.authenticatedUser();
SecurityContext expectedContext = new SecurityContextImpl(authentication);
given(this.repository.loadDeferredContext(this.requestArg.capture()))
.willReturn(new SupplierDeferredSecurityContext(() -> expectedContext, this.strategy));
FilterChain filterChain = (request, response) -> assertThat(SecurityContextHolder.getContext())
.isEqualTo(expectedContext);
this.filter.doFilter(this.request, this.response, filterChain);
}
}
@@ -0,0 +1,67 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.server;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationRegistry;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
import org.springframework.web.server.WebFilterChain;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
/**
* Tests for {@link ObservationWebFilterChainDecorator}
*/
public class ObservationWebFilterChainDecoratorTests {
@Test
void decorateWhenDefaultsThenObserves() {
ObservationHandler<?> handler = mock(ObservationHandler.class);
given(handler.supportsContext(any())).willReturn(true);
ObservationRegistry registry = ObservationRegistry.create();
registry.observationConfig().observationHandler(handler);
ObservationWebFilterChainDecorator decorator = new ObservationWebFilterChainDecorator(registry);
WebFilterChain chain = mock(WebFilterChain.class);
given(chain.filter(any())).willReturn(Mono.empty());
WebFilterChain decorated = decorator.decorate(chain);
decorated.filter(MockServerWebExchange.from(MockServerHttpRequest.get("/").build())).block();
verify(handler).onStart(any());
}
@Test
void decorateWhenNoopThenDoesNotObserve() {
ObservationHandler<?> handler = mock(ObservationHandler.class);
given(handler.supportsContext(any())).willReturn(true);
ObservationRegistry registry = ObservationRegistry.NOOP;
registry.observationConfig().observationHandler(handler);
ObservationWebFilterChainDecorator decorator = new ObservationWebFilterChainDecorator(registry);
WebFilterChain chain = mock(WebFilterChain.class);
given(chain.filter(any())).willReturn(Mono.empty());
WebFilterChain decorated = decorator.decorate(chain);
decorated.filter(MockServerWebExchange.from(MockServerHttpRequest.get("/").build())).block();
verifyNoInteractions(handler);
}
}