1
0
mirror of synced 2026-07-15 15:49:09 +00:00

Compare commits

..

102 Commits

Author SHA1 Message Date
github-actions[bot] 0a9d4dc8fc Release 6.5.10 2026-04-20 17:54:21 +00:00
Josh Cummings 3d4e20597a Merge remote-tracking branch 'oss/6.5.x' into 6.5.x 2026-04-20 11:49:17 -06:00
dependabot[bot] 81bd52ae48 Bump org.hibernate.orm:hibernate-core from 6.6.48.Final to 6.6.49.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.48.Final to 6.6.49.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.49/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.48...6.6.49)

---
updated-dependencies:
- dependency-name: org.hibernate.orm:hibernate-core
  dependency-version: 6.6.49.Final
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-20 11:27:51 -06:00
dependabot[bot] 25b6af2738 Bump org.springframework:spring-framework-bom from 6.2.17 to 6.2.18
Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.2.17 to 6.2.18.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.17...v6.2.18)

---
updated-dependencies:
- dependency-name: org.springframework:spring-framework-bom
  dependency-version: 6.2.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-20 11:27:29 -06:00
dependabot[bot] 95987bffc1 Bump org.apache.maven:maven-resolver-provider from 3.9.14 to 3.9.15
Bumps org.apache.maven:maven-resolver-provider from 3.9.14 to 3.9.15.

---
updated-dependencies:
- dependency-name: org.apache.maven:maven-resolver-provider
  dependency-version: 3.9.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-20 11:27:07 -06:00
Josh Cummings 6e5f8f2a1d Merge remote-tracking branch 'origin/6.5.x' into 6.5.x 2026-04-20 09:51:26 -06:00
Seol-JY 4187af38b2 Verify token deletion in JdbcOneTimeTokenService 2026-04-18 12:30:30 -04:00
Josh Cummings 5b638a54a4 Use SHA Hashes
This commit updates workflows that were using tags to instead
use SHA hashes to reference actions and workflows

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-04-17 14:15:50 -06:00
dependabot[bot] 51eef2b980 Bump io.projectreactor:reactor-bom from 2024.0.16 to 2024.0.17
Bumps [io.projectreactor:reactor-bom](https://github.com/reactor/reactor) from 2024.0.16 to 2024.0.17.
- [Release notes](https://github.com/reactor/reactor/releases)
- [Commits](https://github.com/reactor/reactor/compare/2024.0.16...2024.0.17)

---
updated-dependencies:
- dependency-name: io.projectreactor:reactor-bom
  dependency-version: 2024.0.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-17 11:57:33 -06:00
dependabot[bot] 302cfb116e Bump @springio/antora-extensions from 1.14.10 to 1.14.11 in /docs
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.14.10 to 1.14.11.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.14.10...v1.14.11)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-version: 1.14.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-17 11:08:19 -06:00
dependabot[bot] 695ea1717f Bump org.hibernate.orm:hibernate-core from 6.6.47.Final to 6.6.48.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.47.Final to 6.6.48.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.48/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.47...6.6.48)

---
updated-dependencies:
- dependency-name: org.hibernate.orm:hibernate-core
  dependency-version: 6.6.48.Final
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-17 11:07:58 -06:00
dependabot[bot] 1206c2b141 Bump actions/upload-artifact from 7.0.0 to 7.0.1
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/bbbca2ddaa5d8feaa63e36b76fdaad77386f024f...043fb46d1a93c77aae656e7c1c64a875d1fc6a0a)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-17 11:07:36 -06:00
dependabot[bot] 3539f06146 Bump spring-io/spring-release-actions from 0.0.3 to 0.0.4
Bumps [spring-io/spring-release-actions](https://github.com/spring-io/spring-release-actions) from 0.0.3 to 0.0.4.
- [Release notes](https://github.com/spring-io/spring-release-actions/releases)
- [Commits](https://github.com/spring-io/spring-release-actions/compare/0.0.3...0.0.4)

---
updated-dependencies:
- dependency-name: spring-io/spring-release-actions
  dependency-version: 0.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-17 11:07:08 -06:00
Josh Cummings a317a3d866 Add Support for Always Running Additional Authentication Checks
Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-04-15 21:07:39 -06:00
Josh Cummings 68b820ed09 Check Issuer with Issuer Provided
Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-04-15 18:23:22 -06:00
dependabot[bot] 44d32815b1 Bump @springio/antora-extensions from 1.14.9 to 1.14.10 in /docs
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.14.9 to 1.14.10.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.14.9...v1.14.10)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-version: 1.14.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-07 10:29:49 -06:00
dependabot[bot] 87c3335e01 Bump org.hibernate.orm:hibernate-core from 6.6.45.Final to 6.6.47.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.45.Final to 6.6.47.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.47/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.45...6.6.47)

---
updated-dependencies:
- dependency-name: org.hibernate.orm:hibernate-core
  dependency-version: 6.6.47.Final
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-07 10:07:57 -06:00
Joe Grandja eefbb4da64 Fix DefaultOidcUser.equals()
Closes gh-18622
2026-04-02 10:41:32 -04:00
Rob Winch a2793f31b4 Merge Add XML Based shouldWriteHeadersEagerly tests
Add XML Based shouldWriteHeadersEagerly tests
2026-04-01 12:53:29 -04:00
Robert Winch 679a47a51d Add XML Based shouldWriteHeadersEagerly tests 2026-04-01 11:37:39 -05:00
Josh Cummings 08fca57d12 Add Missing Serialization Support
Closed gh-19012

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-03-31 13:58:35 -06:00
Josh Cummings acabacb971 Update Test to find SuppressWarnings
Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-03-31 13:47:52 -06:00
johnycho 1a130fca3c Improve serialVersionUID check in tests
Signed-off-by: johnycho <shunnn215@gmail.com>
2026-03-31 13:47:50 -06:00
Rob Winch 5a4ada04ac Merge pull request #19004 from rwinch/CredentialRecordOwnerAuthorizationManager
Add CredentialRecordOwnerAuthorizationManager
2026-03-29 23:46:03 -04:00
Robert Winch a856baa6a8 Add CredentialRecordOwnerAuthorizationManager
Add CredentialRecordOwnerAuthorizationManager that verifies the
credential being deleted is owned by the currently authenticated user.
Also add an AuthorizationManager<Bytes> to WebAuthnRegistrationFilter
for the delete credential operation, defaulting to deny all, and wire it
up in WebAuthnConfigurer.

Per the WebAuthn specification [1], credential ids contain at least 16
bytes with at least 100 bits of entropy, making them practically
unguessable. The specification also advises that credential ids should
be kept private, as exposing them can leak personally identifying
information [2]. The CredentialRecordOwnerAuthorizationManager serves as
defense in depth: even if a credential id were somehow exposed, an
unauthorized user could not delete another user's credential.

[1] https://www.w3.org/TR/webauthn-3/#credential-id
[2] https://www.w3.org/TR/webauthn-3/#sctn-credential-id-privacy-leak
2026-03-29 21:54:27 -05:00
Josh Cummings ac63cf4fa5 Polish CustomAuthorizationManager Docs
Issue gh-13967

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-03-27 16:45:25 -06:00
as1605 f6bb55effb Fix documentation for Custom Authorization Manager
Closes gh-13967

Signed-off-by: as1605 <1605.aditya.singh@gmail.com>
2026-03-27 16:45:25 -06:00
Tran Ngoc Nhan 85b756cb74 Update FilterChainProxy#getFilters(String) javadoc
Closes gh-18157

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-03-27 16:09:50 -06:00
dependabot[bot] 7441ce7f16 Bump spring-io/spring-security-release-tools/.github/workflows/perform-release.yml
Bumps [spring-io/spring-security-release-tools/.github/workflows/perform-release.yml](https://github.com/spring-io/spring-security-release-tools) from 1.0.14 to 1.0.15.
- [Release notes](https://github.com/spring-io/spring-security-release-tools/releases)
- [Changelog](https://github.com/spring-io/spring-security-release-tools/blob/main/RELEASE.adoc)
- [Commits](https://github.com/spring-io/spring-security-release-tools/compare/729fed56d42122f88583aff1be35c0800b7d77e9...b92832ecbc7cbe969201e6beafbde0ee400cf095)

---
updated-dependencies:
- dependency-name: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml
  dependency-version: 1.0.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-27 13:25:46 -06:00
dependabot[bot] 9dbcd8cf00 Bump spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml
Bumps [spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml](https://github.com/spring-io/spring-security-release-tools) from 1.0.14 to 1.0.15.
- [Release notes](https://github.com/spring-io/spring-security-release-tools/releases)
- [Changelog](https://github.com/spring-io/spring-security-release-tools/blob/main/RELEASE.adoc)
- [Commits](https://github.com/spring-io/spring-security-release-tools/compare/729fed56d42122f88583aff1be35c0800b7d77e9...b92832ecbc7cbe969201e6beafbde0ee400cf095)

---
updated-dependencies:
- dependency-name: spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml
  dependency-version: 1.0.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-27 13:25:35 -06:00
Josh Cummings 835d6c1fbd Add Issuer Validation to withIssuerLocation Snippets
Closes gh-19000

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-03-27 13:22:24 -06:00
Josh Cummings 95b2cdf7f4 Clarify JavaDoc
Removed note about DelegatingJwtGrantedAuthoritiesConverter from
ExpressionJwtGrantedAuthoritiesConverter and further explained in
DelegatingJwtGrantedAuthoritiesConverter where it comes in handy.

Issue gh-18300

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-03-27 11:48:56 -06:00
Rob Winch 3ecf84855e Merge pull request #18989 from rwinch/gh-18970-null-oncommitted
Merge Handle null value in OnCommittedResponseWrapper header methods
2026-03-26 17:29:33 -04:00
Robert Winch 0039bc0cf0 Handle null value in OnCommittedResponseWrapper header methods
Closes gh-18970
2026-03-26 14:50:44 -05:00
Josh Cummings 057e5181ea Adjust Formatting
Issue gh-18805

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-03-25 15:19:29 -06:00
Tran Ngoc Nhan 178ca56aaf Fallback defaultTargetUrl if refererHeader is empty
Closes gh-18805

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-03-25 15:19:29 -06:00
dependabot[bot] 61ccf14953 Bump org.hibernate.orm:hibernate-core from 6.6.44.Final to 6.6.45.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.44.Final to 6.6.45.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.45/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.44...6.6.45)

---
updated-dependencies:
- dependency-name: org.hibernate.orm:hibernate-core
  dependency-version: 6.6.45.Final
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-25 15:11:27 -06:00
Joe Grandja 6e683f2286 Fix ID Token auth_time validation
Closes gh-18839
2026-03-25 11:33:55 -04:00
Josh Cummings b6e24db68c Return Mono.empty on Empty POST
Closes gh-18973

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-03-23 18:12:21 -06:00
Daniel Garnier-Moiroux aeb5fc1fb0 Fix HttpSessionRequestCache#getMatchingRequest query string parsing
- URL parsing changed in framework 6.2, and fails when path contains a % sign.
- The HttpSessionRequestCache only needs to inspect the query string, not the full URL.

Fixes gh-16656

Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
2026-03-23 17:52:17 -06:00
Tran Ngoc Nhan 62f33d3fcf Add equals and hashCode to HttpMethodRequestMatcher
Closes gh-18911

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-03-20 21:22:20 -06:00
Rob Winch 9fed1ac8c3 New line per sentence
Signed-off-by: Rob Winch <362503+rwinch@users.noreply.github.com>
2026-03-20 15:28:21 -06:00
Josh Cummings 9dbe3bdcc0 Polish Session Management Persistence Docs
Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-03-20 15:28:21 -06:00
sankranti d547ae0181 Fix defaults description in Session Management doc
Corrected that starting from Spring Security 6
security context is not automatically saved by default.

Signed-off-by: sankranti <sankranty@gmail.com>
2026-03-20 15:28:21 -06:00
dependabot[bot] b8b1278e1f Bump @springio/antora-extensions from 1.14.7 to 1.14.9 in /docs
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.14.7 to 1.14.9.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.14.7...v1.14.9)

---
updated-dependencies:
- dependency-name: "@springio/antora-extensions"
  dependency-version: 1.14.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-20 15:22:06 -06:00
dependabot[bot] 381047e386 Bump spring-io/spring-security-release-tools from 1.0.14 to 1.0.15
Bumps [spring-io/spring-security-release-tools](https://github.com/spring-io/spring-security-release-tools) from 1.0.14 to 1.0.15.
- [Release notes](https://github.com/spring-io/spring-security-release-tools/releases)
- [Changelog](https://github.com/spring-io/spring-security-release-tools/blob/main/RELEASE.adoc)
- [Commits](https://github.com/spring-io/spring-security-release-tools/compare/729fed56d42122f88583aff1be35c0800b7d77e9...b92832ecbc7cbe969201e6beafbde0ee400cf095)

---
updated-dependencies:
- dependency-name: spring-io/spring-security-release-tools
  dependency-version: 1.0.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-20 15:21:53 -06:00
dependabot[bot] 376b40a735 Bump io.spring.gradle:spring-security-release-plugin
Bumps [io.spring.gradle:spring-security-release-plugin](https://github.com/spring-io/spring-security-release-tools) from 1.0.14 to 1.0.15.
- [Release notes](https://github.com/spring-io/spring-security-release-tools/releases)
- [Changelog](https://github.com/spring-io/spring-security-release-tools/blob/main/RELEASE.adoc)
- [Commits](https://github.com/spring-io/spring-security-release-tools/compare/v1.0.14...v1.0.15)

---
updated-dependencies:
- dependency-name: io.spring.gradle:spring-security-release-plugin
  dependency-version: 1.0.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-20 14:58:20 -06:00
dependabot[bot] 89fa1cbdd2 Bump spring-io/spring-security-release-tools/.github/workflows/build.yml
Bumps [spring-io/spring-security-release-tools/.github/workflows/build.yml](https://github.com/spring-io/spring-security-release-tools) from 1.0.14 to 1.0.15.
- [Release notes](https://github.com/spring-io/spring-security-release-tools/releases)
- [Changelog](https://github.com/spring-io/spring-security-release-tools/blob/main/RELEASE.adoc)
- [Commits](https://github.com/spring-io/spring-security-release-tools/compare/729fed56d42122f88583aff1be35c0800b7d77e9...b92832ecbc7cbe969201e6beafbde0ee400cf095)

---
updated-dependencies:
- dependency-name: spring-io/spring-security-release-tools/.github/workflows/build.yml
  dependency-version: 1.0.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-20 14:57:09 -06:00
dependabot[bot] 0d75e6d10c Bump @springio/asciidoctor-extensions in /docs
Bumps [@springio/asciidoctor-extensions](https://github.com/spring-io/asciidoctor-extensions) from 1.0.0-alpha.17 to 1.0.0-alpha.18.
- [Changelog](https://github.com/spring-io/asciidoctor-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/asciidoctor-extensions/compare/v1.0.0-alpha.17...v1.0.0-alpha.18)

---
updated-dependencies:
- dependency-name: "@springio/asciidoctor-extensions"
  dependency-version: 1.0.0-alpha.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-20 14:56:46 -06:00
dependabot[bot] 01758c4c59 Bump spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml
Bumps [spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml](https://github.com/spring-io/spring-security-release-tools) from 1.0.14 to 1.0.15.
- [Release notes](https://github.com/spring-io/spring-security-release-tools/releases)
- [Changelog](https://github.com/spring-io/spring-security-release-tools/blob/main/RELEASE.adoc)
- [Commits](https://github.com/spring-io/spring-security-release-tools/compare/729fed56d42122f88583aff1be35c0800b7d77e9...b92832ecbc7cbe969201e6beafbde0ee400cf095)

---
updated-dependencies:
- dependency-name: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml
  dependency-version: 1.0.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-20 14:56:10 -06:00
dependabot[bot] f37833a59c Bump spring-io/spring-security-release-tools/.github/workflows/test.yml
Bumps [spring-io/spring-security-release-tools/.github/workflows/test.yml](https://github.com/spring-io/spring-security-release-tools) from 1.0.14 to 1.0.15.
- [Release notes](https://github.com/spring-io/spring-security-release-tools/releases)
- [Changelog](https://github.com/spring-io/spring-security-release-tools/blob/main/RELEASE.adoc)
- [Commits](https://github.com/spring-io/spring-security-release-tools/compare/729fed56d42122f88583aff1be35c0800b7d77e9...b92832ecbc7cbe969201e6beafbde0ee400cf095)

---
updated-dependencies:
- dependency-name: spring-io/spring-security-release-tools/.github/workflows/test.yml
  dependency-version: 1.0.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-20 14:55:52 -06:00
dependabot[bot] 52e6c4c4be Bump spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml
Bumps [spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml](https://github.com/spring-io/spring-security-release-tools) from 1.0.14 to 1.0.15.
- [Release notes](https://github.com/spring-io/spring-security-release-tools/releases)
- [Changelog](https://github.com/spring-io/spring-security-release-tools/blob/main/RELEASE.adoc)
- [Commits](https://github.com/spring-io/spring-security-release-tools/compare/729fed56d42122f88583aff1be35c0800b7d77e9...b92832ecbc7cbe969201e6beafbde0ee400cf095)

---
updated-dependencies:
- dependency-name: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml
  dependency-version: 1.0.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-20 14:55:38 -06:00
github-actions[bot] 96ceb535f4 Next development version 2026-03-16 18:13:58 +00:00
github-actions[bot] 0c54a55ae8 Release 6.5.9 2026-03-16 17:40:54 +00:00
Josh Cummings 01ff3b086a Add Workflow for Deferring Issues
Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-03-16 10:49:07 -06:00
Rob Winch 33e6f4bd3f Merge Fix Jackson Deserializer for AuthenticationExtensionsClientOutputs 2026-03-16 11:57:07 -04:00
Robert Winch cdd4b36d37 Update Antora UI Spring to v0.4.26 2026-03-16 08:26:19 -05:00
Robert Winch 7672f76fde Bump io.projectreactor:reactor-bom from 2024.0.15 to 2024.0.16 2026-03-16 08:26:12 -05:00
Robert Winch 3db4999da4 Bump org.apache.maven:maven-resolver-provider from 3.9.13 to 3.9.14 2026-03-16 08:26:04 -05:00
dependabot[bot] a708d2f61b Bump org.springframework:spring-framework-bom from 6.2.16 to 6.2.17
Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.2.16 to 6.2.17.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.16...v6.2.17)

---
updated-dependencies:
- dependency-name: org.springframework:spring-framework-bom
  dependency-version: 6.2.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-16 03:07:46 +00:00
Ziqin Wang e726c05e76 Fix Jackson 2 deserializer for AuthenticationExtensionsClientOutputs
The deserializer is updated to properly ignore unknown extensions.

Closes gh-18643

Signed-off-by: Ziqin Wang <ziqin@wangziqin.net>
2026-03-15 15:04:14 +08:00
Ziqin Wang a7039fb3e6 Test Jackson 2 deserializer with unknown primitive WebAuthn ext
Signed-off-by: Ziqin Wang <ziqin@wangziqin.net>
2026-03-15 15:03:28 +08:00
Ziqin Wang 88ea668f47 Test Jackson 2 deserializer with unknown obj/arr WebAuthn ext
Signed-off-by: Ziqin Wang <ziqin@wangziqin.net>
2026-03-15 15:03:17 +08:00
github-actions[bot] 03a5de1955 Update Antora Spring UI to v0.4.26 2026-03-13 17:45:05 +00:00
dependabot[bot] 06cbea383e Bump org.apache.maven:maven-resolver-provider from 3.9.13 to 3.9.14
Bumps org.apache.maven:maven-resolver-provider from 3.9.13 to 3.9.14.

---
updated-dependencies:
- dependency-name: org.apache.maven:maven-resolver-provider
  dependency-version: 3.9.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-13 03:07:50 +00:00
Andrey Litvitski e250236279 Read relayState from authenticationRequest
Closes gh-18243

Signed-off-by: Andrey Litvitski <andrey1010102008@gmail.com>
2026-03-12 10:30:11 -06:00
Josh Cummings ef76ba040d Require non-null authenticationRequest
Closes gh-18880

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-03-11 16:45:23 -06:00
dependabot[bot] d69af716c8 Bump io.projectreactor:reactor-bom from 2024.0.15 to 2024.0.16
Bumps [io.projectreactor:reactor-bom](https://github.com/reactor/reactor) from 2024.0.15 to 2024.0.16.
- [Release notes](https://github.com/reactor/reactor/releases)
- [Commits](https://github.com/reactor/reactor/compare/2024.0.15...2024.0.16)

---
updated-dependencies:
- dependency-name: io.projectreactor:reactor-bom
  dependency-version: 2024.0.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-11 03:06:57 +00:00
Rob Winch 7e37aa2b75 Merge Fix CookieRequestCache parameters 2026-03-09 15:25:05 -04:00
Vishnutheep B 07bfe371b4 Fix CookieRequestCache parameters
Previously the parameters were not restored.

This commit ensures the parameters are restored.

Closes gh-18204

Signed-off-by: Vishnutheep B <vishnutheep@gmail.com>
2026-03-09 14:10:30 -05:00
Robert Winch e12edf43f2 Bump @antora/collector-extension from 1.0.2 to 1.0.3 in /docs 2026-03-09 09:58:04 -05:00
dependabot[bot] a499e56b9b Bump org.apache.maven:maven-resolver-provider from 3.9.12 to 3.9.13
Bumps org.apache.maven:maven-resolver-provider from 3.9.12 to 3.9.13.

---
updated-dependencies:
- dependency-name: org.apache.maven:maven-resolver-provider
  dependency-version: 3.9.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-09 03:09:41 +00:00
dependabot[bot] 8c3f6ea0d4 Bump @antora/collector-extension from 1.0.2 to 1.0.3 in /docs
---
updated-dependencies:
- dependency-name: "@antora/collector-extension"
  dependency-version: 1.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-06 00:37:07 +00:00
Josh Cummings e17d85e460 Add IDE Setup Documentation
Issue gh-17833

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-03-03 18:51:32 -07:00
dependabot[bot] f12036db05 Bump actions/upload-artifact from 6.0.0 to 7.0.0
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/b7c566a772e6b6bfb58ed0dc250532a479d7789f...bbbca2ddaa5d8feaa63e36b76fdaad77386f024f)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-03 18:16:39 -07:00
dependabot[bot] 7b5c502a97 Bump org.hibernate.orm:hibernate-core from 6.6.43.Final to 6.6.44.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.43.Final to 6.6.44.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.44/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.43...6.6.44)

---
updated-dependencies:
- dependency-name: org.hibernate.orm:hibernate-core
  dependency-version: 6.6.44.Final
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-03 17:47:07 -07:00
HaiYan 706b059ea8 Update logout.adoc
Directives should be Directive

Signed-off-by: HaiYan <haiyan_qi@hotmail.com>
2026-03-03 16:43:18 -07:00
Rob Winch ea3b112bea Fix Flaky Crypto Tests 2026-03-03 15:58:17 -06:00
Robert Winch 1261c229a3 Fix Flaky Crypto Tests
Previously the RsaSecretEncryptorTests were flaky because the assumed that a BadPaddigException would be thrown
when using things like different salt. However, given that the tests had random inputs (e.g. keys) there is the
possibility that, despite the fact that it can never be properly decrypted, the final bytes look like a valid
encrypted value.

This updates the tests to ensure that decrypt either throws an Exception or is not equal to the original
plaintext.
2026-03-03 14:52:28 -06:00
Josh Cummings 4501ae7d1c Update Reactive Resource Server startup exceptations
Issue gh-16708

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-02-26 16:56:22 -07:00
Josh Cummings 48112d3d74 Polish Resource Server startup expectations
Issue gh-16708

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-02-26 16:56:22 -07:00
[CLOUD4] 한현 b8735abb63 Clarify Resource Server startup expectations
Clarify that Spring Boot defers OIDC discovery by default.

Closes gh-16708

Signed-off-by: [CLOUD4] 한현 <gusgus1467@naver.com>
2026-02-26 16:56:22 -07:00
Guillaume Husta 68a02ff176 Update Link to CRSF Docs in FAQ
Signed-off-by: Guillaume Husta <guillaume.husta@gmail.com>
2026-02-26 14:47:21 -07:00
dependabot[bot] f37a706d62 Bump org-apache-maven-resolver from 1.9.26 to 1.9.27
Bumps `org-apache-maven-resolver` from 1.9.26 to 1.9.27.

Updates `org.apache.maven.resolver:maven-resolver-connector-basic` from 1.9.26 to 1.9.27
- [Release notes](https://github.com/apache/maven-resolver/releases)
- [Commits](https://github.com/apache/maven-resolver/compare/maven-resolver-1.9.26...maven-resolver-1.9.27)

Updates `org.apache.maven.resolver:maven-resolver-impl` from 1.9.26 to 1.9.27
- [Release notes](https://github.com/apache/maven-resolver/releases)
- [Commits](https://github.com/apache/maven-resolver/compare/maven-resolver-1.9.26...maven-resolver-1.9.27)

Updates `org.apache.maven.resolver:maven-resolver-transport-http` from 1.9.26 to 1.9.27

---
updated-dependencies:
- dependency-name: org.apache.maven.resolver:maven-resolver-connector-basic
  dependency-version: 1.9.27
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.apache.maven.resolver:maven-resolver-impl
  dependency-version: 1.9.27
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.apache.maven.resolver:maven-resolver-transport-http
  dependency-version: 1.9.27
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-26 14:38:30 -07:00
Rob Winch 522c48b3b5 Merge Add Missing OnCommitedResponseWrapper Header Overrides
Add Missing OnCommitedResponseWrapper Header Overrides
2026-02-24 20:16:24 -06:00
Robert Winch 1dae9aa459 Add Missing OnCommitedResponseWrapper Header Overrides
Spring Security's `OnCommitedResponseWrapper` does not override the `setHeader`, `setIntHeader`, `addIntHeader`
methods. This means that if the `Content-Length` response header is specified using any of those methods then
the response body length is not tracked and can be committed before the response headers are written.

Spring Security should override the missing methods and track `Content-Length` as is already done for `addHeader`.

This issue is the underlying problem for spring-projects/spring-framework#36381

Closes gh-18797
2026-02-24 19:46:29 -06:00
Josh Cummings bec25edeb0 Merge pull request #18566 from Hann244/docs/gh-16530-jsp-method-attribute
Clarify need for method attribute in JSP authorize tag
2026-02-24 17:08:14 -07:00
Josh Cummings 4d43edfb20 Polish Documentation
- Combined explanation of method attribute with usage recommendations
- Used one sentence per line format

Issue gh-16530

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-02-24 14:24:11 -07:00
onhann 9f9699f8a5 Clarify need for method attribute in JSP authorize tag
Closes gh-16530

This aligns the JSP documentation with the changes made in gh-16529.
Added a NOTE to clarify that the method attribute is required when the underlying RequestMatcher is method-specific.

Signed-off-by: onhann <gusgus1467@naver.com>
2026-02-24 14:24:11 -07:00
Rob Winch d29c984881 Merge pull request #18544 from Khyojae/gh-18543
Fix GrantedAuthority.authority null in AuthoritiesAuthorizationManager
2026-02-23 11:16:42 -06:00
Robert Winch 1116241ee3 Fix Checks for NullPointerException in AuthoritiesAuthorizationManager
- Fix checkstyle
- Fix the test to use Collection that throws NullPointerException on .contains(null) to replicate the reported issue

Closes gh-18544

Signed-off-by: Robert Winch <362503+rwinch@users.noreply.github.com>
2026-02-23 10:47:11 -06:00
Khyojae d87dc9ae57 Fix: Handle null authority string in AuthoritiesAuthorizationManager
This prevents NPE when GrantedAuthority.getAuthority() returns null. Closes gh-18543

Signed-off-by: Khyojae <khjae201@gmail.com>
2026-02-23 09:30:28 -06:00
dependabot[bot] ac556a45f9 Bump org.hibernate.orm:hibernate-core from 6.6.42.Final to 6.6.43.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.42.Final to 6.6.43.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.43/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.42...6.6.43)

---
updated-dependencies:
- dependency-name: org.hibernate.orm:hibernate-core
  dependency-version: 6.6.43.Final
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-23 08:12:23 -06:00
dependabot[bot] c8731a8dc0 Bump com.fasterxml.jackson:jackson-bom from 2.18.5 to 2.18.6
Bumps [com.fasterxml.jackson:jackson-bom](https://github.com/FasterXML/jackson-bom) from 2.18.5 to 2.18.6.
- [Commits](https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.18.5...jackson-bom-2.18.6)

---
updated-dependencies:
- dependency-name: com.fasterxml.jackson:jackson-bom
  dependency-version: 2.18.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-23 08:12:09 -06:00
Robert Winch 3e3eeda560 Bump ch.qos.logback:logback-classic from 1.5.29 to 1.5.32 2026-02-19 13:28:49 -06:00
dependabot[bot] 58df50c3a3 Bump org-apache-maven-resolver from 1.9.25 to 1.9.26
Bumps `org-apache-maven-resolver` from 1.9.25 to 1.9.26.

Updates `org.apache.maven.resolver:maven-resolver-connector-basic` from 1.9.25 to 1.9.26
- [Release notes](https://github.com/apache/maven-resolver/releases)
- [Commits](https://github.com/apache/maven-resolver/compare/maven-resolver-1.9.25...maven-resolver-1.9.26)

Updates `org.apache.maven.resolver:maven-resolver-impl` from 1.9.25 to 1.9.26
- [Release notes](https://github.com/apache/maven-resolver/releases)
- [Commits](https://github.com/apache/maven-resolver/compare/maven-resolver-1.9.25...maven-resolver-1.9.26)

Updates `org.apache.maven.resolver:maven-resolver-transport-http` from 1.9.25 to 1.9.26

---
updated-dependencies:
- dependency-name: org.apache.maven.resolver:maven-resolver-connector-basic
  dependency-version: 1.9.26
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.apache.maven.resolver:maven-resolver-impl
  dependency-version: 1.9.26
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.apache.maven.resolver:maven-resolver-transport-http
  dependency-version: 1.9.26
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-17 03:06:35 +00:00
dependabot[bot] 79156b2387 Bump ch.qos.logback:logback-classic from 1.5.29 to 1.5.32
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.29 to 1.5.32.
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.29...v_1.5.32)

---
updated-dependencies:
- dependency-name: ch.qos.logback:logback-classic
  dependency-version: 1.5.32
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-17 03:06:15 +00:00
github-actions[bot] 6c2b2a7611 Next development version 2026-02-13 18:24:26 +00:00
github-actions[bot] 0fab34f359 Release 6.5.8 2026-02-13 17:54:05 +00:00
dependabot[bot] 08e5b375ac Bump io.projectreactor:reactor-bom from 2024.0.14 to 2024.0.15
Bumps [io.projectreactor:reactor-bom](https://github.com/reactor/reactor) from 2024.0.14 to 2024.0.15.
- [Release notes](https://github.com/reactor/reactor/releases)
- [Commits](https://github.com/reactor/reactor/compare/2024.0.14...2024.0.15)

---
updated-dependencies:
- dependency-name: io.projectreactor:reactor-bom
  dependency-version: 2024.0.15
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-13 06:41:17 -05:00
dependabot[bot] f9c32afb6f Bump org.springframework:spring-framework-bom from 6.2.15 to 6.2.16
Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.2.15 to 6.2.16.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.15...v6.2.16)

---
updated-dependencies:
- dependency-name: org.springframework:spring-framework-bom
  dependency-version: 6.2.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-13 06:33:08 -05:00
dependabot[bot] 3d61276a1a Bump io.spring.gradle:spring-security-release-plugin
Bumps [io.spring.gradle:spring-security-release-plugin](https://github.com/spring-io/spring-security-release-tools) from 1.0.13 to 1.0.14.
- [Release notes](https://github.com/spring-io/spring-security-release-tools/releases)
- [Changelog](https://github.com/spring-io/spring-security-release-tools/blob/main/RELEASE.adoc)
- [Commits](https://github.com/spring-io/spring-security-release-tools/compare/v1.0.13...v1.0.14)

---
updated-dependencies:
- dependency-name: io.spring.gradle:spring-security-release-plugin
  dependency-version: 1.0.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-12 10:31:30 -07:00
3150 changed files with 53814 additions and 145441 deletions
+53 -33
View File
@@ -3,37 +3,7 @@ registries:
spring-milestones:
type: maven-repository
url: https://repo.spring.io/milestone
shibboleth:
type: maven-repository
url: https://build.shibboleth.net/maven/releases
updates:
- package-ecosystem: gradle
target-branch: 6.5.x
directory: /
schedule:
interval: daily
time: '03:00'
timezone: Etc/UTC
labels:
- 'type: dependency-upgrade'
registries:
- spring-milestones
- shibboleth
ignore:
- dependency-name: com.nimbusds:nimbus-jose-jwt
- dependency-name: org.python:jython
- dependency-name: org.apache.directory.server:*
- dependency-name: org.apache.directory.shared:*
- dependency-name: org.junit:junit-bom
update-types:
- version-update:semver-major
- dependency-name: org.mockito:mockito-bom
update-types:
- version-update:semver-major
- dependency-name: '*'
update-types:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: gradle
target-branch: 6.4.x
directory: /
@@ -45,7 +15,6 @@ updates:
- 'type: dependency-upgrade'
registries:
- spring-milestones
- shibboleth
ignore:
- dependency-name: com.nimbusds:nimbus-jose-jwt
- dependency-name: org.python:jython
@@ -61,7 +30,32 @@ updates:
update-types:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: gradle
target-branch: 6.3.x
directory: /
schedule:
interval: daily
time: '03:00'
timezone: Etc/UTC
labels:
- 'type: dependency-upgrade'
registries:
- spring-milestones
ignore:
- dependency-name: com.nimbusds:nimbus-jose-jwt
- dependency-name: org.python:jython
- dependency-name: org.apache.directory.server:*
- dependency-name: org.apache.directory.shared:*
- dependency-name: org.junit:junit-bom
update-types:
- version-update:semver-major
- dependency-name: org.mockito:mockito-bom
update-types:
- version-update:semver-major
- dependency-name: '*'
update-types:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: gradle
target-branch: main
directory: /
@@ -73,7 +67,6 @@ updates:
- 'type: dependency-upgrade'
registries:
- spring-milestones
- shibboleth
ignore:
- dependency-name: com.nimbusds:nimbus-jose-jwt
- dependency-name: org.python:jython
@@ -94,6 +87,25 @@ updates:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: github-actions
target-branch: 6.3.x
directory: /
schedule:
interval: weekly
labels:
- 'type: task'
- 'in: build'
ignore:
- dependency-name: sjohnr/*
- package-ecosystem: github-actions
target-branch: docs-build
directory: /
schedule:
interval: weekly
labels:
- 'type: task'
- 'in: build'
- package-ecosystem: npm
target-branch: docs-build
directory: /
@@ -111,3 +123,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'
-38
View File
@@ -1,38 +0,0 @@
name: CI
on:
schedule:
- cron: '0 10 * * *' # Once per day at 10am UTC
workflow_dispatch: # Manual trigger
env:
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
permissions:
contents: read
jobs:
snapshot-test:
name: Test Against Snapshots
uses: spring-io/spring-security-release-tools/.github/workflows/test.yml@v1
strategy:
matrix:
include:
- java-version: 21-ea
toolchain: 21
- java-version: 17
toolchain: 17
with:
java-version: ${{ matrix.java-version }}
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot,https://oss.sonatype.org/content/repositories/snapshots -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=7.+ -PreactorVersion=2025.+ -PspringDataVersion=2025.+ --stacktrace
secrets: inherit
send-notification:
name: Send Notification
needs: [ snapshot-test ]
if: ${{ !success() }}
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
+1 -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@1
uses: spring-io/github-actions/.github/workflows/codeql-analysis.yml@e415dadd0910c901e7a7fabd67bbb355b2324500 # 1
@@ -17,7 +17,7 @@ permissions:
jobs:
build:
name: Build
uses: spring-io/spring-security-release-tools/.github/workflows/build.yml@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
uses: spring-io/spring-security-release-tools/.github/workflows/build.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
@@ -27,29 +27,65 @@ jobs:
java-version: ${{ matrix.jdk }}
distribution: temurin
secrets: inherit
test:
name: Test Against Snapshots
uses: spring-io/spring-security-release-tools/.github/workflows/test.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
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 -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=6.2.+ -PreactorVersion=2023.0.+ -PspringDataVersion=2024.0.+ --stacktrace
secrets: inherit
check-samples:
name: Check Samples
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'spring-projects' }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@efc55f07f4dfa22f2afd97f9ea1be4212eeed737 # v2.0.5
with:
java-version: 17
distribution: temurin
- name: Check samples project
env:
LOCAL_REPOSITORY_PATH: ${{ github.workspace }}/build/publications/repos
SAMPLES_DIR: ../spring-security-samples
run: |
# Extract version from gradle.properties
version=$(cat gradle.properties | grep "version=" | awk -F'=' '{print $2}')
# Extract samplesBranch from gradle.properties
samples_branch=$(cat gradle.properties | grep "samplesBranch=" | awk -F'=' '{print $2}')
./gradlew publishMavenJavaPublicationToLocalRepository
./gradlew cloneRepository -PrepositoryName="spring-projects/spring-security-samples" -Pref="$samples_branch" -PcloneOutputDirectory="$SAMPLES_DIR"
./gradlew --refresh-dependencies --project-dir "$SAMPLES_DIR" --init-script spring-security-ci.gradle -PlocalRepositoryPath="$LOCAL_REPOSITORY_PATH" -PspringSecurityVersion="$version" test integrationTest
deploy-artifacts:
name: Deploy Artifacts
needs: [ build]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
needs: [ build, test, check-samples ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
with:
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
default-publish-milestones-central: true
secrets: inherit
deploy-schema:
name: Deploy Schema
needs: [ build ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
needs: [ build, test, check-samples ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
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@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
with:
should-perform-release: ${{ needs.deploy-artifacts.outputs.artifacts-deployed }}
project-version: ${{ needs.deploy-artifacts.outputs.project-version }}
milestone-repo-url: https://repo1.maven.org/maven2
milestone-repo-url: https://repo.spring.io/artifactory/milestone
release-repo-url: https://repo1.maven.org/maven2
artifact-path: org/springframework/security/spring-security-core
slack-announcing-id: spring-security-announcing
@@ -61,6 +97,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
+76
View File
@@ -0,0 +1,76 @@
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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Compute Version
id: compute-version
uses: spring-io/spring-release-actions/compute-version@2420148725bebe44bd59a575a9b1961ca4459b0b # 0.0.4
- name: Get Today's Release Version
id: todays-release
uses: spring-io/spring-release-actions/get-todays-release-version@2420148725bebe44bd59a575a9b1961ca4459b0b # 0.0.4
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@2420148725bebe44bd59a575a9b1961ca4459b0b # 0.0.4
with:
version: ${{ steps.todays-release.outputs.release-version }}
- name: Schedule Next Milestone
uses: spring-io/spring-release-actions/schedule-milestone@2420148725bebe44bd59a575a9b1961ca4459b0b # 0.0.4
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."
-27
View File
@@ -1,27 +0,0 @@
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 }}
permissions:
contents: read
jobs:
perform-release:
name: Perform Release
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@v1
with:
should-perform-release: true
project-version: ${{ inputs.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
@@ -30,6 +30,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
+2 -2
View File
@@ -34,7 +34,7 @@ jobs:
run: ./gradlew -PbuildSrc.skipTests=true :spring-security-docs:antora
- name: Upload Docs
id: upload
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
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@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
# List of active maintenance branches.
branch: [ main, 6.5.x, 6.4.x, 6.3.x ]
branch: [ main, 6.4.x, 6.3.x ]
runs-on: ubuntu-latest
steps:
- name: Checkout
@@ -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
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf # v0.0.22
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
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf # v0.0.22
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@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
uses: spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
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@729fed56d42122f88583aff1be35c0800b7d77e9 # v1.0.14
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
-1
View File
@@ -25,7 +25,6 @@ atlassian-ide-plugin.xml
s101plugin.state
.attach_pid*
.~lock.*#
.kotlin/
!.idea/checkstyle-idea.xml
!.idea/externalDependencies.xml
-3
View File
@@ -79,9 +79,6 @@ See https://github.com/spring-projects/spring-security/tree/main#building-from-s
The wiki pages https://github.com/spring-projects/spring-framework/wiki/Code-Style[Code Style] and https://github.com/spring-projects/spring-framework/wiki/IntelliJ-IDEA-Editor-Settings[IntelliJ IDEA Editor Settings] define the source file coding standards we use along with some IDEA editor settings we customize.
Additionally, since Streams are https://github.com/spring-projects/spring-security/issues/7154[much slower] than `for` loops, please use them judiciously.
The team may ask you to change to a `for` loop if the given code is along a hot path.
To format the code as well as check the style, run `./gradlew format && ./gradlew check`.
[[submit-a-pull-request]]
+21
View File
@@ -68,6 +68,27 @@ 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.
-49
View File
@@ -1,49 +0,0 @@
apply plugin: 'io.spring.convention.spring-module'
dependencies {
management platform(project(":spring-security-dependencies"))
api project(':spring-security-crypto')
api project(':spring-security-core')
api 'org.springframework:spring-aop'
api 'org.springframework:spring-beans'
api 'org.springframework:spring-context'
api 'org.springframework:spring-core'
api 'org.springframework:spring-expression'
api 'io.micrometer:micrometer-observation'
optional project(':spring-security-acl')
optional project(':spring-security-messaging')
optional project(':spring-security-web')
optional 'org.springframework:spring-websocket'
optional 'com.fasterxml.jackson.core:jackson-databind'
optional 'io.micrometer:context-propagation'
optional 'io.projectreactor:reactor-core'
optional 'jakarta.annotation:jakarta.annotation-api'
optional 'org.aspectj:aspectjrt'
optional 'org.springframework:spring-jdbc'
optional 'org.springframework:spring-tx'
optional 'org.jetbrains.kotlinx:kotlinx-coroutines-reactor'
provided 'jakarta.servlet:jakarta.servlet-api'
testImplementation project(path : ':spring-security-web', configuration : 'tests')
testImplementation 'commons-collections:commons-collections'
testImplementation 'io.projectreactor:reactor-test'
testImplementation "org.assertj:assertj-core"
testImplementation "org.junit.jupiter:junit-jupiter-api"
testImplementation "org.junit.jupiter:junit-jupiter-params"
testImplementation "org.junit.jupiter:junit-jupiter-engine"
testImplementation "org.mockito:mockito-core"
testImplementation "org.mockito:mockito-junit-jupiter"
testImplementation "org.springframework:spring-core-test"
testImplementation "org.springframework:spring-test"
testImplementation 'org.skyscreamer:jsonassert'
testImplementation 'org.springframework:spring-test'
testImplementation 'org.jetbrains.kotlin:kotlin-reflect'
testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
testImplementation 'io.mockk:mockk'
testRuntimeOnly 'org.hsqldb:hsqldb'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
@@ -1,36 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.access;
/**
* Represents the interface of a secured object.
*
* @author Ben Alex
*/
public interface ITargetObject {
Integer computeHashCode(String input);
int countLength(String input);
String makeLowerCase(String input);
String makeUpperCase(String input);
String publicMakeLowerCase(String input);
}
@@ -1,53 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.access;
/**
* Simply extends {@link TargetObject} so we have a different object to put configuration
* attributes against.
* <P>
* There is no different behaviour. We have to define each method so that
* <code>Class.getMethod(methodName, args)</code> returns a <code>Method</code>
* referencing this class rather than the parent class.
* </p>
* <P>
* We need to implement <code>ITargetObject</code> again because the
* <code>MethodDefinitionAttributes</code> only locates attributes on interfaces
* explicitly defined by the intercepted class (not the interfaces defined by its parent
* class or classes).
* </p>
*
* @author Ben Alex
*/
public class OtherTargetObject extends TargetObject implements ITargetObject {
@Override
public String makeLowerCase(String input) {
return super.makeLowerCase(input);
}
@Override
public String makeUpperCase(String input) {
return super.makeUpperCase(input);
}
@Override
public String publicMakeLowerCase(String input) {
return super.publicMakeLowerCase(input);
}
}
@@ -1,81 +0,0 @@
/*
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.access;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* Represents a secured object.
*
* @author Ben Alex
*/
public class TargetObject implements ITargetObject {
@Override
public Integer computeHashCode(String input) {
return input.hashCode();
}
@Override
public int countLength(String input) {
return input.length();
}
/**
* Returns the lowercase string, followed by security environment information.
* @param input the message to make lowercase
* @return the lowercase message, a space, the <code>Authentication</code> class that
* was on the <code>SecurityContext</code> at the time of method invocation, and a
* boolean indicating if the <code>Authentication</code> object is authenticated or
* not
*/
@Override
public String makeLowerCase(String input) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth == null) {
return input.toLowerCase() + " Authentication empty";
}
else {
return input.toLowerCase() + " " + auth.getClass().getName() + " " + auth.isAuthenticated();
}
}
/**
* Returns the uppercase string, followed by security environment information.
* @param input the message to make uppercase
* @return the uppercase message, a space, the <code>Authentication</code> class that
* was on the <code>SecurityContext</code> at the time of method invocation, and a
* boolean indicating if the <code>Authentication</code> object is authenticated or
* not
*/
@Override
public String makeUpperCase(String input) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return input.toUpperCase() + " " + auth.getClass().getName() + " " + auth.isAuthenticated();
}
/**
* Delegates through to the {@link #makeLowerCase(String)} method.
* @param input the message to be made lower-case
*/
@Override
public String publicMakeLowerCase(String input) {
return this.makeLowerCase(input);
}
}
@@ -71,7 +71,7 @@ import org.springframework.util.StringUtils;
* <tt>AclEntryVoter</tt>:
* <ul>
* <li>Process domain object class <code>BankAccount</code>, configuration attribute
* <code>VOTE_ACL_BANK_ACCOUNT_READ</code>, require permission
* <code>VOTE_ACL_BANK_ACCONT_READ</code>, require permission
* <code>BasePermission.READ</code></li>
* <li>Process domain object class <code>BankAccount</code>, configuration attribute
* <code>VOTE_ACL_BANK_ACCOUNT_WRITE</code>, require permission list
@@ -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();
}
@@ -65,11 +65,10 @@ import org.springframework.util.Assert;
* NB: This implementation does attempt to provide reasonably optimised lookups - within
* the constraints of a normalised database and standard ANSI SQL features. If you are
* willing to sacrifice either of these constraints (e.g. use a particular database
* feature such as hierarchical queries or materialized views, or reduce normalisation)
* you are likely to achieve better performance. In such situations you will need to
* provide your own custom <code>LookupStrategy</code>. This class does not support
* subclassing, as it is likely to change in future releases and therefore subclassing is
* unsupported.
* feature such as hierarchical queries or materalized views, or reduce normalisation) you
* are likely to achieve better performance. In such situations you will need to provide
* your own custom <code>LookupStrategy</code>. This class does not support subclassing,
* as it is likely to change in future releases and therefore subclassing is unsupported.
* <p>
* There are two SQL queries executed, one in the <tt>lookupPrimaryKeys</tt> method and
* one in <tt>lookupObjectIdentities</tt>. These are built from the same select and "order
@@ -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 -6
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 {
@@ -18,8 +18,6 @@ dependencies {
api 'org.springframework:spring-context'
api 'org.springframework:spring-core'
optional project(':spring-security-access')
testImplementation 'org.springframework:spring-aop'
testImplementation "org.assertj:assertj-core"
testImplementation "org.junit.jupiter:junit-jupiter-api"
+36 -9
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,7 +35,7 @@ ext.milestoneBuild = !(snapshotBuild || releaseBuild)
repositories {
mavenCentral()
maven { url = "https://repo.spring.io/milestone" }
maven { url "https://repo.spring.io/milestone" }
}
springRelease {
@@ -46,19 +46,46 @@ springRelease {
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 -4
View File
@@ -1,6 +1,5 @@
plugins {
id "java-gradle-plugin"
id "groovy-gradle-plugin"
id "java"
id "groovy"
}
@@ -12,7 +11,7 @@ java {
repositories {
gradlePluginPortal()
mavenCentral()
maven { url = 'https://repo.spring.io/snapshot' }
maven { url 'https://repo.spring.io/milestone' }
}
sourceSets {
@@ -64,7 +63,6 @@ configurations {
dependencies {
implementation platform(libs.io.projectreactor.reactor.bom)
implementation libs.spring.nullability
implementation libs.com.google.code.gson.gson
implementation libs.com.thaiopensource.trag
implementation libs.net.sourceforge.saxon.saxon
@@ -78,7 +76,6 @@ dependencies {
implementation libs.com.github.spullara.mustache.java.compiler
implementation libs.io.spring.javaformat.spring.javaformat.gradle.plugin
implementation libs.io.spring.nohttp.nohttp.gradle
implementation libs.org.jetbrains.kotlin.kotlin.gradle.plugin
implementation (libs.net.sourceforge.htmlunit) {
exclude group: 'org.eclipse.jetty.websocket', module: 'websocket-client'
}
@@ -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
}
}
}
@@ -31,12 +31,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()));
@@ -61,7 +61,7 @@ public class ManagementConfigurationPlugin implements Plugin<Project> {
PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
publishing.getPublications().withType(MavenPublication.class, (mavenPublication -> {
mavenPublication.versionMapping((versions) ->
versions.allVariants((versionMapping) -> versionMapping.fromResolutionResult())
versions.allVariants(versionMapping -> versionMapping.fromResolutionResult())
);
}));
});
@@ -80,11 +80,6 @@ class RepositoryConventionPlugin implements Plugin<Project> {
}
url = 'https://repo.spring.io/release/'
}
forceMavenRepositories.findAll { it.startsWith('https://') || it.startsWith('file://') }.each { mavenUrl ->
maven {
url mavenUrl
}
}
}
}
@@ -32,15 +32,12 @@ public class SchemaZipPlugin implements Plugin<Project> {
for (def key : schemas.keySet()) {
def shortName = key.replaceAll(/http.*schema.(.*).spring-.*/, '$1')
assert shortName != key
def schemaResourceName = schemas.get(key)
File xsdFile = module.sourceSets.main.resources.find {
it.path.endsWith(schemaResourceName)
}
if (xsdFile == null) {
throw new IllegalStateException("Could not find schema file for resource name " + schemaResourceName + " in src/main/resources")
it.path.endsWith(schemas.get(key))
}
assert xsdFile != null
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)
}
}
}
@@ -1,17 +0,0 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id 'kotlin'
}
project.plugins.withId("org.jetbrains.kotlin.jvm", (kotlinProject) -> {
project.tasks.withType(KotlinCompile).configureEach {
kotlinOptions {
languageVersion = '2.2'
apiVersion = '2.2'
freeCompilerArgs = ["-Xjsr305=strict", "-Xsuppress-version-warnings"]
jvmTarget = '17'
}
}
})
@@ -1,3 +0,0 @@
plugins {
id 'io.spring.nullability'
}
@@ -81,6 +81,9 @@ public class CheckClasspathForProhibitedDependencies extends DefaultTask {
if (group.startsWith("javax")) {
return true;
}
if (group.equals("commons-logging")) {
return true;
}
if (group.equals("org.slf4j") && id.getName().equals("jcl-over-slf4j")) {
return true;
}
@@ -46,7 +46,7 @@ public class CheckExpectedBranchVersionPlugin implements Plugin<Project> {
task.setDescription("Check if the project version matches the branch version");
task.onlyIf("skipCheckExpectedBranchVersion property is false or not present", CheckExpectedBranchVersionPlugin::skipPropertyFalseOrNotPresent);
task.getVersion().convention(project.provider(() -> project.getVersion().toString()));
task.getBranchName().convention(project.getProviders().exec((execSpec) -> execSpec.setCommandLine("git", "symbolic-ref", "--short", "HEAD")).getStandardOutput().getAsText());
task.getBranchName().convention(project.getProviders().exec(execSpec -> execSpec.setCommandLine("git", "symbolic-ref", "--short", "HEAD")).getStandardOutput().getAsText());
task.getOutputFile().convention(project.getLayout().getBuildDirectory().file("check-expected-branch-version"));
});
project.getTasks().named(JavaBasePlugin.CHECK_TASK_NAME, checkTask -> checkTask.dependsOn(checkExpectedBranchVersionTask));
@@ -83,8 +83,12 @@ public class VerifyDependenciesVersionsPlugin implements Plugin<Project> {
String transitiveNimbusJoseJwtVersion = TransitiveDependencyLookupUtils.lookupJwtVersion(oauth2OidcSdkVersion);
String expectedNimbusJoseJwtVersion = this.getExpectedNimbusJoseJwtVersion().get();
if (!transitiveNimbusJoseJwtVersion.equals(expectedNimbusJoseJwtVersion)) {
String message = String.format("Found transitive nimbus-jose-jwt:%s in oauth2-oidc-sdk:%s, but the project contains a different version of nimbus-jose-jwt [%s]. Please align the versions.", transitiveNimbusJoseJwtVersion, oauth2OidcSdkVersion, expectedNimbusJoseJwtVersion);
throw new VerificationException(message);
String transitiveNimbusJoseJwtMajorMinorVersion = transitiveNimbusJoseJwtVersion.substring(0, transitiveNimbusJoseJwtVersion.lastIndexOf("."));
String expectedNimbusJoseJwtMajorMinorVersion = expectedNimbusJoseJwtVersion.substring(0, expectedNimbusJoseJwtVersion.lastIndexOf("."));
if (!transitiveNimbusJoseJwtMajorMinorVersion.equals(expectedNimbusJoseJwtMajorMinorVersion)) {
String message = String.format("Found transitive nimbus-jose-jwt:%s in oauth2-oidc-sdk:%s, but the project contains a different version of nimbus-jose-jwt [%s]. Please align the major/minor versions.", transitiveNimbusJoseJwtVersion, oauth2OidcSdkVersion, expectedNimbusJoseJwtVersion);
throw new VerificationException(message);
}
}
String message = String.format("Found transitive nimbus-jose-jwt:%s in oauth2-oidc-sdk:%s, the project contains expected version of nimbus-jose-jwt [%s]. Verified all versions align.", transitiveNimbusJoseJwtVersion, oauth2OidcSdkVersion, expectedNimbusJoseJwtVersion);
try {
@@ -17,6 +17,8 @@ package io.spring.gradle;
import org.apache.commons.io.FileUtils;
import org.gradle.testkit.runner.GradleRunner;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import java.io.File;
import java.io.IOException;
@@ -23,6 +23,8 @@ import org.gradle.testfixtures.ProjectBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import java.io.File;
import static org.assertj.core.api.Assertions.assertThat;
/**
@@ -5,6 +5,7 @@ import org.apache.commons.io.FileUtils;
import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.TaskOutcome;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-6
View File
@@ -1,7 +1,3 @@
plugins {
id 'security-nullability'
}
apply plugin: 'io.spring.convention.spring-module'
dependencies {
@@ -15,11 +11,9 @@ dependencies {
api 'org.springframework:spring-web'
optional 'com.fasterxml.jackson.core:jackson-databind'
optional 'tools.jackson.core:jackson-databind'
provided 'jakarta.servlet:jakarta.servlet-api'
testImplementation project(path : ':spring-security-web', configuration : 'tests')
testImplementation "org.assertj:assertj-core"
testImplementation "org.junit.jupiter:junit-jupiter-api"
testImplementation "org.junit.jupiter:junit-jupiter-params"
@@ -16,8 +16,6 @@
package org.springframework.security.cas;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
@@ -36,7 +34,7 @@ public class ServiceProperties implements InitializingBean {
public static final String DEFAULT_CAS_SERVICE_PARAMETER = "service";
private @Nullable String service;
private String service;
private boolean authenticateAllArtifacts;
@@ -64,7 +62,7 @@ public class ServiceProperties implements InitializingBean {
* </pre>
* @return the URL of the service the user is authenticating to
*/
public final @Nullable String getService() {
public final String getService() {
return this.service;
}
@@ -21,6 +21,7 @@ import java.util.ArrayList;
import org.apereo.cas.client.validation.Assertion;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.SpringSecurityCoreVersion;
/**
* Temporary authentication object needed to load the user details service.
@@ -30,7 +31,7 @@ import org.springframework.security.authentication.AbstractAuthenticationToken;
*/
public final class CasAssertionAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 620L;
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final Assertion assertion;
@@ -16,16 +16,11 @@
package org.springframework.security.cas.authentication;
import java.util.ArrayList;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apereo.cas.client.validation.Assertion;
import org.apereo.cas.client.validation.TicketValidationException;
import org.apereo.cas.client.validation.TicketValidator;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
@@ -38,9 +33,7 @@ import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.authority.FactorGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
@@ -69,9 +62,6 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
private static final String AUTHORITY = FactorGrantedAuthority.CAS_AUTHORITY;
@SuppressWarnings("NullAway.Init")
private AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService;
private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
@@ -80,13 +70,11 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
private StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache();
@SuppressWarnings("NullAway.Init")
private String key;
@SuppressWarnings("NullAway.Init")
private TicketValidator ticketValidator;
private @Nullable ServiceProperties serviceProperties;
private ServiceProperties serviceProperties;
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
@@ -101,7 +89,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
}
@Override
public @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (!supports(authentication.getClass())) {
return null;
}
@@ -141,17 +129,12 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
private CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
try {
Object credentials = authentication.getCredentials();
if (credentials == null) {
throw new BadCredentialsException("Authentication.getCredentials() cannot be null");
}
Assertion assertion = this.ticketValidator.validate(credentials.toString(), getServiceUrl(authentication));
Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(),
getServiceUrl(authentication));
UserDetails userDetails = loadUserByAssertion(assertion);
this.userDetailsChecker.check(userDetails);
Collection<GrantedAuthority> authorities = new ArrayList<>(
this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()));
authorities.add(FactorGrantedAuthority.fromAuthority(AUTHORITY));
return new CasAuthenticationToken(this.key, userDetails, credentials, authorities, userDetails, assertion);
return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(),
this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, assertion);
}
catch (TicketValidationException ex) {
throw new BadCredentialsException(ex.getMessage(), ex);
@@ -166,8 +149,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
* @param authentication
* @return
*/
@NullUnmarked
private @Nullable String getServiceUrl(Authentication authentication) {
private String getServiceUrl(Authentication authentication) {
String serviceUrl;
if (authentication.getDetails() instanceof ServiceAuthenticationDetails) {
return ((ServiceAuthenticationDetails) authentication.getDetails()).getServiceUrl();
@@ -233,7 +215,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
return this.statelessTicketCache;
}
protected @Nullable TicketValidator getTicketValidator() {
protected TicketValidator getTicketValidator() {
return this.ticketValidator;
}
@@ -20,10 +20,10 @@ import java.io.Serializable;
import java.util.Collection;
import org.apereo.cas.client.validation.Assertion;
import org.jspecify.annotations.Nullable;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -36,7 +36,7 @@ import org.springframework.util.ObjectUtils;
*/
public class CasAuthenticationToken extends AbstractAuthenticationToken implements Serializable {
private static final long serialVersionUID = 620L;
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final Object credentials;
@@ -105,19 +105,6 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken implemen
setAuthenticated(true);
}
protected CasAuthenticationToken(Builder<?> builder) {
super(builder);
Assert.isTrue(!"".equals(builder.principal), "principal cannot be null or empty");
Assert.notNull(!"".equals(builder.credentials), "credentials cannot be null or empty");
Assert.notNull(builder.userDetails, "userDetails cannot be null");
Assert.notNull(builder.assertion, "assertion cannot be null");
this.keyHash = builder.keyHash;
this.principal = builder.principal;
this.credentials = builder.credentials;
this.userDetails = builder.userDetails;
this.assertion = builder.assertion;
}
private static Integer extractKeyHash(String key) {
Assert.hasLength(key, "key cannot be null or empty");
return key.hashCode();
@@ -167,11 +154,6 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken implemen
return this.userDetails;
}
@Override
public Builder<?> toBuilder() {
return new Builder<>(this);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -181,81 +163,4 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken implemen
return (sb.toString());
}
/**
* A builder of {@link CasAuthenticationToken} instances
*
* @since 7.0
*/
public static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<B> {
private Integer keyHash;
private Object principal;
private Object credentials;
private UserDetails userDetails;
private Assertion assertion;
protected Builder(CasAuthenticationToken token) {
super(token);
this.keyHash = token.keyHash;
this.principal = token.principal;
this.credentials = token.credentials;
this.userDetails = token.userDetails;
this.assertion = token.assertion;
}
/**
* Use this key
* @param key the key to use
* @return the {@link Builder} for further configurations
*/
public B key(String key) {
this.keyHash = key.hashCode();
return (B) this;
}
@Override
public B principal(@Nullable Object principal) {
Assert.notNull(principal, "principal cannot be null");
this.principal = principal;
return (B) this;
}
@Override
public B credentials(@Nullable Object credentials) {
Assert.notNull(credentials, "credentials cannot be null");
this.credentials = credentials;
return (B) this;
}
/**
* Use this {@link UserDetails}
* @param userDetails the {@link UserDetails} to use
* @return the {@link Builder} for further configurations
*/
public B userDetails(UserDetails userDetails) {
this.userDetails = userDetails;
return (B) this;
}
/**
* Use this {@link Assertion}
* @param assertion the {@link Assertion} to use
* @return the {@link Builder} for further configurations
*/
public B assertion(Assertion assertion) {
this.assertion = assertion;
return (B) this;
}
@Override
public CasAuthenticationToken build() {
return new CasAuthenticationToken(this);
}
}
}
@@ -19,10 +19,9 @@ package org.springframework.security.cas.authentication;
import java.io.Serial;
import java.util.Collection;
import org.jspecify.annotations.Nullable;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;
import org.springframework.util.Assert;
/**
@@ -39,11 +38,11 @@ public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationT
static final String CAS_STATEFUL_IDENTIFIER = "_cas_stateful_";
@Serial
private static final long serialVersionUID = 620L;
private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
private final String identifier;
private @Nullable Object credentials;
private Object credentials;
/**
* This constructor can be safely used by any code that wishes to create a
@@ -52,7 +51,7 @@ public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationT
*
*/
public CasServiceTicketAuthenticationToken(String identifier, Object credentials) {
super((Collection<? extends GrantedAuthority>) null);
super(null);
this.identifier = identifier;
this.credentials = credentials;
setAuthenticated(false);
@@ -75,12 +74,6 @@ public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationT
super.setAuthenticated(true);
}
protected CasServiceTicketAuthenticationToken(Builder<?> builder) {
super(builder);
this.identifier = builder.principal;
this.credentials = builder.credentials;
}
public static CasServiceTicketAuthenticationToken stateful(Object credentials) {
return new CasServiceTicketAuthenticationToken(CAS_STATEFUL_IDENTIFIER, credentials);
}
@@ -94,7 +87,7 @@ public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationT
}
@Override
public @Nullable Object getCredentials() {
public Object getCredentials() {
return this.credentials;
}
@@ -116,46 +109,4 @@ public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationT
this.credentials = null;
}
public Builder<?> toBuilder() {
return new Builder<>(this);
}
/**
* A builder of {@link CasServiceTicketAuthenticationToken} instances
*
* @since 7.0
*/
public static class Builder<B extends Builder<B>> extends AbstractAuthenticationBuilder<B> {
private String principal;
private @Nullable Object credentials;
protected Builder(CasServiceTicketAuthenticationToken token) {
super(token);
this.principal = token.identifier;
this.credentials = token.credentials;
}
@Override
public B principal(@Nullable Object principal) {
Assert.isInstanceOf(String.class, principal, "principal must be of type String");
this.principal = (String) principal;
return (B) this;
}
@Override
public B credentials(@Nullable Object credentials) {
Assert.notNull(credentials, "credentials cannot be null");
this.credentials = credentials;
return (B) this;
}
@Override
public CasServiceTicketAuthenticationToken build() {
return new CasServiceTicketAuthenticationToken(this);
}
}
}
@@ -16,8 +16,6 @@
package org.springframework.security.cas.authentication;
import org.jspecify.annotations.Nullable;
/**
* Implementation of @link {@link StatelessTicketCache} that has no backing cache. Useful
* in instances where storing of tickets for stateless session management is not required.
@@ -35,7 +33,7 @@ public final class NullStatelessTicketCache implements StatelessTicketCache {
* @return null since we are not storing any tickets.
*/
@Override
public @Nullable CasAuthenticationToken getByTicketId(final String serviceTicket) {
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
return null;
}
@@ -18,7 +18,6 @@ package org.springframework.security.cas.authentication;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.cache.Cache;
import org.springframework.core.log.LogMessage;
@@ -43,7 +42,7 @@ public class SpringCacheBasedTicketCache implements StatelessTicketCache {
}
@Override
public @Nullable CasAuthenticationToken getByTicketId(final String serviceTicket) {
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
final Cache.ValueWrapper element = (serviceTicket != null) ? this.cache.get(serviceTicket) : null;
logger.debug(LogMessage.of(() -> "Cache hit: " + (element != null) + "; service ticket: " + serviceTicket));
return (element != null) ? (CasAuthenticationToken) element.get() : null;
@@ -16,8 +16,6 @@
package org.springframework.security.cas.authentication;
import org.jspecify.annotations.Nullable;
/**
* Caches CAS service tickets and CAS proxy tickets for stateless connections.
*
@@ -71,7 +69,7 @@ public interface StatelessTicketCache {
* </p>
* @return the fully populated authentication token
*/
@Nullable CasAuthenticationToken getByTicketId(String serviceTicket);
CasAuthenticationToken getByTicketId(String serviceTicket);
/**
* Adds the specified <code>CasAuthenticationToken</code> to the cache.
@@ -18,7 +18,4 @@
* An {@code AuthenticationProvider} that can process CAS service tickets and proxy
* tickets.
*/
@NullMarked
package org.springframework.security.cas.authentication;
import org.jspecify.annotations.NullMarked;
@@ -1,60 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.cas.jackson;
import java.util.Date;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.apereo.cas.client.authentication.AttributePrincipal;
/**
* Helps in jackson deserialization of class
* {@link org.apereo.cas.client.validation.AssertionImpl}, which is used with
* {@link org.springframework.security.cas.authentication.CasAuthenticationToken}.
*
* @author Sebastien Deleuze
* @author Jitendra Singh
* @since 7.0
* @see CasJacksonModule
* @see org.springframework.security.jackson.SecurityJacksonModules
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
class AssertionImplMixin {
/**
* Mixin Constructor helps in deserialize
* {@link org.apereo.cas.client.validation.AssertionImpl}
* @param principal the Principal to associate with the Assertion.
* @param validFromDate when the assertion is valid from.
* @param validUntilDate when the assertion is valid to.
* @param authenticationDate when the assertion is authenticated.
* @param attributes the key/value pairs for this attribute.
*/
@JsonCreator
AssertionImplMixin(@JsonProperty("principal") AttributePrincipal principal,
@JsonProperty("validFromDate") Date validFromDate, @JsonProperty("validUntilDate") Date validUntilDate,
@JsonProperty("authenticationDate") Date authenticationDate,
@JsonProperty("attributes") Map<String, Object> attributes) {
}
}
@@ -1,59 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.cas.jackson;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.apereo.cas.client.proxy.ProxyRetriever;
/**
* Helps in deserialize
* {@link org.apereo.cas.client.authentication.AttributePrincipalImpl} which is used with
* {@link org.springframework.security.cas.authentication.CasAuthenticationToken}.
*
* @author Sebastien Deleuze
* @author Jitendra Singh
* @since 7.0
* @see CasJacksonModule
* @see org.springframework.security.jackson.SecurityJacksonModules
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
class AttributePrincipalImplMixin {
/**
* Mixin Constructor helps in deserialize
* {@link org.apereo.cas.client.authentication.AttributePrincipalImpl}
* @param name the unique identifier for the principal.
* @param attributes the key/value pairs for this principal.
* @param proxyGrantingTicket the ticket associated with this principal.
* @param proxyRetriever the ProxyRetriever implementation to call back to the CAS
* server.
*/
@JsonCreator
AttributePrincipalImplMixin(@JsonProperty("name") String name,
@JsonProperty("attributes") Map<String, Object> attributes,
@JsonProperty("proxyGrantingTicket") String proxyGrantingTicket,
@JsonProperty("proxyRetriever") ProxyRetriever proxyRetriever) {
}
}
@@ -1,69 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.cas.jackson;
import java.util.Collection;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.apereo.cas.client.validation.Assertion;
import org.springframework.security.cas.authentication.CasAuthenticationProvider;
import org.springframework.security.cas.authentication.CasAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
/**
* Mixin class which helps in deserialize {@link CasAuthenticationToken} using jackson.
*
* @author Sebastien Deleuze
* @author Jitendra Singh
* @since 7.0
* @see CasJacksonModule
* @see org.springframework.security.jackson.SecurityJacksonModules
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,
getterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)
class CasAuthenticationTokenMixin {
/**
* Mixin Constructor helps in deserialize {@link CasAuthenticationToken}
* @param keyHash hashCode of provided key to identify if this object made by a given
* {@link CasAuthenticationProvider}
* @param principal typically the UserDetails object (cannot be <code>null</code>)
* @param credentials the service/proxy ticket ID from CAS (cannot be
* <code>null</code>)
* @param authorities the authorities granted to the user (from the
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
* be <code>null</code>)
* @param userDetails the user details (from the
* {@link org.springframework.security.core.userdetails.UserDetailsService}) (cannot
* be <code>null</code>)
* @param assertion the assertion returned from the CAS servers. It contains the
* principal and how to obtain a proxy ticket for the user.
*/
@JsonCreator
CasAuthenticationTokenMixin(@JsonProperty("keyHash") Integer keyHash, @JsonProperty("principal") Object principal,
@JsonProperty("credentials") Object credentials,
@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities,
@JsonProperty("userDetails") UserDetails userDetails, @JsonProperty("assertion") Assertion assertion) {
}
}
@@ -1,71 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.cas.jackson;
import org.apereo.cas.client.authentication.AttributePrincipalImpl;
import org.apereo.cas.client.validation.AssertionImpl;
import tools.jackson.core.Version;
import tools.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import org.springframework.security.cas.authentication.CasAuthenticationToken;
import org.springframework.security.jackson.SecurityJacksonModule;
import org.springframework.security.jackson.SecurityJacksonModules;
/**
* Jackson module for spring-security-cas. This module register
* {@link AssertionImplMixin}, {@link AttributePrincipalImplMixin} and
* {@link CasAuthenticationTokenMixin}. If no default typing enabled by default then it'll
* enable it because typing info is needed to properly serialize/deserialize objects. In
* order to use this module just add this module into your JsonMapper configuration.
*
* <p>
* The recommended way to configure it is to use {@link SecurityJacksonModules} in order
* to enable properly automatic inclusion of type information with related validation.
*
* <pre>
* ClassLoader loader = getClass().getClassLoader();
* JsonMapper mapper = JsonMapper.builder()
* .addModules(SecurityJacksonModules.getModules(loader))
* .build();
* </pre>
*
* @author Sebastien Deleuze
* @author Jitendra Singh
* @since 7.0
* @see SecurityJacksonModules
*/
public class CasJacksonModule extends SecurityJacksonModule {
public CasJacksonModule() {
super(CasJacksonModule.class.getName(), new Version(1, 0, 0, null, null, null));
}
@Override
public void configurePolymorphicTypeValidator(BasicPolymorphicTypeValidator.Builder builder) {
builder.allowIfSubType(AssertionImpl.class)
.allowIfSubType(AttributePrincipalImpl.class)
.allowIfSubType(CasAuthenticationToken.class);
}
@Override
public void setupModule(SetupContext context) {
context.setMixIn(AssertionImpl.class, AssertionImplMixin.class);
context.setMixIn(AttributePrincipalImpl.class, AttributePrincipalImplMixin.class);
context.setMixIn(CasAuthenticationToken.class, CasAuthenticationTokenMixin.class);
}
}
@@ -1,20 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Jackson 3+ serialization support for CAS.
*/
package org.springframework.security.cas.jackson;
@@ -33,7 +33,6 @@ import org.apereo.cas.client.authentication.AttributePrincipal;
* this class we need to register with
* {@link com.fasterxml.jackson.databind.ObjectMapper}. Type information will be stored
* in @class property.
*
* <p>
* <pre>
* ObjectMapper mapper = new ObjectMapper();
@@ -44,11 +43,7 @@ import org.apereo.cas.client.authentication.AttributePrincipal;
* @since 4.2
* @see CasJackson2Module
* @see org.springframework.security.jackson2.SecurityJackson2Modules
* @deprecated as of 7.0 in favor of
* {@code org.springframework.security.cas.jackson.AssertionImplMixin} based on Jackson 3
*/
@SuppressWarnings("removal")
@Deprecated(forRemoval = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@@ -30,7 +30,6 @@ import org.apereo.cas.client.proxy.ProxyRetriever;
* {@link org.apereo.cas.client.authentication.AttributePrincipalImpl} which is used with
* {@link org.springframework.security.cas.authentication.CasAuthenticationToken}. Type
* information will be stored in property named @class.
*
* <p>
* <pre>
* ObjectMapper mapper = new ObjectMapper();
@@ -41,12 +40,7 @@ import org.apereo.cas.client.proxy.ProxyRetriever;
* @since 4.2
* @see CasJackson2Module
* @see org.springframework.security.jackson2.SecurityJackson2Modules
* @deprecated as of 7.0 in favor of
* {@code org.springframework.security.cas.jackson.AttributePrincipalImplMixin} based on
* Jackson 3
*/
@SuppressWarnings("removal")
@Deprecated(forRemoval = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
isGetterVisibility = JsonAutoDetect.Visibility.NONE)
@@ -40,6 +40,7 @@ import org.springframework.security.core.userdetails.UserDetails;
* </ol>
*
* <p>
*
* <pre>
* ObjectMapper mapper = new ObjectMapper();
* mapper.registerModule(new CasJackson2Module());
@@ -49,12 +50,7 @@ import org.springframework.security.core.userdetails.UserDetails;
* @since 4.2
* @see CasJackson2Module
* @see org.springframework.security.jackson2.SecurityJackson2Modules
* @deprecated as of 7.0 in favor of
* {@code org.springframework.security.cas.jackson.CasAuthenticationTokenMixin} based on
* Jackson 3
*/
@SuppressWarnings("removal")
@Deprecated(forRemoval = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, isGetterVisibility = JsonAutoDetect.Visibility.NONE,
getterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.ANY)
@@ -37,14 +37,11 @@ import org.springframework.security.jackson2.SecurityJackson2Modules;
* </pre> <b>Note: use {@link SecurityJackson2Modules#getModules(ClassLoader)} to get list
* of all security modules on the classpath.</b>
*
* @author Jitendra Singh
* @author Jitendra Singh.
* @since 4.2
* @see org.springframework.security.jackson2.SecurityJackson2Modules
* @deprecated as of 7.0 in favor of
* {@link org.springframework.security.cas.jackson.CasJacksonModule} based on Jackson 3
*/
@Deprecated(forRemoval = true)
@SuppressWarnings({ "serial", "removal" })
@SuppressWarnings("serial")
public class CasJackson2Module extends SimpleModule {
public CasJackson2Module() {
@@ -1,23 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Jackson 2 support for CAS.
*/
@NullMarked
package org.springframework.security.cas.jackson2;
import org.jspecify.annotations.NullMarked;
@@ -18,7 +18,4 @@
* Spring Security support for Apereo's Central Authentication Service
* (<a href="https://github.com/apereo/cas">CAS</a>).
*/
@NullMarked
package org.springframework.security.cas;
import org.jspecify.annotations.NullMarked;
@@ -1,23 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* {@link org.springframework.security.core.userdetails.UserDetails} abstractions for CAS.
*/
@NullMarked
package org.springframework.security.cas.userdetails;
import org.jspecify.annotations.NullMarked;
@@ -22,7 +22,6 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apereo.cas.client.util.CommonUtils;
import org.apereo.cas.client.util.WebUtils;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.cas.ServiceProperties;
@@ -48,10 +47,9 @@ import org.springframework.util.Assert;
*/
public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, InitializingBean {
@SuppressWarnings("NullAway.Init")
private ServiceProperties serviceProperties;
private @Nullable String loginUrl;
private String loginUrl;
/**
* Determines whether the Service URL should include the session id for the specific
@@ -119,7 +117,7 @@ public class CasAuthenticationEntryPoint implements AuthenticationEntryPoint, In
* <code>https://www.mycompany.com/cas/login</code>.
* @return the enterprise-wide CAS login URL
*/
public final @Nullable String getLoginUrl() {
public final String getLoginUrl() {
return this.loginUrl;
}
@@ -26,7 +26,6 @@ import jakarta.servlet.http.HttpSession;
import org.apereo.cas.client.proxy.ProxyGrantingTicketStorage;
import org.apereo.cas.client.util.WebUtils;
import org.apereo.cas.client.validation.TicketValidator;
import org.jspecify.annotations.Nullable;
import org.springframework.core.log.LogMessage;
import org.springframework.security.authentication.AuthenticationDetailsSource;
@@ -35,7 +34,7 @@ import org.springframework.security.authentication.AuthenticationTrustResolverIm
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasServiceTicketAuthenticationToken;
import org.springframework.security.cas.authentication.ServiceAuthenticationDetails;
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
@@ -52,12 +51,12 @@ import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* Processes a CAS service ticket, obtains proxy granting tickets, and processes proxy
* tickets.
@@ -191,12 +190,12 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
/**
* The last portion of the receptor url, i.e. /proxy/receptor
*/
private @Nullable RequestMatcher proxyReceptorMatcher;
private RequestMatcher proxyReceptorMatcher;
/**
* The backing storage to store ProxyGrantingTicket requests.
*/
private @Nullable ProxyGrantingTicketStorage proxyGrantingTicketStorage;
private ProxyGrantingTicketStorage proxyGrantingTicketStorage;
private String artifactParameter = ServiceProperties.DEFAULT_CAS_ARTIFACT_PARAMETER;
@@ -217,7 +216,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
public CasAuthenticationFilter() {
super("/login/cas");
RequestMatcher processUri = pathPattern("/login/cas");
RequestMatcher processUri = PathPatternRequestMatcher.withDefaults().matcher("/login/cas");
setRequiresAuthenticationRequestMatcher(processUri);
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
setSecurityContextRepository(this.securityContextRepository);
@@ -245,7 +244,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
}
@Override
public @Nullable Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException {
// if the request is a proxy request process it and return null to indicate the
// request has been processed
@@ -326,7 +325,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
*/
@@ -336,7 +335,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
}
public final void setProxyReceptorUrl(final String proxyReceptorUrl) {
this.proxyReceptorMatcher = pathPattern(proxyReceptorUrl);
this.proxyReceptorMatcher = new AntPathRequestMatcher("/**" + proxyReceptorUrl);
}
public final void setProxyGrantingTicketStorage(final ProxyGrantingTicketStorage proxyGrantingTicketStorage) {
@@ -383,8 +382,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 +395,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,11 +418,10 @@ 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
*/
@SuppressWarnings("NullAway") // Dataflow analysis limitation
private boolean proxyReceptorRequest(HttpServletRequest request) {
final boolean result = proxyReceptorConfigured() && this.proxyReceptorMatcher.matches(request);
this.logger.debug(LogMessage.format("proxyReceptorRequest = %s", result));
@@ -21,9 +21,7 @@ import java.net.URL;
import java.util.regex.Pattern;
import jakarta.servlet.http.HttpServletRequest;
import org.jspecify.annotations.Nullable;
import org.springframework.security.cas.authentication.ServiceAuthenticationDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
@@ -50,8 +48,8 @@ final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails
* string from containing the artifact name and value. This can be created using
* {@link #createArtifactPattern(String)}.
*/
DefaultServiceAuthenticationDetails(@Nullable String casService, HttpServletRequest request,
Pattern artifactPattern) throws MalformedURLException {
DefaultServiceAuthenticationDetails(String casService, HttpServletRequest request, Pattern artifactPattern)
throws MalformedURLException {
super(request);
URL casServiceUrl = new URL(casService);
int port = getServicePort(casServiceUrl);
@@ -62,7 +60,7 @@ final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails
/**
* Returns the current URL minus the artifact parameter and its value, if present.
* @see org.springframework.security.cas.authentication.ServiceAuthenticationDetails#getServiceUrl()
* @see org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails#getServiceUrl()
*/
@Override
public String getServiceUrl() {
@@ -105,7 +103,7 @@ final class DefaultServiceAuthenticationDetails extends WebAuthenticationDetails
* @return the query String minus the artifactParameterName and the corresponding
* value.
*/
private @Nullable String getQueryString(final HttpServletRequest request, final Pattern artifactPattern) {
private String getQueryString(final HttpServletRequest request, final Pattern artifactPattern) {
final String query = request.getQueryString();
if (query == null) {
return null;
@@ -0,0 +1,44 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.cas.web.authentication;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.core.Authentication;
/**
* In order for the
* {@link org.springframework.security.cas.authentication.CasAuthenticationProvider} to
* provide the correct service url to authenticate the ticket, the returned value of
* {@link Authentication#getDetails()} should implement this interface when tickets can be
* sent to any URL rather than only {@link ServiceProperties#getService()}.
*
* @author Rob Winch
* @see ServiceAuthenticationDetailsSource
* @deprecated Please use
* org.springframework.security.cas.authentication.ServiceAuthenticationDetails
*/
@Deprecated
public interface ServiceAuthenticationDetails
extends org.springframework.security.cas.authentication.ServiceAuthenticationDetails {
/**
* Gets the absolute service url (i.e. https://example.com/service/).
* @return the service url. Cannot be <code>null</code>.
*/
String getServiceUrl();
}
@@ -23,7 +23,6 @@ import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.ServiceAuthenticationDetails;
import org.springframework.util.Assert;
/**
@@ -18,7 +18,4 @@
* Authentication processing mechanisms which respond to the submission of authentication
* credentials using CAS.
*/
@NullMarked
package org.springframework.security.cas.web.authentication;
import org.jspecify.annotations.NullMarked;
@@ -17,7 +17,4 @@
/**
* Authenticates standard web browser users via CAS.
*/
@NullMarked
package org.springframework.security.cas.web;
import org.jspecify.annotations.NullMarked;
@@ -27,14 +27,13 @@ import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.SecurityAssertions;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetails;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.FactorGrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.AuthenticationUserDetailsService;
import org.springframework.security.core.userdetails.User;
@@ -348,22 +347,6 @@ public class CasAuthenticationProviderTests {
assertThat(checkCount.get()).isEqualTo(1);
}
@Test
public void authenticateWhenSuccessfulThenIssuesFactor() throws Exception {
CasAuthenticationProvider cap = new CasAuthenticationProvider();
cap.setAuthenticationUserDetailsService(new MockAuthoritiesPopulator());
cap.setKey("qwerty");
StatelessTicketCache cache = new MockStatelessTicketCache();
cap.setStatelessTicketCache(cache);
cap.setServiceProperties(makeServiceProperties());
cap.setTicketValidator(new MockTicketValidator(true));
cap.afterPropertiesSet();
CasServiceTicketAuthenticationToken token = CasServiceTicketAuthenticationToken.stateful("ST-123");
token.setDetails("details");
Authentication result = cap.authenticate(token);
SecurityAssertions.assertThat(result).hasAuthority(FactorGrantedAuthority.CAS_AUTHORITY);
}
private class MockAuthoritiesPopulator implements AuthenticationUserDetailsService {
@Override
@@ -18,7 +18,6 @@ package org.springframework.security.cas.authentication;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.apereo.cas.client.validation.Assertion;
import org.apereo.cas.client.validation.AssertionImpl;
@@ -27,7 +26,6 @@ import org.junit.jupiter.api.Test;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.PasswordEncodedUser;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
@@ -157,29 +155,4 @@ public class CasAuthenticationTokenTests {
assertThat(result.lastIndexOf("Credentials (Service/Proxy Ticket):") != -1).isTrue();
}
@Test
public void toBuilderWhenApplyThenCopies() {
Assertion assertionOne = new AssertionImpl("test");
CasAuthenticationToken factorOne = new CasAuthenticationToken("key", "alice", "pass",
AuthorityUtils.createAuthorityList("FACTOR_ONE"), PasswordEncodedUser.user(), assertionOne);
Assertion assertionTwo = new AssertionImpl("test");
CasAuthenticationToken factorTwo = new CasAuthenticationToken("yek", "bob", "ssap",
AuthorityUtils.createAuthorityList("FACTOR_TWO"), PasswordEncodedUser.admin(), assertionTwo);
CasAuthenticationToken authentication = factorOne.toBuilder()
.authorities((a) -> a.addAll(factorTwo.getAuthorities()))
.key("yek")
.principal(factorTwo.getPrincipal())
.credentials(factorTwo.getCredentials())
.userDetails(factorTwo.getUserDetails())
.assertion(factorTwo.getAssertion())
.build();
Set<String> authorities = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
assertThat(authentication.getKeyHash()).isEqualTo(factorTwo.getKeyHash());
assertThat(authentication.getPrincipal()).isEqualTo(factorTwo.getPrincipal());
assertThat(authentication.getCredentials()).isEqualTo(factorTwo.getCredentials());
assertThat(authentication.getUserDetails()).isEqualTo(factorTwo.getUserDetails());
assertThat(authentication.getAssertion()).isEqualTo(factorTwo.getAssertion());
assertThat(authorities).containsExactlyInAnyOrder("FACTOR_ONE", "FACTOR_TWO");
}
}
@@ -1,151 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.cas.jackson;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import org.apereo.cas.client.authentication.AttributePrincipalImpl;
import org.apereo.cas.client.validation.Assertion;
import org.apereo.cas.client.validation.AssertionImpl;
import org.json.JSONException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.skyscreamer.jsonassert.JSONAssert;
import tools.jackson.databind.json.JsonMapper;
import org.springframework.security.cas.authentication.CasAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.jackson.SecurityJacksonModules;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Jitendra Singh
* @since 4.2
*/
public class CasAuthenticationTokenMixinTests {
private static final String KEY = "casKey";
private static final String PASSWORD = "\"1234\"";
private static final Date START_DATE = new Date();
private static final Date END_DATE = new Date();
public static final String AUTHORITY_JSON = "{\"@class\": \"org.springframework.security.core.authority.SimpleGrantedAuthority\", \"authority\": \"ROLE_USER\"}";
public static final String AUTHORITIES_SET_JSON = "[\"java.util.Collections$UnmodifiableSet\", [" + AUTHORITY_JSON
+ "]]";
public static final String AUTHORITIES_ARRAYLIST_JSON = "[\"java.util.Collections$UnmodifiableRandomAccessList\", ["
+ AUTHORITY_JSON + "]]";
// @formatter:off
public static final String USER_JSON = "{"
+ "\"@class\": \"org.springframework.security.core.userdetails.User\", "
+ "\"username\": \"admin\","
+ " \"password\": " + PASSWORD + ", "
+ "\"accountNonExpired\": true, "
+ "\"accountNonLocked\": true, "
+ "\"credentialsNonExpired\": true, "
+ "\"enabled\": true, "
+ "\"authorities\": " + AUTHORITIES_SET_JSON
+ "}";
// @formatter:on
private static final String CAS_TOKEN_JSON = "{"
+ "\"@class\": \"org.springframework.security.cas.authentication.CasAuthenticationToken\", "
+ "\"keyHash\": " + KEY.hashCode() + "," + "\"principal\": " + USER_JSON + ", " + "\"credentials\": "
+ PASSWORD + ", " + "\"authorities\": " + AUTHORITIES_ARRAYLIST_JSON + "," + "\"userDetails\": " + USER_JSON
+ "," + "\"authenticated\": true, " + "\"details\": null," + "\"assertion\": {"
+ "\"@class\": \"org.apereo.cas.client.validation.AssertionImpl\", " + "\"principal\": {"
+ "\"@class\": \"org.apereo.cas.client.authentication.AttributePrincipalImpl\", "
+ "\"name\": \"assertName\", " + "\"attributes\": {\"@class\": \"java.util.Collections$EmptyMap\"}, "
+ "\"proxyGrantingTicket\": null, " + "\"proxyRetriever\": null" + "}, "
+ "\"validFromDate\": [\"java.util.Date\", " + START_DATE.getTime() + "], "
+ "\"validUntilDate\": [\"java.util.Date\", " + END_DATE.getTime() + "],"
+ "\"authenticationDate\": [\"java.util.Date\", " + START_DATE.getTime() + "], "
+ "\"attributes\": {\"@class\": \"java.util.Collections$EmptyMap\"},"
+ "\"context\": {\"@class\":\"java.util.HashMap\"}" + "}" + "}";
private static final String CAS_TOKEN_CLEARED_JSON = CAS_TOKEN_JSON.replaceFirst(PASSWORD, "null");
protected JsonMapper mapper;
@BeforeEach
public void setup() {
ClassLoader loader = getClass().getClassLoader();
this.mapper = JsonMapper.builder().addModules(SecurityJacksonModules.getModules(loader)).build();
}
@Test
public void serializeCasAuthenticationTest() throws JSONException {
CasAuthenticationToken token = createCasAuthenticationToken();
String actualJson = this.mapper.writeValueAsString(token);
JSONAssert.assertEquals(CAS_TOKEN_JSON, actualJson, true);
}
@Test
public void serializeCasAuthenticationTestAfterEraseCredentialInvoked() throws JSONException {
CasAuthenticationToken token = createCasAuthenticationToken();
token.eraseCredentials();
String actualJson = this.mapper.writeValueAsString(token);
JSONAssert.assertEquals(CAS_TOKEN_CLEARED_JSON, actualJson, true);
}
@Test
public void deserializeCasAuthenticationTestAfterEraseCredentialInvoked() {
CasAuthenticationToken token = this.mapper.readValue(CAS_TOKEN_CLEARED_JSON, CasAuthenticationToken.class);
assertThat(((UserDetails) token.getPrincipal()).getPassword()).isNull();
}
@Test
public void deserializeCasAuthenticationTest() throws IOException {
CasAuthenticationToken token = this.mapper.readValue(CAS_TOKEN_JSON, CasAuthenticationToken.class);
assertThat(token).isNotNull();
assertThat(token.getPrincipal()).isNotNull().isInstanceOf(User.class);
assertThat(((User) token.getPrincipal()).getUsername()).isEqualTo("admin");
assertThat(((User) token.getPrincipal()).getPassword()).isEqualTo("1234");
assertThat(token.getUserDetails()).isNotNull().isInstanceOf(User.class);
assertThat(token.getAssertion()).isNotNull().isInstanceOf(AssertionImpl.class);
assertThat(token.getKeyHash()).isEqualTo(KEY.hashCode());
assertThat(token.getUserDetails().getAuthorities()).extracting(GrantedAuthority::getAuthority)
.containsOnly("ROLE_USER");
assertThat(token.getAssertion().getAuthenticationDate()).isEqualTo(START_DATE);
assertThat(token.getAssertion().getValidFromDate()).isEqualTo(START_DATE);
assertThat(token.getAssertion().getValidUntilDate()).isEqualTo(END_DATE);
assertThat(token.getAssertion().getPrincipal().getName()).isEqualTo("assertName");
assertThat(token.getAssertion().getAttributes()).hasSize(0);
}
private CasAuthenticationToken createCasAuthenticationToken() {
User principal = new User("admin", "1234", Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")));
Collection<? extends GrantedAuthority> authorities = Collections
.singletonList(new SimpleGrantedAuthority("ROLE_USER"));
Assertion assertion = new AssertionImpl(new AttributePrincipalImpl("assertName"), START_DATE, END_DATE,
START_DATE, Collections.<String, Object>emptyMap());
return new CasAuthenticationToken(KEY, principal, principal.getPassword(), authorities,
new User("admin", "1234", authorities), assertion);
}
}
@@ -44,7 +44,6 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Jitendra Singh
* @since 4.2
*/
@SuppressWarnings("removal")
public class CasAuthenticationTokenMixinTests {
private static final String KEY = "casKey";
@@ -43,6 +43,7 @@ import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -54,9 +55,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.springframework.security.web.servlet.TestMockHttpServletRequests.get;
import static org.springframework.security.web.servlet.TestMockHttpServletRequests.post;
import static org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher.pathPattern;
/**
* Tests {@link CasAuthenticationFilter}.
@@ -81,7 +79,9 @@ public class CasAuthenticationFilterTests {
@Test
public void testNormalOperation() throws Exception {
MockHttpServletRequest request = post("/login/cas").param("ticket", "ST-0-ER94xMJmn6pha35CQRoZ").build();
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/login/cas");
request.setServletPath("/login/cas");
request.addParameter("ticket", "ST-0-ER94xMJmn6pha35CQRoZ");
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setAuthenticationManager((a) -> a);
assertThat(filter.requiresAuthentication(request, new MockHttpServletResponse())).isTrue();
@@ -104,22 +104,24 @@ public class CasAuthenticationFilterTests {
String url = "/login/cas";
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setFilterProcessesUrl(url);
MockHttpServletRequest request = post(url).build();
MockHttpServletRequest request = new MockHttpServletRequest("POST", url);
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath(url);
assertThat(filter.requiresAuthentication(request, response)).isTrue();
}
@Test
public void testRequiresAuthenticationProxyRequest() {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
MockHttpServletRequest request = get("/pgtCallback").build();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath("/pgtCallback");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
filter.setProxyReceptorUrl(request.getServletPath());
assertThat(filter.requiresAuthentication(request, response)).isFalse();
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
assertThat(filter.requiresAuthentication(request, response)).isTrue();
request = get("/other").build();
request.setServletPath("/other");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
}
@@ -131,10 +133,12 @@ public class CasAuthenticationFilterTests {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setFilterProcessesUrl(url);
filter.setServiceProperties(properties);
MockHttpServletRequest request = post(url).build();
MockHttpServletRequest request = new MockHttpServletRequest("POST", url);
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath(url);
assertThat(filter.requiresAuthentication(request, response)).isTrue();
request = post("/other").build();
request = new MockHttpServletRequest("POST", "/other");
request.setServletPath("/other");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
request.setParameter(properties.getArtifactParameter(), "value");
assertThat(filter.requiresAuthentication(request, response)).isTrue();
@@ -152,8 +156,9 @@ public class CasAuthenticationFilterTests {
@Test
public void testAuthenticateProxyUrl() throws Exception {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
MockHttpServletRequest request = get("/pgtCallback").build();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath("/pgtCallback");
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
filter.setProxyReceptorUrl(request.getServletPath());
assertThat(filter.attemptAuthentication(request, response)).isNull();
@@ -167,7 +172,9 @@ public class CasAuthenticationFilterTests {
given(manager.authenticate(any(Authentication.class))).willReturn(authentication);
ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setAuthenticateAllArtifacts(true);
MockHttpServletRequest request = post("/authenticate").param("ticket", "ST-1-123").build();
MockHttpServletRequest request = new MockHttpServletRequest("POST", "/authenticate");
request.setParameter("ticket", "ST-1-123");
request.setServletPath("/authenticate");
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = mock(FilterChain.class);
CasAuthenticationFilter filter = new CasAuthenticationFilter();
@@ -193,9 +200,10 @@ public class CasAuthenticationFilterTests {
@Test
public void testChainNotInvokedForProxyReceptor() throws Exception {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
MockHttpServletRequest request = get("/pgtCallback").build();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = mock(FilterChain.class);
request.setServletPath("/pgtCallback");
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
filter.setProxyReceptorUrl(request.getServletPath());
filter.doFilter(request, response, chain);
@@ -263,14 +271,16 @@ public class CasAuthenticationFilterTests {
@Test
public void requiresAuthenticationWhenProxyRequestMatcherThenMatches() {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
MockHttpServletRequest request = get("/pgtCallback").build();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/pgtCallback");
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath("/pgtCallback");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
filter.setProxyReceptorMatcher(pathPattern(request.getServletPath()));
filter.setProxyReceptorMatcher(PathPatternRequestMatcher.withDefaults().matcher(request.getServletPath()));
assertThat(filter.requiresAuthentication(request, response)).isFalse();
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
assertThat(filter.requiresAuthentication(request, response)).isTrue();
request = get("/other").build();
request.setRequestURI("/other");
request.setServletPath("/other");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
}
@@ -26,7 +26,6 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.ServiceAuthenticationDetails;
import org.springframework.security.web.util.UrlUtils;
import static org.assertj.core.api.Assertions.assertThat;
+33 -10
View File
@@ -1,9 +1,10 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.springframework.gradle.xsd.CreateVersionlessXsdTask
import trang.RncToXsd
apply plugin: 'io.spring.convention.spring-module'
apply plugin: 'trang'
apply plugin: 'security-kotlin'
apply plugin: 'kotlin'
configurations {
opensaml5 {
@@ -20,18 +21,16 @@ dependencies {
api 'org.springframework:spring-context'
api 'org.springframework:spring-core'
optional project(':spring-security-access')
optional project(':spring-security-data')
optional project(':spring-security-ldap')
optional project(':spring-security-messaging')
optional project(path: ':spring-security-saml2-service-provider')
opensaml5 project(path: ':spring-security-saml2-service-provider', configuration: 'opensamlFiveMain')
optional project(':spring-security-oauth2-client')
optional project(':spring-security-oauth2-jose')
optional project(':spring-security-oauth2-resource-server')
optional project(':spring-security-oauth2-authorization-server')
optional project(':spring-security-rsocket')
optional project(':spring-security-web')
optional project(':spring-security-webauthn')
optional 'io.projectreactor:reactor-core'
optional 'org.aspectj:aspectjweaver'
optional 'org.springframework:spring-jdbc'
@@ -44,22 +43,20 @@ dependencies {
optional 'org.jetbrains.kotlin:kotlin-reflect'
optional 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
optional 'jakarta.annotation:jakarta.annotation-api'
optional libs.webauthn4j.core
provided 'jakarta.servlet:jakarta.servlet-api'
testImplementation project(':spring-security-aspects')
testImplementation project(':spring-security-cas')
testImplementation project(':spring-security-test')
testImplementation project(path : ':spring-security-access', configuration : 'tests')
testImplementation project(path : ':spring-security-core', configuration : 'tests')
testImplementation project(path : ':spring-security-ldap', configuration : 'tests')
testImplementation project(path : ':spring-security-oauth2-client', configuration : 'tests')
testImplementation project(path : ':spring-security-oauth2-resource-server', configuration : 'tests')
testImplementation project(path : ':spring-security-oauth2-authorization-server', configuration : 'tests')
testImplementation project(':spring-security-saml2-service-provider')
testImplementation project(path : ':spring-security-saml2-service-provider', configuration : 'tests')
testImplementation project(path : ':spring-security-web', configuration : 'tests')
testImplementation project(path : ':spring-security-webauthn', configuration : 'tests')
testImplementation "jakarta.inject:jakarta.inject-api"
testImplementation "org.assertj:assertj-core"
testImplementation "org.junit.jupiter:junit-jupiter-api"
@@ -81,6 +78,12 @@ dependencies {
exclude group: 'commons-logging', module: 'commons-logging'
exclude group: 'xml-apis', module: 'xml-apis'
}
testImplementation "org.apache.directory.server:apacheds-core"
testImplementation "org.apache.directory.server:apacheds-core-entry"
testImplementation "org.apache.directory.server:apacheds-protocol-shared"
testImplementation "org.apache.directory.server:apacheds-protocol-ldap"
testImplementation "org.apache.directory.server:apacheds-server-jndi"
testImplementation 'org.apache.directory.shared:shared-ldap'
testImplementation "com.unboundid:unboundid-ldapsdk"
testImplementation 'jakarta.persistence:jakarta.persistence-api'
testImplementation "org.hibernate.orm:hibernate-core"
@@ -124,7 +127,6 @@ dependencies {
testRuntimeOnly 'org.hsqldb:hsqldb'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
}
def rncToXsd = tasks.named('rncToXsd', RncToXsd)
@@ -144,18 +146,27 @@ 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/'
}
}
tasks.withType(KotlinCompile).configureEach {
kotlinOptions {
languageVersion = "1.7"
apiVersion = "1.7"
freeCompilerArgs = ["-Xjsr305=strict", "-Xsuppress-version-warnings"]
jvmTarget = "17"
}
}
configure(project.tasks.withType(Test)) {
doFirst {
systemProperties['springSecurityVersion'] = version
@@ -172,3 +183,15 @@ test {
}
}
}
tasks.register("opensaml5Test", Test) {
filter {
includeTestsMatching "org.springframework.security.config.annotation.web.configurers.saml2.*"
}
useJUnitPlatform()
classpath = sourceSets.main.output + sourceSets.test.output + configurations.opensaml5
}
tasks.named("check") {
dependsOn opensaml5Test
}
@@ -18,6 +18,7 @@ package org.springframework.security.config.annotation.authentication.ldap;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.Collections;
import java.util.List;
import javax.naming.directory.SearchControls;
@@ -38,11 +39,12 @@ import org.springframework.security.config.annotation.configuration.ObjectPostPr
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.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.authentication.LdapAuthenticationProvider;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.test.web.servlet.MockMvc;
@@ -118,7 +120,8 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
this.spring.register(BindAuthenticationConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withUsername("bob").withRoles("DEVELOPERS"));
.andExpect(authenticated().withUsername("bob")
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
}
// SEC-2472
@@ -127,7 +130,8 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
this.spring.register(PasswordEncoderConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bcrypt").password("password"))
.andExpect(authenticated().withUsername("bcrypt").withRoles("DEVELOPERS"));
.andExpect(authenticated().withUsername("bcrypt")
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS"))));
}
private LdapAuthenticationProvider ldapProvider() {
@@ -322,11 +326,11 @@ public class LdapAuthenticationProviderBuilderSecurityBuilderTests {
abstract static class BaseLdapServerConfig extends BaseLdapProviderConfig {
@Bean
UnboundIdContainer ldapServer() throws Exception {
UnboundIdContainer unboundIdContainer = new UnboundIdContainer("dc=springframework,dc=org",
ApacheDSContainer ldapServer() throws Exception {
ApacheDSContainer apacheDSContainer = new ApacheDSContainer("dc=springframework,dc=org",
"classpath:/test-server.ldif");
unboundIdContainer.setPort(getPort());
return unboundIdContainer;
apacheDSContainer.setPort(getPort());
return apacheDSContainer;
}
}
@@ -16,6 +16,8 @@
package org.springframework.security.config.annotation.authentication.ldap;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -26,6 +28,8 @@ import org.springframework.security.config.annotation.authentication.ldap.LdapAu
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.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;
import org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers;
import org.springframework.test.web.servlet.MockMvc;
@@ -60,7 +64,7 @@ public class LdapAuthenticationProviderConfigurerTests {
.password("bobspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
.withUsername("bob")
.withRoles("DEVELOPERS");
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_DEVELOPERS")));
// @formatter:on
this.mockMvc.perform(request).andExpect(expectedUser);
}
@@ -75,7 +79,7 @@ public class LdapAuthenticationProviderConfigurerTests {
.password("bobspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
.withUsername("bob")
.withRoles("ROL_", new String[] { "DEVELOPERS" });
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROL_DEVELOPERS")));
// @formatter:on
this.mockMvc.perform(request).andExpect(expectedUser);
}
@@ -104,7 +108,8 @@ public class LdapAuthenticationProviderConfigurerTests {
.password("otherbenspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher expectedUser = authenticated()
.withUsername("otherben")
.withRoles("SUBMANAGERS", "MANAGERS", "DEVELOPERS");
.withAuthorities(
AuthorityUtils.createAuthorityList("ROLE_SUBMANAGERS", "ROLE_MANAGERS", "ROLE_DEVELOPERS"));
// @formatter:on
this.mockMvc.perform(request).andExpect(expectedUser);
}
@@ -16,6 +16,7 @@
package org.springframework.security.config.annotation.authentication.ldap;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -33,6 +34,7 @@ import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders;
@@ -77,7 +79,7 @@ public class NamespaceLdapAuthenticationProviderTests {
.user("bob")
.password("bobspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()
.withRoles("PREFIX_", new String[] { "DEVELOPERS" });
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("PREFIX_DEVELOPERS")));
// @formatter:on
this.mockMvc.perform(request).andExpect(user);
}
@@ -101,7 +103,7 @@ public class NamespaceLdapAuthenticationProviderTests {
.user("bob")
.password("bobspassword");
SecurityMockMvcResultMatchers.AuthenticatedMatcher user = authenticated()
.withRoles("EXTRA");
.withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_EXTRA")));
// @formatter:on
this.mockMvc.perform(request).andExpect(user);
}
@@ -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
@@ -1,147 +0,0 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.config.annotation.rsocket;
import java.util.ArrayList;
import java.util.List;
import io.rsocket.core.RSocketServer;
import io.rsocket.exceptions.RejectedSetupException;
import io.rsocket.frame.decoder.PayloadDecoder;
import io.rsocket.transport.netty.server.CloseableChannel;
import io.rsocket.transport.netty.server.TcpServerTransport;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.rsocket.RSocketRequester;
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.rsocket.core.PayloadSocketAcceptorInterceptor;
import org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor;
import org.springframework.security.rsocket.util.matcher.PayloadExchangeAuthorizationContext;
import org.springframework.stereotype.Controller;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Andrey Litvitski
*/
@ContextConfiguration
@ExtendWith(SpringExtension.class)
public class AnonymousAuthenticationITests {
@Autowired
RSocketMessageHandler handler;
@Autowired
SecuritySocketAcceptorInterceptor interceptor;
@Autowired
ServerController controller;
private CloseableChannel server;
private RSocketRequester requester;
@BeforeEach
public void setup() {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
.block();
// @formatter:on
}
@AfterEach
public void dispose() {
this.requester.rsocket().dispose();
this.server.dispose();
this.controller.payloads.clear();
}
@Test
public void requestWhenAnonymousDisabledThenRespondsWithForbidden() {
this.requester = RSocketRequester.builder()
.rsocketStrategies(this.handler.getRSocketStrategies())
.connectTcp("localhost", this.server.address().getPort())
.block();
String data = "andrew";
assertThatExceptionOfType(RejectedSetupException.class).isThrownBy(
() -> this.requester.route("secure.retrieve-mono").data(data).retrieveMono(String.class).block());
assertThat(this.controller.payloads).isEmpty();
}
@Configuration
@EnableRSocketSecurity
static class Config {
@Bean
ServerController controller() {
return new ServerController();
}
@Bean
RSocketMessageHandler messageHandler() {
return new RSocketMessageHandler();
}
@Bean
PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
ReactiveAuthorizationManager<PayloadExchangeAuthorizationContext> anonymous = (authentication,
exchange) -> authentication.map(trustResolver::isAnonymous).map(AuthorizationDecision::new);
rsocket.authorizePayload((authorize) -> authorize.anyExchange().access(anonymous));
rsocket.anonymous((anonymousAuthentication) -> anonymousAuthentication.disable());
return rsocket.build();
}
}
@Controller
static class ServerController {
private List<String> payloads = new ArrayList<>();
@MessageMapping("**")
String retrieveMono(String payload) {
add(payload);
return "Hi " + payload;
}
private void add(String p) {
this.payloads.add(p);
}
}
}
@@ -74,7 +74,8 @@ public class HelloRSocketITests {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
@@ -87,7 +87,8 @@ public class HelloRSocketObservationITests {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
@@ -74,7 +74,8 @@ public class HelloRSocketWithWebFluxITests {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
@@ -86,7 +86,8 @@ public class JwtITests {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
@@ -81,7 +81,8 @@ public class RSocketMessageHandlerConnectionITests {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))
@@ -79,7 +79,8 @@ public class RSocketMessageHandlerITests {
// @formatter:off
this.server = RSocketServer.create()
.payloadDecoder(PayloadDecoder.ZERO_COPY)
.interceptors((registry) -> registry.forSocketAcceptor(this.interceptor)
.interceptors((registry) ->
registry.forSocketAcceptor(this.interceptor)
)
.acceptor(this.handler.responder())
.bind(TcpServerTransport.create("localhost", 0))

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