1
0
mirror of synced 2026-07-10 05:10:03 +00:00

Compare commits

..

55 Commits

Author SHA1 Message Date
Robert Winch 4f52b71be8 Bump org.hibernate.orm:hibernate-core from 6.6.40.Final to 6.6.41.Final 2026-01-21 15:31:04 -06:00
Robert Winch 398430f672 Bump ch.qos.logback:logback-classic from 1.5.24 to 1.5.25 2026-01-21 15:30:50 -06:00
dependabot[bot] e54bb9f3f5 Bump io.spring.develocity.conventions from 0.0.24 to 0.0.25
Bumps [io.spring.develocity.conventions](https://github.com/spring-io/develocity-conventions) from 0.0.24 to 0.0.25.
- [Release notes](https://github.com/spring-io/develocity-conventions/releases)
- [Commits](https://github.com/spring-io/develocity-conventions/compare/v0.0.24...v0.0.25)

---
updated-dependencies:
- dependency-name: io.spring.develocity.conventions
  dependency-version: 0.0.25
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-21 03:06:52 +00:00
dependabot[bot] aa21e62fb8 Bump ch.qos.logback:logback-classic from 1.5.24 to 1.5.25
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.24 to 1.5.25.
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.24...v_1.5.25)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-19 03:21:35 +00:00
dependabot[bot] b58187082a Bump org.hibernate.orm:hibernate-core from 6.6.40.Final to 6.6.41.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.40.Final to 6.6.41.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.41/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.40...6.6.41)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-19 03:21:26 +00:00
songhee fee6a9bb0e docs: add CurrentSecurityContext section and link references
Signed-off-by: songhee <songhee9327@gmail.com>
2026-01-15 12:31:58 -07:00
github-actions[bot] e588a3528f Update Antora Spring UI to v0.4.25 2026-01-09 15:22:22 -06:00
Robert Winch 334df75c39 Bump org.hibernate.orm:hibernate-core from 6.6.39.Final to 6.6.40.Final 2026-01-08 12:58:34 -06:00
Robert Winch 11b601159d Bump org.apache.maven:maven-resolver-provider from 3.9.11 to 3.9.12 2026-01-08 12:58:25 -06:00
dependabot[bot] 8521b7d5a6 Bump ch.qos.logback:logback-classic from 1.5.22 to 1.5.24
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.22 to 1.5.24.
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.22...v_1.5.24)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-07 03:21:38 +00:00
dependabot[bot] 00fbe052f4 Bump org.apache.maven:maven-resolver-provider from 3.9.11 to 3.9.12
Bumps org.apache.maven:maven-resolver-provider from 3.9.11 to 3.9.12.

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-22 03:10:38 +00:00
dependabot[bot] 5023cc7e4c Bump org.hibernate.orm:hibernate-core from 6.6.39.Final to 6.6.40.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.39.Final to 6.6.40.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.40/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.39...6.6.40)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-22 03:10:27 +00:00
Robert Winch 3cd8dc5c44 Bump io.micrometer:micrometer-observation from 1.14.13 to 1.14.14 2025-12-19 16:47:42 -06:00
Robert Winch 97342f83fd Bump org.springframework.ldap:spring-ldap-core from 3.2.15 to 3.2.16 2025-12-19 16:47:19 -06:00
Robert Winch dfe062b390 Bump org.hibernate.orm:hibernate-core from 6.6.38.Final to 6.6.39.Final 2025-12-19 16:45:18 -06:00
Robert Winch 703a81390e Bump org.springframework.data:spring-data-bom from 2024.1.12 to 2024.1.13 2025-12-19 16:45:10 -06:00
dependabot[bot] 18c4c4c528 Bump org-aspectj from 1.9.25 to 1.9.25.1
Bumps `org-aspectj` from 1.9.25 to 1.9.25.1.

Updates `org.aspectj:aspectjrt` from 1.9.25 to 1.9.25.1
- [Release notes](https://github.com/eclipse/org.aspectj/releases)
- [Commits](https://github.com/eclipse/org.aspectj/commits)

Updates `org.aspectj:aspectjweaver` from 1.9.25 to 1.9.25.1
- [Release notes](https://github.com/eclipse/org.aspectj/releases)
- [Commits](https://github.com/eclipse/org.aspectj/commits)

---
updated-dependencies:
- dependency-name: org.aspectj:aspectjrt
  dependency-version: 1.9.25.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.aspectj:aspectjweaver
  dependency-version: 1.9.25.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-17 03:12:14 +00:00
dependabot[bot] 6d722edab1 Bump org.springframework.data:spring-data-bom
Bumps [org.springframework.data:spring-data-bom](https://github.com/spring-projects/spring-data-bom) from 2024.1.12 to 2024.1.13.
- [Release notes](https://github.com/spring-projects/spring-data-bom/releases)
- [Commits](https://github.com/spring-projects/spring-data-bom/compare/2024.1.12...2024.1.13)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-16 03:13:17 +00:00
dependabot[bot] e712531555 Bump org.hibernate.orm:hibernate-core from 6.6.38.Final to 6.6.39.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.38.Final to 6.6.39.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.39/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.38...6.6.39)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-16 03:13:08 +00:00
dependabot[bot] 100c07de98 Bump org.springframework.ldap:spring-ldap-core from 3.2.15 to 3.2.16
Bumps [org.springframework.ldap:spring-ldap-core](https://github.com/spring-projects/spring-ldap) from 3.2.15 to 3.2.16.
- [Release notes](https://github.com/spring-projects/spring-ldap/releases)
- [Changelog](https://github.com/spring-projects/spring-ldap/blob/main/changelog.txt)
- [Commits](https://github.com/spring-projects/spring-ldap/compare/3.2.15...3.2.16)

---
updated-dependencies:
- dependency-name: org.springframework.ldap:spring-ldap-core
  dependency-version: 3.2.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-16 03:12:14 +00:00
dependabot[bot] 279afe3e28 Bump io.micrometer:micrometer-observation from 1.14.13 to 1.14.14
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.13 to 1.14.14.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.13...v1.14.14)

---
updated-dependencies:
- dependency-name: io.micrometer:micrometer-observation
  dependency-version: 1.14.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 15:54:23 +00:00
Rob Winch b7b859cd9a Bump org-apache-maven-resolver from 1.9.24 to 1.9.25 2025-12-15 09:46:59 -06:00
Rob Winch b83f682154 Bump io.projectreactor:reactor-bom from 2024.0.12 to 2024.0.13 2025-12-15 09:46:56 -06:00
Rob Winch aca1643284 Bump ch.qos.logback:logback-classic from 1.5.21 to 1.5.22 2025-12-15 09:46:54 -06:00
dependabot[bot] cf2114e36e Bump org.springframework:spring-framework-bom from 6.2.14 to 6.2.15
Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.2.14 to 6.2.15.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.14...v6.2.15)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-12 03:15:32 +00:00
dependabot[bot] ecd17a9ee0 Bump ch.qos.logback:logback-classic from 1.5.21 to 1.5.22
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.21 to 1.5.22.
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.21...v_1.5.22)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-12 03:15:25 +00:00
dependabot[bot] ef5cdb50cc Bump io.projectreactor:reactor-bom from 2024.0.12 to 2024.0.13
Bumps [io.projectreactor:reactor-bom](https://github.com/reactor/reactor) from 2024.0.12 to 2024.0.13.
- [Release notes](https://github.com/reactor/reactor/releases)
- [Commits](https://github.com/reactor/reactor/compare/2024.0.12...2024.0.13)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-10 03:14:23 +00:00
dependabot[bot] b2e2d74cab Bump org-apache-maven-resolver from 1.9.24 to 1.9.25
Bumps `org-apache-maven-resolver` from 1.9.24 to 1.9.25.

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

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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-10 03:14:06 +00:00
dependabot[bot] d3a55291bc Bump org.hibernate.orm:hibernate-core from 6.6.36.Final to 6.6.38.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.36.Final to 6.6.38.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.38/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.36...6.6.38)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-01 03:26:20 +00:00
Rob Winch e584196c1d Fix typos in documentation depenendencies->dependencies
Closes gh-18197
2025-11-21 10:17:49 -06:00
Peter Potrowl 5baff27ffb Fix typo in ldap.adoc
Signed-off-by: Peter Potrowl <peter.potrowl@gmail.com>
2025-11-21 10:12:20 -06:00
Peter Potrowl 39aaf25b60 Fix typo in passkeys.adoc
Signed-off-by: Peter Potrowl <peter.potrowl@gmail.com>
2025-11-21 10:12:20 -06:00
Rob Winch 8ecc4a9157 Bump org.hibernate.orm:hibernate-core from 6.6.34.Final to 6.6.36.Final 2025-11-21 09:55:00 -06:00
Rob Winch af33ace82f Bump ch.qos.logback:logback-classic from 1.5.20 to 1.5.21 2025-11-21 09:54:58 -06:00
dependabot[bot] ddebff043d Bump org.springframework:spring-framework-bom from 6.2.13 to 6.2.14
Bumps [org.springframework:spring-framework-bom](https://github.com/spring-projects/spring-framework) from 6.2.13 to 6.2.14.
- [Release notes](https://github.com/spring-projects/spring-framework/releases)
- [Commits](https://github.com/spring-projects/spring-framework/compare/v6.2.13...v6.2.14)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-21 03:13:00 +00:00
Josh Cummings 29c63bee69 Stop Deploying JavaDoc Outside of Antora
Closes gh-18198
2025-11-19 15:23:50 -07:00
dependabot[bot] 944322932a Bump ch.qos.logback:logback-classic from 1.5.20 to 1.5.21
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.20 to 1.5.21.
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.20...v_1.5.21)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-18 03:15:36 +00:00
dependabot[bot] affa36b9bb Bump org.hibernate.orm:hibernate-core from 6.6.34.Final to 6.6.36.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.34.Final to 6.6.36.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.36/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.34...6.6.36)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-18 03:15:13 +00:00
github-actions[bot] 9ff2d96088 Next development version 2025-11-17 17:36:38 +00:00
github-actions[bot] 644901fed5 Release 6.4.13 2025-11-17 17:05:12 +00:00
Rob Winch 09805317e6 Update Spring Data 2024.1.12
Closes gh-18179
2025-11-17 10:27:01 -06:00
Rob Winch 4ab933803f Update to Reactor 2024.0.12
This aligns with Spring Framework's version of Reactor

Closes gh-18178
2025-11-17 10:26:09 -06:00
Rob Winch d2b1cb572f Update to Spring Framework 6.2.13
Closes gh-18177
2025-11-17 10:25:21 -06:00
Rob Winch d0166004aa Bump com.fasterxml.jackson:jackson-bom from 2.18.4.1 to 2.18.5 2025-11-17 09:33:40 -06:00
Rob Winch 9f96fbcda0 Bump org.hibernate.orm:hibernate-core from 6.6.33.Final to 6.6.34.Final 2025-11-17 09:33:37 -06:00
Rob Winch ccffb48fd1 Bump org-aspectj from 1.9.24 to 1.9.25 2025-11-17 09:33:35 -06:00
Rob Winch d0fcdebe88 Bump io.spring.gradle:spring-security-release-plugin from 1.0.11 to 1.0.13 2025-11-17 09:33:33 -06:00
dependabot[bot] f997e22d9d Bump io.micrometer:micrometer-observation from 1.14.12 to 1.14.13
Bumps [io.micrometer:micrometer-observation](https://github.com/micrometer-metrics/micrometer) from 1.14.12 to 1.14.13.
- [Release notes](https://github.com/micrometer-metrics/micrometer/releases)
- [Commits](https://github.com/micrometer-metrics/micrometer/compare/v1.14.12...v1.14.13)

---
updated-dependencies:
- dependency-name: io.micrometer:micrometer-observation
  dependency-version: 1.14.13
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-07 03:14:59 +00:00
dependabot[bot] ff908c4d7c 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.11 to 1.0.13.
- [Release notes](https://github.com/spring-io/spring-security-release-tools/releases)
- [Commits](https://github.com/spring-io/spring-security-release-tools/compare/v1.0.11...v1.0.13)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-05 03:15:17 +00:00
dependabot[bot] 521f533fc4 Bump org-aspectj from 1.9.24 to 1.9.25
Bumps `org-aspectj` from 1.9.24 to 1.9.25.

Updates `org.aspectj:aspectjrt` from 1.9.24 to 1.9.25
- [Release notes](https://github.com/eclipse/org.aspectj/releases)
- [Commits](https://github.com/eclipse/org.aspectj/commits)

Updates `org.aspectj:aspectjweaver` from 1.9.24 to 1.9.25
- [Release notes](https://github.com/eclipse/org.aspectj/releases)
- [Commits](https://github.com/eclipse/org.aspectj/commits)

---
updated-dependencies:
- dependency-name: org.aspectj:aspectjrt
  dependency-version: 1.9.25
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.aspectj:aspectjweaver
  dependency-version: 1.9.25
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-05 03:14:41 +00:00
Josh Cummings 532d0bef14 Add Test to Confirm 72-byte BCrypt Password Limit
Closes gh-18133
2025-11-04 14:04:02 -07:00
Daniel Garnier-Moiroux fed6df5167 Default WebAuthnConfigurer#rpName to rpId
In WebAuthn L3 spec, PublicKeyCredentialEntity.name is deprecated:

> This member is deprecated because many clients do not display it,
> but it remains a required dictionary member for backwards compatibility.
> Relying Parties MAY, as a safe default, set this equal to the RP ID.

Source: https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialentity

Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
2025-11-04 11:16:22 -06:00
Daniel Garnier-Moiroux 4feeb0f843 Docs: document effects of disabling CORS configurer
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
2025-11-04 10:23:46 -06:00
dependabot[bot] 8b0689cbb8 Bump org.hibernate.orm:hibernate-core from 6.6.33.Final to 6.6.34.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 6.6.33.Final to 6.6.34.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/6.6.34/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/6.6.33...6.6.34)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-28 03:15:58 +00:00
dependabot[bot] 28e158d1cb Bump com.fasterxml.jackson:jackson-bom from 2.18.4.1 to 2.18.5
Bumps [com.fasterxml.jackson:jackson-bom](https://github.com/FasterXML/jackson-bom) from 2.18.4.1 to 2.18.5.
- [Commits](https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.18.4.1...jackson-bom-2.18.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-28 03:15:38 +00:00
3475 changed files with 53259 additions and 171944 deletions
-2
View File
@@ -1,2 +0,0 @@
require:
members: false
+26 -37
View File
@@ -3,12 +3,9 @@ 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
target-branch: 6.3.x
directory: /
schedule:
interval: daily
@@ -18,12 +15,10 @@ updates:
- '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
@@ -34,34 +29,6 @@ updates:
update-types:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: gradle
target-branch: 6.4.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: main
directory: /
@@ -73,12 +40,10 @@ updates:
- '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
@@ -92,7 +57,31 @@ updates:
- dependency-name: '*'
update-types:
- 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'
- package-ecosystem: github-actions
target-branch: main
directory: /
schedule:
interval: weekly
labels:
- 'type: task'
- 'in: build'
- package-ecosystem: github-actions
target-branch: docs-build
directory: /
schedule:
interval: weekly
labels:
- 'type: task'
- 'in: build'
- package-ecosystem: npm
target-branch: docs-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 }}
@@ -27,36 +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@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 -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@v4
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@v2
with:
java-version: 17
distribution: temurin
- name: Check samples project
env:
LOCAL_REPOSITORY_PATH: ${{ github.workspace }}/build/publications/repos
SAMPLES_DIR: ../spring-security-samples
run: |
# Extract version from gradle.properties
version=$(cat gradle.properties | grep "version=" | awk -F'=' '{print $2}')
# Extract samplesBranch from gradle.properties
samples_branch=$(cat gradle.properties | grep "samplesBranch=" | awk -F'=' '{print $2}')
./gradlew publishMavenJavaPublicationToLocalRepository
./gradlew cloneRepository -PrepositoryName="spring-projects/spring-security-samples" -Pref="$samples_branch" -PcloneOutputDirectory="$SAMPLES_DIR"
./gradlew --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]
needs: [ build, test, check-samples ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@v1
with:
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
default-publish-milestones-central: true
secrets: inherit
deploy-docs:
name: Deploy Docs
needs: [ build ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-docs.yml@v1
with:
should-deploy-docs: ${{ needs.build.outputs.should-deploy-artifacts }}
secrets: inherit
deploy-schema:
name: Deploy Schema
needs: [ build ]
needs: [ build, test, check-samples ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@v1
with:
should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }}
secrets: inherit
perform-release:
name: Perform Release
needs: [ deploy-artifacts, deploy-docs, deploy-schema ]
needs: [ deploy-artifacts, deploy-schema ]
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@v1
with:
should-perform-release: ${{ needs.deploy-artifacts.outputs.artifacts-deployed }}
project-version: ${{ needs.deploy-artifacts.outputs.project-version }}
milestone-repo-url: https://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
-41
View File
@@ -1,41 +0,0 @@
name: Finalize Release
on:
workflow_dispatch: # Manual trigger
env:
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
permissions:
contents: read
jobs:
project-version:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.project-version.outputs.version }}
steps:
- id: project-version
run: echo "version=$(grep '^version=' gradle.properties | cut -d'=' -f2)" >> $GITHUB_OUTPUT
perform-release:
name: Perform Release
needs: [ project-version ]
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@v1
with:
should-perform-release: true
project-version: ${{ needs.project-version.outputs.version }}
milestone-repo-url: https://repo1.maven.org/maven2
release-repo-url: https://repo1.maven.org/maven2
artifact-path: org/springframework/security/spring-security-core
slack-announcing-id: spring-security-announcing
secrets: inherit
send-notification:
name: Send Notification
needs: [ perform-release ]
if: ${{ !success() }}
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
@@ -26,7 +26,7 @@ jobs:
java-version: '17'
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/gradle-build-action@v2
uses: gradle/gradle-build-action@v3
- name: Upgrade Wrappers
run: ./gradlew clean upgradeGradleWrapperAll --continue -Porg.gradle.java.installations.auto-download=false
env:
+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.3.x, 6.2.x, 5.8.x ]
runs-on: ubuntu-latest
steps:
- name: Checkout
-1
View File
@@ -25,7 +25,6 @@ atlassian-ide-plugin.xml
s101plugin.state
.attach_pid*
.~lock.*#
.kotlin/
!.idea/checkstyle-idea.xml
!.idea/externalDependencies.xml
+25 -17
View File
@@ -79,10 +79,7 @@ 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`.
To format the code as well as check the style, run `./gradlew format check`.
[[submit-a-pull-request]]
=== Submit a Pull Request
@@ -92,30 +89,41 @@ We are excited for your pull request! :heart:
Please do your best to follow these steps.
Don't worry if you don't get them all correct the first time, we will help you.
1. [[sign-cla]] All commits must include a __Signed-off-by__ trailer at the end of each commit message to indicate that the contributor agrees to the Developer Certificate of Origin.
For additional details, please refer to the blog post https://spring.io/blog/2025/01/06/hello-dco-goodbye-cla-simplifying-contributions-to-spring[Hello DCO, Goodbye CLA: Simplifying Contributions to Spring].
2. [[create-an-issue-list]] Must you https://github.com/spring-projects/spring-security/issues/new/choose[create an issue] first? No, but it is recommended for features and larger bug fixes. It's easier discuss with the team first to determine the right fix or enhancement.
[[sign-cla]]
1. If you have not previously done so, please sign the https://cla.spring.io/sign/spring[Contributor License Agreement].
You will be reminded automatically when you submit the PR.
[[create-an-issue]]
1. Must you https://github.com/spring-projects/spring-security/issues/new/choose[create an issue] first? No, but it is recommended for features and larger bug fixes. It's easier discuss with the team first to determine the right fix or enhancement.
For typos and straightforward bug fixes, starting with a pull request is encouraged.
Please include a description for context and motivation.
Note that the team may close your pull request if it's not a fit for the project.
3. [[choose-a-branch]] Always check out the branch indicated in the milestone and submit pull requests against it (for example, for milestone `5.8.3` use the `5.8.x` branch).
[[choose-a-branch]]
1. Always check out the branch indicated in the milestone and submit pull requests against it (for example, for milestone `5.8.3` use the `5.8.x` branch).
If there is no milestone, choose `main`.
Once merged, the fix will be forwarded-ported to applicable branches including `main`.
4. [[create-a-local-branch]] Create a local branch
[[create-a-local-branch]]
1. Create a local branch
If this is for an issue, consider a branch name with the issue number, like `gh-22276`.
5. [[write-tests]] Add documentation and JUnit Tests for your changes.
6. [[update-copyright]] In all files you edited, if the copyright header is of the form 2002-20xx, update the final copyright year to the current year.
7. [[add-since]] If on `main`, add `@since` JavaDoc attributes to new public APIs that your PR adds
8. [[change-rnc]] If you are updating the XSD, please instead update the RNC file and then run `./gradlew :spring-security-config:rncToXsd`.
9. [[format-code]] For each commit, build the code using `./gradlew format && ./gradlew check`.
[[write-tests]]
1. Add documentation and JUnit Tests for your changes.
[[update-copyright]]
1. In all files you edited, if the copyright header is of the form 2002-20xx, update the final copyright year to the current year.
[[add-since]]
1. If on `main`, add `@since` JavaDoc attributes to new public APIs that your PR adds
[[change-rnc]]
1. If you are updating the XSD, please instead update the RNC file and then run `./gradlew :spring-security-config:rncToXsd`.
[[format-code]]
1. For each commit, build the code using `./gradlew format check`.
This command ensures the code meets most of <<code-style,the style guide>>; a notable exception is import order.
10. [[commit-atomically]] Choose the granularity of your commits consciously and squash commits that represent
[[commit-atomically]]
1. Choose the granularity of your commits consciously and squash commits that represent
multiple edits or corrections of the same logical change.
See https://git-scm.com/book/en/Git-Tools-Rewriting-History[Rewriting History section of Pro Git] for an overview of streamlining the commit history.
11. [[format-commit-messages]] Format commit messages using 55 characters for the subject line, 72 characters per line
[[format-commit-messages]]
1. Format commit messages using 55 characters for the subject line, 72 characters per line
for the description, followed by the issue fixed, for example, `Closes gh-22276`.
See the https://git-scm.com/book/en/Distributed-Git-Contributing-to-a-Project#Commit-Guidelines[Commit Guidelines section of Pro Git] for best practices around commit messages, and use `git log` to see some examples.
Favor imperative tense over present tense (use "Fix" instead of "Fixes"); avoid past tense (use "Fix" instead of "Fixed").
Present tense is preferred.
+
[indent=0]
----
-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
@@ -96,11 +96,7 @@ import org.springframework.util.StringUtils;
* All comparisons and prefixes are case sensitive.
*
* @author Ben Alex
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
* annotations may also prove useful, for example
* {@code @PreAuthorize("hasPermission(#id, ObjectsReturnType.class, read)")}
*/
@Deprecated
public class AclEntryVoter extends AbstractAclVoter {
private static final Log logger = LogFactory.getLog(AclEntryVoter.class);
@@ -20,7 +20,6 @@ import java.util.List;
import org.springframework.security.access.AfterInvocationProvider;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.acls.AclPermissionEvaluator;
import org.springframework.security.acls.domain.ObjectIdentityRetrievalStrategyImpl;
import org.springframework.security.acls.domain.SidRetrievalStrategyImpl;
import org.springframework.security.acls.model.Acl;
@@ -40,11 +39,7 @@ import org.springframework.util.ObjectUtils;
* services.
*
* @author Ben Alex
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
* annotations may also prove useful, for example
* {@code @PostAuthorize("hasPermission(filterObject, read)")}
*/
@Deprecated
public abstract class AbstractAclProvider implements AfterInvocationProvider {
protected final AclService aclService;
@@ -26,7 +26,6 @@ import org.springframework.core.log.LogMessage;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.AuthorizationServiceException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.acls.AclPermissionEvaluator;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.core.Authentication;
@@ -63,11 +62,7 @@ import org.springframework.security.core.Authentication;
*
* @author Ben Alex
* @author Paulo Neves
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
* annotations may also prove useful, for example
* {@code @PostFilter("hasPermission(filterObject, read)")}
*/
@Deprecated
public class AclEntryAfterInvocationCollectionFilteringProvider extends AbstractAclProvider {
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationCollectionFilteringProvider.class);
@@ -27,7 +27,6 @@ import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.acls.AclPermissionEvaluator;
import org.springframework.security.acls.model.AclService;
import org.springframework.security.acls.model.Permission;
import org.springframework.security.core.Authentication;
@@ -60,12 +59,7 @@ import org.springframework.security.core.SpringSecurityMessageSource;
* granted and <code>null</code> will be returned.
* <p>
* All comparisons and prefixes are case sensitive.
*
* @deprecated please use {@link AclPermissionEvaluator} instead. Spring Method Security
* annotations may also prove useful, for example
* {@code @PostAuthorize("hasPermission(filterObject, read)")}
*/
@Deprecated
public class AclEntryAfterInvocationProvider extends AbstractAclProvider implements MessageSourceAware {
protected static final Log logger = LogFactory.getLog(AclEntryAfterInvocationProvider.class);
@@ -32,9 +32,7 @@ import org.springframework.core.log.LogMessage;
*
* @author Ben Alex
* @author Paulo Neves
* @deprecated please see {@code PostFilter}
*/
@Deprecated
class ArrayFilterer<T> implements Filterer<T> {
protected static final Log logger = LogFactory.getLog(ArrayFilterer.class);
@@ -31,9 +31,7 @@ import org.springframework.core.log.LogMessage;
*
* @author Ben Alex
* @author Paulo Neves
* @deprecated please see {@code PostFilter}
*/
@Deprecated
class CollectionFilterer<T> implements Filterer<T> {
protected static final Log logger = LogFactory.getLog(CollectionFilterer.class);
@@ -23,9 +23,7 @@ import java.util.Iterator;
*
* @author Ben Alex
* @author Paulo Neves
* @deprecated please use {@code PreFilter} and {@code @PostFilter} instead
*/
@Deprecated
interface Filterer<T> extends Iterable<T> {
/**
@@ -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
@@ -100,8 +100,8 @@ public class JdbcAclService implements AclService {
@Override
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
Object[] args = { parentIdentity.getIdentifier().toString(), parentIdentity.getType() };
List<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql,
(rs, rowNum) -> mapObjectIdentityRow(rs), args);
List<ObjectIdentity> objects = this.jdbcOperations.query(this.findChildrenSql, args,
(rs, rowNum) -> mapObjectIdentityRow(rs));
return (!objects.isEmpty()) ? objects : null;
}
@@ -190,7 +190,8 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
* @return the primary key or null if not found
*/
protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate, Class idType) {
List<Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, Long.class, type);
List<Long> classIds = this.jdbcOperations.queryForList(this.selectClassPrimaryKey, new Object[] { type },
Long.class);
if (!classIds.isEmpty()) {
return classIds.get(0);
@@ -241,8 +242,8 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
* @return the primary key or null if not found
*/
protected Long createOrRetrieveSidPrimaryKey(String sidName, boolean sidIsPrincipal, boolean allowCreate) {
List<Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey, Long.class, sidIsPrincipal,
sidName);
List<Long> sidIds = this.jdbcOperations.queryForList(this.selectSidPrimaryKey,
new Object[] { sidIsPrincipal, sidName }, Long.class);
if (!sidIds.isEmpty()) {
return sidIds.get(0);
}
@@ -109,7 +109,7 @@ public class JdbcAclServiceTests {
List<ObjectIdentity> result = new ArrayList<>();
result.add(new ObjectIdentityImpl(Object.class, "5577"));
Object[] args = { "1", "org.springframework.security.acls.jdbc.JdbcAclServiceTests$MockLongIdDomainObject" };
given(this.jdbcOperations.query(anyString(), any(RowMapper.class), eq(args))).willReturn(result);
given(this.jdbcOperations.query(anyString(), eq(args), any(RowMapper.class))).willReturn(result);
ObjectIdentity objectIdentity = new ObjectIdentityImpl(MockLongIdDomainObject.class, 1L);
List<ObjectIdentity> objectIdentities = this.aclService.findChildren(objectIdentity);
assertThat(objectIdentities).hasSize(1);
-2
View File
@@ -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"
+1 -1
View File
@@ -42,7 +42,7 @@ springRelease {
weekOfMonth = 3
dayOfWeek = 1
referenceDocUrl = "https://docs.spring.io/spring-security/reference/{version}/index.html"
apiDocUrl = "https://docs.spring.io/spring-security/site/docs/{version}/api/"
apiDocUrl = "https://docs.spring.io/spring-security/reference/{version}/api/java/index.html"
replaceSnapshotVersionInReferenceDocUrl = true
}
+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'
}
@@ -1,82 +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 io.spring.gradle.convention
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.tasks.bundling.Zip
import org.gradle.api.Plugin
import org.gradle.api.Project
public class DeployDocsPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getPluginManager().apply('org.hidetake.ssh')
project.ssh.settings {
knownHosts = allowAnyHosts
}
project.remotes {
docs {
role 'docs'
if (project.hasProperty('deployDocsHost')) {
host = project.findProperty('deployDocsHost')
} else {
host = 'docs.af.pivotal.io'
}
retryCount = 5 // retry 5 times (default is 0)
retryWaitSec = 10 // wait 10 seconds between retries (default is 0)
user = project.findProperty('deployDocsSshUsername')
if (project.hasProperty('deployDocsSshKeyPath')) {
identity = project.file(project.findProperty('deployDocsSshKeyPath'))
} else if (project.hasProperty('deployDocsSshKey')) {
identity = project.findProperty('deployDocsSshKey')
}
if(project.hasProperty('deployDocsSshPassphrase')) {
passphrase = project.findProperty('deployDocsSshPassphrase')
}
}
}
project.task('deployDocs') {
dependsOn 'docsZip'
doFirst {
project.ssh.run {
session(project.remotes.docs) {
def now = System.currentTimeMillis()
def name = project.rootProject.name
def version = project.rootProject.version
def tempPath = "/tmp/${name}-${now}-docs/".replaceAll(' ', '_')
execute "mkdir -p $tempPath"
project.tasks.docsZip.outputs.each { o ->
put from: o.files, into: tempPath
}
execute "unzip $tempPath*.zip -d $tempPath"
def extractPath = "/var/www/domains/spring.io/docs/htdocs/autorepo/docs/${name}/${version}/"
execute "rm -rf $extractPath"
execute "mkdir -p $extractPath"
execute "mv $tempPath/docs/* $extractPath"
execute "chmod -R g+w $extractPath"
}
}
}
}
}
}
@@ -17,7 +17,6 @@ public class DocsPlugin implements Plugin<Project> {
PluginManager pluginManager = project.getPluginManager();
pluginManager.apply(BasePlugin);
pluginManager.apply(DeployDocsPlugin);
pluginManager.apply(JavadocApiPlugin);
Task docsZip = project.tasks.create('docsZip', Zip) {
@@ -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,13 +32,10 @@ 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'
from xsdFile.path
@@ -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;
+1 -11
View File
@@ -30,16 +30,6 @@ ossrh: {
}
}
},
docs: {
stage('Deploy Docs') {
node {
checkout scm
withCredentials([file(credentialsId: 'docs.spring.io-jenkins_private_ssh_key', variable: 'DEPLOY_SSH_KEY')]) {
sh "./gradlew deployDocs -PdeployDocsSshKeyPath=$DEPLOY_SSH_KEY -PdeployDocsSshUsername=$SPRING_DOCS_USERNAME --refresh-dependencies --no-daemon --stacktrace"
}
}
}
},
schema: {
stage('Deploy Schema') {
node {
@@ -49,4 +39,4 @@ schema: {
}
}
}
}
}
-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,11 @@ 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.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 +189,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,8 +215,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
public CasAuthenticationFilter() {
super("/login/cas");
RequestMatcher processUri = pathPattern("/login/cas");
setRequiresAuthenticationRequestMatcher(processUri);
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
setSecurityContextRepository(this.securityContextRepository);
}
@@ -245,7 +241,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
@@ -323,20 +319,8 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
super.setAuthenticationFailureHandler(new CasAuthenticationFailureHandler(failureHandler));
}
/**
* Use this {@code RequestMatcher} to match proxy receptor requests. Without setting
* this matcher, {@link CasAuthenticationFilter} will not capture any proxy receptor
* requets.
* @param proxyReceptorMatcher the {@link RequestMatcher} to use
* @since 6.5
*/
public final void setProxyReceptorMatcher(RequestMatcher proxyReceptorMatcher) {
Assert.notNull(proxyReceptorMatcher, "proxyReceptorMatcher cannot be null");
this.proxyReceptorMatcher = proxyReceptorMatcher;
}
public final void setProxyReceptorUrl(final String proxyReceptorUrl) {
this.proxyReceptorMatcher = pathPattern(proxyReceptorUrl);
this.proxyReceptorMatcher = new AntPathRequestMatcher("/**" + proxyReceptorUrl);
}
public final void setProxyGrantingTicketStorage(final ProxyGrantingTicketStorage proxyGrantingTicketStorage) {
@@ -423,7 +407,6 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
* @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";
@@ -54,9 +54,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 +78,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();
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 +103,24 @@ public class CasAuthenticationFilterTests {
String url = "/login/cas";
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setFilterProcessesUrl(url);
MockHttpServletRequest request = post(url).build();
MockHttpServletRequest request = new MockHttpServletRequest();
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 +132,11 @@ public class CasAuthenticationFilterTests {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
filter.setFilterProcessesUrl(url);
filter.setServiceProperties(properties);
MockHttpServletRequest request = post(url).build();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setServletPath(url);
assertThat(filter.requiresAuthentication(request, response)).isTrue();
request = post("/other").build();
request.setServletPath("/other");
assertThat(filter.requiresAuthentication(request, response)).isFalse();
request.setParameter(properties.getArtifactParameter(), "value");
assertThat(filter.requiresAuthentication(request, response)).isTrue();
@@ -152,8 +154,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 +170,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();
request.setParameter("ticket", "ST-1-123");
request.setServletPath("/authenticate");
MockHttpServletResponse response = new MockHttpServletResponse();
FilterChain chain = mock(FilterChain.class);
CasAuthenticationFilter filter = new CasAuthenticationFilter();
@@ -193,9 +198,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);
@@ -260,18 +266,4 @@ public class CasAuthenticationFilterTests {
verify(securityContextRepository).setContext(any(SecurityContext.class));
}
@Test
public void requiresAuthenticationWhenProxyRequestMatcherThenMatches() {
CasAuthenticationFilter filter = new CasAuthenticationFilter();
MockHttpServletRequest request = get("/pgtCallback").build();
MockHttpServletResponse response = new MockHttpServletResponse();
assertThat(filter.requiresAuthentication(request, response)).isFalse();
filter.setProxyReceptorMatcher(pathPattern(request.getServletPath()));
assertThat(filter.requiresAuthentication(request, response)).isFalse();
filter.setProxyGrantingTicketStorage(mock(ProxyGrantingTicketStorage.class));
assertThat(filter.requiresAuthentication(request, response)).isTrue();
request = get("/other").build();
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;
+31 -8
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)
@@ -156,6 +158,15 @@ tasks.named('sourcesJar', Jar).configure {
}
}
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);
}
@@ -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))
@@ -79,7 +79,8 @@ public class SimpleAuthenticationITests {
// @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))
@@ -17,6 +17,7 @@
package org.springframework.security.config.ldap;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -37,11 +38,12 @@ 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.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator;
import org.springframework.security.ldap.userdetails.UserDetailsContextMapper;
@@ -80,7 +82,8 @@ public class LdapBindAuthenticationManagerFactoryITests {
this.spring.register(CustomAuthoritiesPopulatorConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withRoles("EXTRA"));
.andExpect(
authenticated().withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_EXTRA"))));
}
@Test
@@ -91,7 +94,8 @@ public class LdapBindAuthenticationManagerFactoryITests {
this.spring.register(CustomAuthoritiesMapperConfig.class).autowire();
this.mockMvc.perform(formLogin().user("bob").password("bobspassword"))
.andExpect(authenticated().withRoles("CUSTOM"));
.andExpect(
authenticated().withAuthorities(Collections.singleton(new SimpleGrantedAuthority("ROLE_CUSTOM"))));
}
@Test
@@ -222,18 +226,18 @@ public class LdapBindAuthenticationManagerFactoryITests {
@EnableWebSecurity
abstract static class BaseLdapServerConfig implements DisposableBean {
private UnboundIdContainer container;
private ApacheDSContainer container;
@Bean
UnboundIdContainer ldapServer() {
this.container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
ApacheDSContainer ldapServer() throws Exception {
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
this.container.setPort(0);
return this.container;
}
@Bean
BaseLdapPathContextSource contextSource(UnboundIdContainer container) {
int port = container.getPort();
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
int port = container.getLocalPort();
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
}
@@ -31,7 +31,7 @@ import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.formLogin;
@@ -93,18 +93,18 @@ public class LdapPasswordComparisonAuthenticationManagerFactoryITests {
@EnableWebSecurity
abstract static class BaseLdapServerConfig implements DisposableBean {
private UnboundIdContainer container;
private ApacheDSContainer container;
@Bean
UnboundIdContainer ldapServer() {
this.container = new UnboundIdContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
ApacheDSContainer ldapServer() throws Exception {
this.container = new ApacheDSContainer("dc=springframework,dc=org", "classpath:/test-server.ldif");
this.container.setPort(0);
return this.container;
}
@Bean
BaseLdapPathContextSource contextSource(UnboundIdContainer container) {
int port = container.getPort();
BaseLdapPathContextSource contextSource(ApacheDSContainer container) {
int port = container.getLocalPort();
return new DefaultSpringSecurityContextSource("ldap://localhost:" + port + "/dc=springframework,dc=org");
}
@@ -56,7 +56,7 @@ public class LdapProviderBeanDefinitionParserTests {
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class);
Authentication auth = authenticationManager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("otherben", "otherbenspassword"));
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword"));
UserDetails ben = (UserDetails) auth.getPrincipal();
assertThat(ben.getAuthorities()).hasSize(3);
}
@@ -127,27 +127,6 @@ public class LdapProviderBeanDefinitionParserTests {
assertThat(auth).isNotNull();
}
@Test
public void supportsShaPasswordEncoder() {
this.appCtx = new InMemoryXmlApplicationContext("""
<ldap-server ldif='classpath:test-server.ldif' port='0'/>
<authentication-manager>
<ldap-authentication-provider user-dn-pattern='uid={0},ou=people'>
<password-compare>
<password-encoder ref='pe' />
</password-compare>
</ldap-authentication-provider>
</authentication-manager>
<b:bean id='pe' class='org.springframework.security.crypto.password.LdapShaPasswordEncoder' />
""");
AuthenticationManager authenticationManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER,
AuthenticationManager.class);
Authentication auth = authenticationManager
.authenticate(UsernamePasswordAuthenticationToken.unauthenticated("ben", "benspassword"));
assertThat(auth).isNotNull();
}
@Test
public void inetOrgContextMapperIsSupported() {
this.appCtx = new InMemoryXmlApplicationContext(
@@ -169,7 +148,7 @@ public class LdapProviderBeanDefinitionParserTests {
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server />" + "<authentication-manager>"
+ " <ldap-authentication-provider user-dn-pattern='uid={0},ou=${udp}' group-search-filter='${gsf}={0}' />"
+ "</authentication-manager>"
+ "<b:bean id='org.springframework.context.support.PropertySourcesPlaceholderConfigurer' class='org.springframework.context.support.PropertySourcesPlaceholderConfigurer' />");
+ "<b:bean id='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer' class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer' />");
ProviderManager providerManager = this.appCtx.getBean(BeanIds.AUTHENTICATION_MANAGER, ProviderManager.class);
assertThat(providerManager.getProviders()).hasSize(1);
@@ -26,7 +26,7 @@ import org.springframework.ldap.core.LdapTemplate;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.util.InMemoryXmlApplicationContext;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.ldap.server.UnboundIdContainer;
import org.springframework.security.ldap.server.ApacheDSContainer;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -92,9 +92,9 @@ public class LdapServerBeanDefinitionParserTests {
@Test
public void defaultLdifFileIsSuccessful() {
this.appCtx = new InMemoryXmlApplicationContext("<ldap-server/>");
UnboundIdContainer dsContainer = this.appCtx.getBean(UnboundIdContainer.class);
ApacheDSContainer dsContainer = this.appCtx.getBean(ApacheDSContainer.class);
assertThat(ReflectionTestUtils.getField(dsContainer, "ldif")).isEqualTo("classpath*:*.ldif");
assertThat(ReflectionTestUtils.getField(dsContainer, "ldifResources")).isEqualTo("classpath*:*.ldif");
}
private int getDefaultPort() throws IOException {
@@ -7,6 +7,7 @@
<logger name="org.springframework.security" level="${sec.log.level:-WARN}"/>
<logger name="org.apache.directory" level="ERROR"/>
<logger name="JdbmTable" level="INFO"/>
<logger name="JdbmIndex" level="INFO"/>
<logger name="org.apache.mina" level="WARN"/>
@@ -54,6 +54,8 @@ public abstract class BeanIds {
public static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = PREFIX + "methodSecurityMetadataSourceAdvisor";
public static final String EMBEDDED_APACHE_DS = PREFIX + "apacheDirectoryServerContainer";
public static final String EMBEDDED_UNBOUNDID = PREFIX + "unboundidServerContainer";
public static final String CONTEXT_SOURCE = PREFIX + "securityContextSource";
@@ -18,7 +18,6 @@ package org.springframework.security.config;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -78,23 +77,26 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
public SecurityNamespaceHandler() {
String coreVersion = SpringSecurityCoreVersion.getVersion();
String configVersion = configVersion();
if (!Objects.equals(coreVersion, configVersion)) {
String message = "You are attempting to run spring-security-core:%s with spring-security-config:%s";
this.logger.error(String.format(message, coreVersion, configVersion));
}
}
private static String configVersion() {
Package pkg = SpringSecurityCoreVersion.class.getPackage();
return (pkg != null) ? pkg.getImplementationVersion() : null;
if (pkg == null || coreVersion == null) {
this.logger.info("Couldn't determine package version information.");
return;
}
String version = pkg.getImplementationVersion();
this.logger.info("Spring Security 'config' module version is " + version);
if (version.compareTo(coreVersion) != 0) {
this.logger
.error("You are running with different versions of the Spring Security 'core' and 'config' modules");
}
}
@Override
public BeanDefinition parse(Element element, ParserContext pc) {
if (!namespaceMatchesVersion(element)) {
pc.getReaderContext()
.fatal("You cannot use any XSD older than spring-security-7.0.xsd. Either change to spring-security.xsd or spring-security-7.0.xsd",
.fatal("You cannot use a spring-security-2.0.xsd or spring-security-3.0.xsd or "
+ "spring-security-3.1.xsd schema or spring-security-3.2.xsd schema or spring-security-4.0.xsd schema "
+ "with Spring Security 6.4. Please update your schema declarations to the 6.4 schema.",
element);
}
String name = pc.getDelegate().getLocalName(element);
@@ -219,7 +221,7 @@ public final class SecurityNamespaceHandler implements NamespaceHandler {
private boolean matchesVersionInternal(Element element) {
String schemaLocation = element.getAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation");
return schemaLocation.matches("(?m).*spring-security-7\\.0.*.xsd.*")
return schemaLocation.matches("(?m).*spring-security-6\\.4.*.xsd.*")
|| schemaLocation.matches("(?m).*spring-security.xsd.*")
|| !schemaLocation.matches("(?m).*spring-security.*");
}
@@ -1,52 +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;
/**
* A {@link Customizer} that allows invocation of code that throws a checked exception.
*
* @param <T> The type of input.
*/
@FunctionalInterface
public interface ThrowingCustomizer<T> extends Customizer<T> {
/**
* Default {@link Customizer#customize(Object)} that wraps any thrown checked
* exceptions (by default in a {@link RuntimeException}).
* @param t the object to customize
*/
default void customize(T t) {
try {
customizeWithException(t);
}
catch (RuntimeException ex) {
throw ex;
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
/**
* Performs the customization on the given object, possibly throwing a checked
* exception.
* @param t the object to customize
* @throws Exception on error
*/
void customizeWithException(T t) throws Exception;
}
@@ -80,6 +80,15 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
this(objectPostProcessor, false);
}
/**
* @deprecated
*/
@Deprecated(since = "6.4", forRemoval = true)
protected AbstractConfiguredSecurityBuilder(
org.springframework.security.config.annotation.ObjectPostProcessor<Object> objectPostProcessor) {
this(objectPostProcessor, false);
}
/***
* Creates a new instance with the provided {@link ObjectPostProcessor}. This post
* processor must support Object since there are many types of objects that may be
@@ -96,10 +105,22 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
}
/**
* Similar to {@link SecurityBuilder#build()} and {@link #getObject()} but checks the
* state to determine if {@link SecurityBuilder#build()} needs to be called first.
* @return the result of {@link SecurityBuilder#build()} or {@link #getObject()}. If
* an error occurs while building, returns null.
* @deprecated
*/
@Deprecated(since = "6.4", forRemoval = true)
protected AbstractConfiguredSecurityBuilder(
org.springframework.security.config.annotation.ObjectPostProcessor<Object> objectPostProcessor,
boolean allowConfigurersOfSameType) {
Assert.notNull(objectPostProcessor, "objectPostProcessor cannot be null");
this.objectPostProcessor = objectPostProcessor;
this.allowConfigurersOfSameType = allowConfigurersOfSameType;
}
/**
* Similar to {@link #build()} and {@link #getObject()} but checks the state to
* determine if {@link #build()} needs to be called first.
* @return the result of {@link #build()} or {@link #getObject()}. If an error occurs
* while building, returns null.
*/
public O getOrBuild() {
if (!isUnbuilt()) {
@@ -114,6 +135,24 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
}
}
/**
* Applies a {@link SecurityConfigurerAdapter} to this {@link SecurityBuilder} and
* invokes {@link SecurityConfigurerAdapter#setBuilder(SecurityBuilder)}.
* @param configurer
* @return the {@link SecurityConfigurerAdapter} for further customizations
* @throws Exception
* @deprecated For removal in 7.0. Use
* {@link #with(SecurityConfigurerAdapter, Customizer)} instead.
*/
@Deprecated(since = "6.2", forRemoval = true)
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurerAdapter<O, B>> C apply(C configurer) throws Exception {
configurer.addObjectPostProcessor(this.objectPostProcessor);
configurer.setBuilder((B) this);
add(configurer);
return configurer;
}
/**
* Applies a {@link SecurityConfigurer} to this {@link SecurityBuilder} overriding any
* {@link SecurityConfigurer} of the exact same class. Note that object hierarchies
@@ -122,33 +161,11 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
* @return the {@link SecurityConfigurerAdapter} for further customizations
* @throws Exception
*/
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) {
public <C extends SecurityConfigurer<O, B>> C apply(C configurer) throws Exception {
add(configurer);
return configurer;
}
/**
* Applies a {@link SecurityConfigurerAdapter} to this {@link SecurityBuilder} and
* invokes {@link SecurityConfigurerAdapter#setBuilder(SecurityBuilder)}.
*
* <p>
* A shortcut for applying a configurer as-is, or in other words: <code>
* .with(new MyConfigurer())
* </code>
*
* <p>
* Is identical to: <code>
* .with(new MyConfigurer(), Customizer.withDefaults())
* </code>
* @param configurer
* @return the {@link SecurityBuilder} for further customizations
* @throws Exception
* @since 7.0
*/
public <C extends SecurityConfigurerAdapter<O, B>> B with(C configurer) {
return with(configurer, Customizer.withDefaults());
}
/**
* Applies a {@link SecurityConfigurerAdapter} to this {@link SecurityBuilder} and
* invokes {@link SecurityConfigurerAdapter#setBuilder(SecurityBuilder)}.
@@ -158,7 +175,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
* @since 6.2
*/
@SuppressWarnings("unchecked")
public <C extends SecurityConfigurerAdapter<O, B>> B with(C configurer, Customizer<C> customizer) {
public <C extends SecurityConfigurerAdapter<O, B>> B with(C configurer, Customizer<C> customizer) throws Exception {
configurer.addObjectPostProcessor(this.objectPostProcessor);
configurer.setBuilder((B) this);
add(configurer);
@@ -326,7 +343,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
* </ul>
*/
@Override
protected final O doBuild() {
protected final O doBuild() throws Exception {
synchronized (this.configurers) {
this.buildState = BuildState.INITIALIZING;
beforeInit();
@@ -346,7 +363,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
* method. Subclasses may override this method to hook into the lifecycle without
* using a {@link SecurityConfigurer}.
*/
protected void beforeInit() {
protected void beforeInit() throws Exception {
}
/**
@@ -355,17 +372,17 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
* override this method to hook into the lifecycle without using a
* {@link SecurityConfigurer}.
*/
protected void beforeConfigure() {
protected void beforeConfigure() throws Exception {
}
/**
* Subclasses must implement this method to build the object that is being returned.
* @return the Object to be buit or null if the implementation allows it
*/
protected abstract O performBuild();
protected abstract O performBuild() throws Exception;
@SuppressWarnings("unchecked")
private void init() {
private void init() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.init((B) this);
@@ -380,7 +397,7 @@ public abstract class AbstractConfiguredSecurityBuilder<O, B extends SecurityBui
}
@SuppressWarnings("unchecked")
private void configure() {
private void configure() throws Exception {
Collection<SecurityConfigurer<O, B>> configurers = getConfigurers();
for (SecurityConfigurer<O, B> configurer : configurers) {
configurer.configure((B) this);

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