1
0
mirror of synced 2026-09-01 15:06:11 +00:00

Compare commits

..

125 Commits

Author SHA1 Message Date
Josh Cummings a825937b81 Release 7.1.1
Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:51 -06:00
Josh Cummings 74c91f8008 Update to Spring Data 2026.0.1
Closes gh-19490

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:51 -06:00
Josh Cummings b822214582 Update to Spring LDAP 4.1.1
Closes gh-19491

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:51 -06:00
Josh Cummings 39da00ff9c Update to Micrometer 1.17.1
Closes gh-19489

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:51 -06:00
Josh Cummings 4be80b754e Update to Reactor 2025.0.7
Closes gh-19488

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:51 -06:00
Josh Cummings 564a6775f6 Update to Spring Framework 7.0.9
Closes gh-19487

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:51 -06:00
Josh Cummings 293019846e Configure Build for Commercial Repositories
Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:51 -06:00
Josh Cummings 25f9a9199b Configure with Commercial Workflows
Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:51 -06:00
Josh Cummings bf8569f881 Remove OSS CI configuration 2026-08-13 16:33:51 -06:00
Josh Cummings dcee218cac Use jspecify Nullable in DPoPProofReplayValidator
org.springframework.lang.Nullable is banned by the
bannedNullabilityImports checkstyle rule on this line; the embargoed
commit that introduced this file predates that rule's adoption here.
Switch to org.jspecify.annotations.Nullable to match the rest of the
codebase (already used correctly elsewhere in this same file).

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:33 -06:00
Josh Cummings 952fc5a843 Align Null Behavior
Previously, ExternalInetAddressMatcher treated a null InetAddress
as external by negating InternalInetAddressMatcher's result.

This commit makes ExternalInetAddressMatcher return false for a
null address, so that neither the internal nor the external
matcher classifies an unknown address as a match.

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:33 -06:00
Josh Cummings 31611d0873 Deprecate AesBytesEncryptor
This commit separates AesBytesEncryptor into two separate
implememtations, allowing for a migration away from default
arrangements that used a null IV

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:33 -06:00
Josh Cummings a447020c92 Improve Equivalence Tests
This commit adds equals and hashCode implementations as well
as a readResolve implementation to ensure that deserialization
mechanisms can correctly assess the equality of a constnat
and a corresponding deserialized instance. For defense-in-depth
reasons, this commit also favors .equals over == for these
constants.

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:33 -06:00
Joe Grandja 81d17a55a7 Bind UnboundIdContainer to loopback address 2026-08-13 16:33:33 -06:00
Joe Grandja 1bbbed7a32 Apply html encoding in DefaultConsentPage 2026-08-13 16:33:33 -06:00
Joe Grandja b47b96e6b4 Provide ability to configure DPoP proof replay 2026-08-13 16:33:33 -06:00
Josh Cummings deddcce169 Use Constant-Time Comparison
This commit updates password encoders to use a
constant-time comparison method to defend against
timing attacks

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:33 -06:00
Josh Cummings f5bfdc4b79 Use Utf8#isEqual
Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:33 -06:00
Josh Cummings 09ae8bee20 Use Constant-Time Equals
This commit updates the filter to use constant-time
equals for sensitive material

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:32 -06:00
Josh Cummings e809909d34 Use Constant-Time Equals
This commit updates the filter to use constant-time
equals for sensitive material

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:32 -06:00
Josh Cummings 2f06cd7e4d Add Utf8#isEqual
This commit adds a constant-time equals method,
useful for comparing password hashes or other
sensitive material

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:32 -06:00
Josh Cummings bcbd2f7a73 Use MessageDigest#isEqual
This commit favors constant-time comparison
to mitigate timing attacks

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:32 -06:00
Josh Cummings 022bb82aab Add Tests for User and Device Code
Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:32 -06:00
Josh Cummings 9fdcc6104d Check isAnyLocalAddress
This commit updates InternalInetAddressMatcher to check
InetAddress#isAnyLocalAddress in order to catch
additional IP addresses

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-13 16:33:32 -06:00
Josh Cummings 7cecfbb4db Merge branch '7.0.x' into 7.1.x
Closes gh-19548
2026-08-13 15:58:42 -06:00
dae won 5b923c78ea Validate Parameter in setPostAuthenticationChecks
The null check in setPostAuthenticationChecks of
AbstractUserDetailsReactiveAuthenticationManager asserted the current
field value, which is initialized to a default and can never be null,
instead of the method parameter. As a result, null was silently
accepted and the next authenticate call failed with a raw
NullPointerException instead of failing fast with a clear message.

Closes gh-19276

Signed-off-by: dae won <eodnjs01477@gmail.com>
2026-08-13 15:58:01 -06:00
Josh Cummings bd4f9a1644 Merge branch '7.0.x' into 7.1.x
Closes gh-19545
2026-08-13 15:20:20 -06:00
Tran Ngoc Nhan 65a1099ccb Correct validation logic in CasAuthenticationToken
Closes gh-19368

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-08-13 15:19:26 -06:00
Josh Cummings ade851b2f1 Merge branch '7.0.x' into 7.1.x
Closes gh-19539
2026-08-13 11:36:49 -06:00
junhyeong9812 9fdd2dc675 Lowercase username in changePassword lookup
InMemoryUserDetailsManager keys its user map on the lower-cased
username everywhere except changePassword, which looked the current
user up with the raw name. A user whose username contains uppercase
letters could therefore not change its password. Lower-case the
lookup key to match the rest of the class.

Closes gh-19336

Signed-off-by: junhyeong9812 <pickjog@gmail.com>
2026-08-13 11:35:59 -06:00
dependabot[bot] 86fe79a848 Bump org.apache.httpcomponents.client5:httpclient5 from 5.6.3 to 5.6.4
Bumps [org.apache.httpcomponents.client5:httpclient5](https://github.com/apache/httpcomponents-client) from 5.6.3 to 5.6.4.
- [Changelog](https://github.com/apache/httpcomponents-client/blob/rel/v5.6.4/RELEASE_NOTES.txt)
- [Commits](https://github.com/apache/httpcomponents-client/compare/rel/v5.6.3...rel/v5.6.4)

---
updated-dependencies:
- dependency-name: org.apache.httpcomponents.client5:httpclient5
  dependency-version: 5.6.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-13 03:19:25 +00:00
Josh Cummings 852e81640b Merge branch '7.0.x' into 7.1.x 2026-08-11 19:40:43 -06:00
Josh Cummings 2aa3c1ab2e Remove NullAway
Issue gh-17816

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-11 19:40:18 -06:00
Kim Tae Eun 17c58ef088 Remove BeanResolver Null Checks
StandardEvaluationContext.setBeanResolver now accepts a nullable
BeanResolver, so the workarounds added for the original limitation
are no longer needed. This removes the explicit null guards (and
equivalent Optional.ofNullable idiom) as well as the
@SuppressWarnings("NullAway") annotations introduced to silence
the false positives.

In addition to the seven sites that explicitly referenced
spring-projects/spring-framework#35371, two sites in
spring-security-core followed the same workaround pattern without
the comment marker (AbstractSecurityExpressionHandler and
DefaultMethodSecurityExpressionHandler) and have also been
simplified.

Closes gh-17816

Signed-off-by: Kim Tae Eun <snowykte0426@naver.com>
2026-08-11 18:37:09 -07:00
Josh Cummings fd233120a8 Merge branch '7.0.x' into 7.1.x 2026-08-11 16:19:41 -06:00
Arz Meow 63306ad8ee Update One-Time Token Docs for Renamed APIs
Replace deprecated `UriComponentsBuilder` usage and renamed
`oneTimeTokenLogin` DSL method references in the docs with their
current equivalents.

Closes gh-18367

Signed-off-by: Arz Meow <arthur37231@gmail.com>
2026-08-11 14:47:25 -07:00
Tran Ngoc Nhan 24aec1d002 Fix broken Javadoc links
Closes gh-19194

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-08-11 14:31:49 -07:00
Tran Ngoc Nhan 863753580c Fix Broken DefaultLoginPageGeneratingFilter Javadoc Link
Use the `javadoc:` macro so the reference resolves correctly.

Signed-off-by: Tran Ngoc Nhan <ngocnhan.tran1996@gmail.com>
2026-08-11 14:31:43 -07:00
Tran Ngoc Nhan fa92ce3758 Remove Unnecessary AuthorizationDecision Cast
Closes gh-19282
2026-08-11 14:30:52 -07:00
Josh Cummings e7a4349774 Merge branch '7.0.x' into 7.1.x 2026-08-11 14:00:37 -06:00
dependabot[bot] 22f4a5d4da Bump com.nimbusds:oauth2-oidc-sdk from 11.38.1 to 11.38.2
Bumps [com.nimbusds:oauth2-oidc-sdk](https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions) from 11.38.1 to 11.38.2 and com.nimbusds:nimbus-jose-jwt from 10.9 to 10.9.1, which oauth2-oidc-sdk 11.38.2 requires.
- [Changelog](https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions/src/master/CHANGELOG.txt)
- [Commits](https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions/branches/compare/11.38.2..11.38.1)

---
updated-dependencies:
- dependency-name: com.nimbusds:oauth2-oidc-sdk
  dependency-version: 11.38.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: com.nimbusds:nimbus-jose-jwt
  dependency-version: 10.9.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-11 19:33:41 +00:00
dependabot[bot] 561f1bba0f Bump com.unboundid:unboundid-ldapsdk from 7.0.4 to 7.0.5
Bumps [com.unboundid:unboundid-ldapsdk](https://github.com/pingidentity/ldapsdk) from 7.0.4 to 7.0.5.
- [Release notes](https://github.com/pingidentity/ldapsdk/releases)
- [Changelog](https://github.com/pingidentity/ldapsdk/blob/master/docs/release-notes.html)
- [Commits](https://github.com/pingidentity/ldapsdk/compare/7.0.4...7.0.5)

---
updated-dependencies:
- dependency-name: com.unboundid:unboundid-ldapsdk
  dependency-version: 7.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-11 12:00:52 -07:00
dependabot[bot] 268fce2616 Bump antora from 3.2.0-alpha.12 to 3.2.0-rc.2 in /docs
---
updated-dependencies:
- dependency-name: antora
  dependency-version: 3.2.0-rc.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-11 10:27:36 -07:00
dependabot[bot] c4c547b73b Bump org.apache.kerby:kerb-simplekdc from 2.1.1 to 2.1.2
Bumps org.apache.kerby:kerb-simplekdc from 2.1.1 to 2.1.2.

---
updated-dependencies:
- dependency-name: org.apache.kerby:kerb-simplekdc
  dependency-version: 2.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-11 10:27:26 -07:00
dependabot[bot] d487048224 Bump ch.qos.logback:logback-classic from 1.5.34 to 1.5.38
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.34 to 1.5.38.
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.34...v_1.5.38)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-11 10:27:10 -07:00
dependabot[bot] 1d15210ae2 Bump actions/checkout from 6.0.3 to 7.0.1
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.3 to 7.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/df4cb1c069e1874edd31b4311f1884172cec0e10...3d3c42e5aac5ba805825da76410c181273ba90b1)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-11 10:26:57 -07:00
dependabot[bot] 5f377e5ec2 Bump @springio/antora-extensions from 1.14.12 to 1.14.13 in /docs
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.14.12 to 1.14.13.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.14.12...v1.14.13)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-11 10:26:13 -07:00
dependabot[bot] e107329766 Bump actions/setup-java from 5.2.0 to 5.7.0
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.2.0 to 5.7.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/be666c2fcd27ec809703dec50e508c2fdc7f6654...b6effb05e454b25005698d916606bdc6ffcbf961)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-11 10:25:54 -07:00
Josh Cummings c96eca3ef4 Merge branch '7.0.x' into 7.1.x 2026-08-11 11:09:21 -06:00
Josh Cummings 905b4786e8 Remove Milestone Date Validation
Closes gh-19526

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-11 11:08:05 -06:00
dependabot[bot] 08d3108521 Bump org.junit:junit-bom from 6.1.2 to 6.1.3
Bumps [org.junit:junit-bom](https://github.com/junit-team/junit-framework) from 6.1.2 to 6.1.3.
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](https://github.com/junit-team/junit-framework/compare/r6.1.2...r6.1.3)

---
updated-dependencies:
- dependency-name: org.junit:junit-bom
  dependency-version: 6.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-11 16:40:18 +00:00
Josh Cummings e3139ff669 Merge branch '7.0.x'
# Conflicts:
#	.github/workflows/continuous-integration-workflow.yml
#	.github/workflows/finalize-release.yml
#	.github/workflows/release-scheduler.yml
2026-08-10 15:53:04 -06:00
Josh Cummings 36e7420bd0 Add Release Train Integration
This commit adds the build-level configuration required for
release train participation.

See gh-19518
2026-08-10 15:48:02 -06:00
Josh Cummings f03e612a14 Remove Release Workflows
This commit removes workflows for scheduling and
releasing releases in favor of release train support

Closes gh-19518
2026-08-10 15:46:10 -06:00
dependabot[bot] ebd3f2364c Bump gradle-wrapper from 9.6.1 to 9.7.0
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.6.1 to 9.7.0.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.6.1...v9.7.0)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-10 03:19:32 +00:00
dependabot[bot] 7fd2a726d6 Bump js-yaml from 4.2.0 to 4.3.1 in /javascript
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.2.0 to 4.3.1.
- [Changelog](https://github.com/nodeca/js-yaml/blob/4.3.1/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.2.0...4.3.1)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.3.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-07 01:00:53 +00:00
dependabot[bot] dfacecdb88 Bump brace-expansion from 1.1.11 to 1.1.18 in /javascript
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.11 to 1.1.18.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/1.1.11...v1.1.18)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 1.1.18
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-07 00:58:27 +00:00
dependabot[bot] 92b8883fc2 Bump actions/setup-java from 5.6.0 to 5.7.0
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.6.0 to 5.7.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/03ad4de0992f5dab5e18fcb136590ce7c4a0ac95...b6effb05e454b25005698d916606bdc6ffcbf961)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-07 00:46:42 +00:00
dependabot[bot] 43731e0523 Bump @springio/antora-extensions from 1.14.12 to 1.14.13 in /docs
Bumps [@springio/antora-extensions](https://github.com/spring-io/antora-extensions) from 1.14.12 to 1.14.13.
- [Changelog](https://github.com/spring-io/antora-extensions/blob/main/CHANGELOG.adoc)
- [Commits](https://github.com/spring-io/antora-extensions/compare/v1.14.12...v1.14.13)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-07 00:46:03 +00:00
dependabot[bot] f686a9e313 Bump com.webauthn4j:webauthn4j-core
Bumps [com.webauthn4j:webauthn4j-core](https://github.com/webauthn4j/webauthn4j) from 0.31.8.RELEASE to 0.31.9.RELEASE.
- [Release notes](https://github.com/webauthn4j/webauthn4j/releases)
- [Commits](https://github.com/webauthn4j/webauthn4j/compare/0.31.8.RELEASE...0.31.9.RELEASE)

---
updated-dependencies:
- dependency-name: com.webauthn4j:webauthn4j-core
  dependency-version: 0.31.9.RELEASE
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-05 03:17:09 +00:00
Josh Cummings 08d062bdc0 Merge branch '7.0.x'
# Conflicts:
#	docs/modules/ROOT/pages/features/exploits/http.adoc
2026-08-03 17:53:54 -06:00
Sumit Kumar Das 2c4db99229 Differentiate Forwarded and X-Forwarded headers in proxy docs
The proxy server section pointed at RFC 7239 and then told the reader to
configure the application server for the X-Forwarded headers, conflating
the standard Forwarded header with the non-standard X-Forwarded-* set.

Describe the two kinds of headers separately, note that most proxies send
X-Forwarded-* while Spring Framework and servers such as Reactor Netty and
Jetty understand both, and state that the edge proxy has to drop or
overwrite untrusted values for both kinds rather than only one.

Closes gh-19461

Signed-off-by: Sumit Kumar Das <skdas5405@gmail.com>
2026-08-03 17:52:32 -06:00
Josh Cummings c84875f923 Merge branch 'main' of github.com:spring-projects/spring-security 2026-08-03 16:10:02 -06:00
Josh Cummings 8d452e2c32 Merge branch '7.0.x' 2026-08-03 16:07:26 -06:00
Josh Cummings 0f6f453ea0 Increase Default NimbusJwtDecoder Timeouts to 30 Seconds
NimbusJwtDecoder's default RestOperations now respects the JDK's
sun.net.client.defaultConnectTimeout/defaultReadTimeout system properties,
falling back to 30 seconds instead of the previous 500 milliseconds,
matching JwtDecoderProviderConfigurationUtils's existing behavior.

Also documents this default and the RestOperations override in the
reference guide and migration guide (the reference guide's existing
"Configuring Timeouts" section already claimed 30 seconds -- it's been
inaccurate since the 500ms default shipped and is now correct again), and
documents providing a custom JwtDecoderFactory<ClientRegistration> for
OAuth2 Login's ID Token decoding.

Issue gh-19474

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-08-03 15:51:41 -06:00
dependabot[bot] 7ec316a030 Bump org.apache.httpcomponents.client5:httpclient5 from 5.6.2 to 5.6.3
Bumps [org.apache.httpcomponents.client5:httpclient5](https://github.com/apache/httpcomponents-client) from 5.6.2 to 5.6.3.
- [Changelog](https://github.com/apache/httpcomponents-client/blob/rel/v5.6.3/RELEASE_NOTES.txt)
- [Commits](https://github.com/apache/httpcomponents-client/compare/rel/v5.6.2...rel/v5.6.3)

---
updated-dependencies:
- dependency-name: org.apache.httpcomponents.client5:httpclient5
  dependency-version: 5.6.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-03 03:16:40 +00:00
Josh Cummings 61fae604d2 Merge branch '7.0.x' 2026-07-31 16:03:03 -06:00
Josh Cummings 1eef373ca0 Improve Error Message for Invalid JSR-250 Usage
This commit adds an IllegalStateException guard when spring-security-access
is missing and the application is using @EnableGlobalMethodSecurity with
jsr250Enabled.

Issue gh-19441

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-07-31 16:01:21 -06:00
jyx-07 7a03cd5a55 Fail fast when spring-security-access is missing
The "Move Core Access API" refactoring (gh-17847) relocated
MethodSecurityMetadataSourceAdvisor and MethodSecurityInterceptor
from spring-security-core, a mandatory dependency of
spring-security-config, into the new spring-security-access module,
which spring-security-config only depends on optionally.

GlobalMethodSecuritySelector (backing the deprecated
@EnableGlobalMethodSecurity) and ReactiveMethodSecuritySelector
(backing @EnableReactiveMethodSecurity(useAuthorizationManager =
false)) still unconditionally import configuration that constructs
those classes: MethodSecurityMetadataSourceAdvisorRegistrar in proxy
mode, GlobalMethodSecurityConfiguration in both proxy and aspectj
mode, and ReactiveMethodSecurityConfiguration for the legacy reactive
path. Applications that use any of these deprecated configuration
paths without explicitly adding spring-security-access now fail at
startup with a confusing NoClassDefFoundError deep inside Spring's
configuration-processing machinery, instead of an actionable message.

@EnableMethodSecurity and @EnableReactiveMethodSecurity's default
(AuthorizationManager-based) mode, the non-deprecated replacements,
never reference these classes and are unaffected either way.

Add a ClassUtils.isPresent check to both selectors so that, whenever
a legacy configuration path that needs it is chosen (proxy mode,
aspectj mode, or the legacy reactive interceptor), a missing
spring-security-access dependency now fails fast with a clear
IllegalStateException that names the missing dependency and points
to the supported alternative, rather than a NoClassDefFoundError.

This preserves gh-17847's footprint-reduction intent: the check only
runs for the deprecated legacy annotations, so the majority of
applications using @EnableMethodSecurity see no change in behavior or
dependencies. @EnableGlobalMethodSecurity remains deprecated; this
change adds no new investment in it beyond giving existing users of
it a clear diagnostic instead of a confusing crash.

Closes gh-19441

Signed-off-by: jyx-07 <s25069@gsm.hs.kr>
2026-07-31 16:01:21 -06:00
Josh Cummings bb59d5c0dd Merge branch '7.0.x'
# Conflicts:
#	oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2PushedAuthorizationRequestUri.java
2026-07-31 13:22:17 -06:00
Andrey Litvitski e4fafce066 Fix OAuth2PushedAuthorizationRequestUri parsing
OAuth2PushedAuthorizationRequestUri uses Base64URL encoding for the state, which can produce a value containing ___, the same sequence used as the delimiter. As a result, indexOf may locate a delimiter within the state instead of the delimiter preceding the expiration timestamp.

Use lastIndexOf to reliably locate the delimiter before the timestamp without changing the existing request URI format.

Closes gh-19444

Signed-off-by: Andrey Litvitski <andrey1010102008@gmail.com>
2026-07-31 13:19:21 -06:00
Josh Cummings 2104c49597 Add tests for OAuth2PushedAuthorizationRequestUri
Adds baseline test coverage for OAuth2PushedAuthorizationRequestUri,
which previously had none: a round-trip via create()/parse() and an
explicit parse() case on a hand-constructed request URI.

See gh-19444.

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-07-31 13:19:21 -06:00
Josh Cummings 5bee669ae3 Merge branch '7.0.x' 2026-07-31 12:31:45 -06:00
Josh Cummings c5b1f78118 Support Testing Against Snapshot Spring LDAP Versions
Adds an opt-in override to spring-security-dependencies so it can be
built against a specific Spring LDAP version instead of whatever's
declared in the version catalog, matching the existing pattern for
Spring Framework/Reactor/Spring Data/Micrometer. Passing
-PisOverrideVersionCatalog=true -PspringLdapVersion=X takes precedence
over the catalog. Default behavior (no properties passed) is unchanged.

Closes gh-19481

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-07-31 12:31:17 -06:00
dependabot[bot] f94cef17b8 Bump ch.qos.logback:logback-classic from 1.6.0 to 1.6.1
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.6.0 to 1.6.1.
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.6.0...v_1.6.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-29 03:16:09 +00:00
dependabot[bot] 25f2e1c2a3 Bump ch.qos.logback:logback-classic from 1.5.38 to 1.6.0
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.38 to 1.6.0.
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.38...v_1.6.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-24 03:13:25 +00:00
dependabot[bot] 67a3de25eb Bump actions/checkout from 7.0.0 to 7.0.1
Bumps [actions/checkout](https://github.com/actions/checkout) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0...3d3c42e5aac5ba805825da76410c181273ba90b1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-24 00:44:38 +00:00
dependabot[bot] 5bd6b4a570 Bump actions/setup-java from 5.5.0 to 5.6.0
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.5.0 to 5.6.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/0f481fcb613427c0f801b606911222b5b6f3083a...03ad4de0992f5dab5e18fcb136590ce7c4a0ac95)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-17 00:45:28 +00:00
dependabot[bot] 08dcb1c0e4 Bump org-jetbrains-kotlin from 2.4.0 to 2.4.10
Bumps `org-jetbrains-kotlin` from 2.4.0 to 2.4.10.

Updates `org.jetbrains.kotlin:kotlin-bom` from 2.4.0 to 2.4.10
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v2.4.0...v2.4.10)

Updates `org.jetbrains.kotlin:kotlin-gradle-plugin` from 2.4.0 to 2.4.10
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v2.4.0...v2.4.10)

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin:kotlin-bom
  dependency-version: 2.4.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin:kotlin-gradle-plugin
  dependency-version: 2.4.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-15 03:20:37 +00:00
dependabot[bot] e1ad5d2392 Bump org.hibernate.orm:hibernate-core from 7.4.4.Final to 7.4.5.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 7.4.4.Final to 7.4.5.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.4.5/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/7.4.4...7.4.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-14 03:16:11 +00:00
dependabot[bot] 7654aa2f6f Bump org.junit:junit-bom from 6.1.1 to 6.1.2
Bumps [org.junit:junit-bom](https://github.com/junit-team/junit-framework) from 6.1.1 to 6.1.2.
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](https://github.com/junit-team/junit-framework/compare/r6.1.1...r6.1.2)

---
updated-dependencies:
- dependency-name: org.junit:junit-bom
  dependency-version: 6.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-14 03:15:24 +00:00
dependabot[bot] a29d4ad5a4 Bump com.nimbusds:oauth2-oidc-sdk from 11.37.2 to 11.38.1
Bumps [com.nimbusds:oauth2-oidc-sdk](https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions) from 11.37.2 to 11.38.1.
- [Changelog](https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions/src/master/CHANGELOG.txt)
- [Commits](https://bitbucket.org/connect2id/oauth-2.0-sdk-with-openid-connect-extensions/branches/compare/11.38.1..11.37.2)

---
updated-dependencies:
- dependency-name: com.nimbusds:oauth2-oidc-sdk
  dependency-version: 11.38.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-13 03:17:43 +00:00
dependabot[bot] ab33b40f19 Bump org-bouncycastle from 1.84 to 1.85
Bumps `org-bouncycastle` from 1.84 to 1.85.

Updates `org.bouncycastle:bcpkix-jdk18on` from 1.84 to 1.85
- [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html)
- [Commits](https://github.com/bcgit/bc-java/commits)

Updates `org.bouncycastle:bcprov-jdk18on` from 1.84 to 1.85
- [Changelog](https://github.com/bcgit/bc-java/blob/main/docs/releasenotes.html)
- [Commits](https://github.com/bcgit/bc-java/commits)

---
updated-dependencies:
- dependency-name: org.bouncycastle:bcpkix-jdk18on
  dependency-version: '1.85'
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.bouncycastle:bcprov-jdk18on
  dependency-version: '1.85'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-13 03:16:56 +00:00
dependabot[bot] d3d6497798 Bump tools.jackson:jackson-bom from 3.2.0 to 3.2.1
Bumps [tools.jackson:jackson-bom](https://github.com/FasterXML/jackson-bom) from 3.2.0 to 3.2.1.
- [Commits](https://github.com/FasterXML/jackson-bom/compare/jackson-bom-3.2.0...jackson-bom-3.2.1)

---
updated-dependencies:
- dependency-name: tools.jackson:jackson-bom
  dependency-version: 3.2.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-13 03:14:19 +00:00
dependabot[bot] 6680d42d34 Bump ch.qos.logback:logback-classic from 1.5.37 to 1.5.38
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.37 to 1.5.38.
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.37...v_1.5.38)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-10 03:17:42 +00:00
dependabot[bot] c2efa64c4d Bump actions/setup-java from 5.4.0 to 5.5.0
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.4.0 to 5.5.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/1bcf9fb12cf4aa7d266a90ae39939e61372fe520...0f481fcb613427c0f801b606911222b5b6f3083a)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-10 00:57:47 +00:00
Josh Cummings 0a75d5b785 Merge branch '7.0.x' 2026-07-09 11:02:02 -06:00
Josh Cummings d2366650ec Polish Serialization Test
This commit makes the version calculation work for
intermediate version numbers like 7.0.5.1
2026-07-09 11:01:06 -06:00
Josh Cummings 39790c0a7e Merge branch '7.0.x' 2026-07-09 08:44:09 -06:00
Josh Cummings 5338b7a05b Move Wildcards to Minor Versions
This commit targets the major.minor version pair that
matches Spring Security 7.0.x

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-07-09 08:43:57 -06:00
Josh Cummings 71f81dede2 Merge branch '7.0.x' 2026-07-09 08:32:45 -06:00
Josh Cummings bd86d0b233 Add Micrometer to Snapshot Test
Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-07-09 08:32:11 -06:00
Josh Cummings f8442794a8 Check Only JDK 17
This commit removes the extra build for
JDK 21 to save time on builds.

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-07-09 08:32:11 -06:00
Josh Cummings b62affe126 Correct Copyright Headers
This commit updates all headers to have
the same date range.

Signed-off-by: Josh Cummings <3627351+jzheaux@users.noreply.github.com>
2026-07-08 17:11:04 -06:00
dependabot[bot] 60e7733d8a Bump com.fasterxml.jackson:jackson-bom from 2.22.0 to 2.22.1
Bumps [com.fasterxml.jackson:jackson-bom](https://github.com/FasterXML/jackson-bom) from 2.22.0 to 2.22.1.
- [Commits](https://github.com/FasterXML/jackson-bom/compare/jackson-bom-2.22.0...jackson-bom-2.22.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-08 03:15:25 +00:00
dependabot[bot] 2bac0be1d6 Bump org.hibernate.orm:hibernate-core from 7.4.3.Final to 7.4.4.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 7.4.3.Final to 7.4.4.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.4.4/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/7.4.3...7.4.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-06 03:20:54 +00:00
dependabot[bot] bb51ad6096 Bump com.webauthn4j:webauthn4j-core
Bumps [com.webauthn4j:webauthn4j-core](https://github.com/webauthn4j/webauthn4j) from 0.31.7.RELEASE to 0.31.8.RELEASE.
- [Release notes](https://github.com/webauthn4j/webauthn4j/releases)
- [Commits](https://github.com/webauthn4j/webauthn4j/compare/0.31.7.RELEASE...0.31.8.RELEASE)

---
updated-dependencies:
- dependency-name: com.webauthn4j:webauthn4j-core
  dependency-version: 0.31.8.RELEASE
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-02 03:12:59 +00:00
dependabot[bot] 9161eced5b Bump org.hibernate.orm:hibernate-core from 7.4.2.Final to 7.4.3.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 7.4.2.Final to 7.4.3.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.4.3/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/7.4.2...7.4.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-30 03:16:50 +00:00
dependabot[bot] 769fd66ed3 Bump org.apache.httpcomponents.client5:httpclient5 from 5.6.1 to 5.6.2
Bumps [org.apache.httpcomponents.client5:httpclient5](https://github.com/apache/httpcomponents-client) from 5.6.1 to 5.6.2.
- [Changelog](https://github.com/apache/httpcomponents-client/blob/rel/v5.6.2/RELEASE_NOTES.txt)
- [Commits](https://github.com/apache/httpcomponents-client/compare/rel/v5.6.1...rel/v5.6.2)

---
updated-dependencies:
- dependency-name: org.apache.httpcomponents.client5:httpclient5
  dependency-version: 5.6.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-30 03:16:17 +00:00
dependabot[bot] 49ecfa71dd Bump gradle-wrapper from 9.6.0 to 9.6.1
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.6.0 to 9.6.1.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.6.0...v9.6.1)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-29 03:18:05 +00:00
dependabot[bot] c6d41fd1a5 Bump org.junit:junit-bom from 6.1.0 to 6.1.1
Bumps [org.junit:junit-bom](https://github.com/junit-team/junit-framework) from 6.1.0 to 6.1.1.
- [Release notes](https://github.com/junit-team/junit-framework/releases)
- [Commits](https://github.com/junit-team/junit-framework/compare/r6.1.0...r6.1.1)

---
updated-dependencies:
- dependency-name: org.junit:junit-bom
  dependency-version: 6.1.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-29 03:16:18 +00:00
dependabot[bot] 316cf90ec2 Bump ch.qos.logback:logback-classic from 1.5.36 to 1.5.37
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.36 to 1.5.37.
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.36...v_1.5.37)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-29 03:15:30 +00:00
dependabot[bot] f5fbc2b46b Bump ch.qos.logback:logback-classic from 1.5.35 to 1.5.36
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.35 to 1.5.36.
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.35...v_1.5.36)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-26 03:16:22 +00:00
dependabot[bot] 49d2ebf3cf Bump org.apache.kerby:kerb-simplekdc from 2.1.1 to 2.1.2
Bumps org.apache.kerby:kerb-simplekdc from 2.1.1 to 2.1.2.

---
updated-dependencies:
- dependency-name: org.apache.kerby:kerb-simplekdc
  dependency-version: 2.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-26 03:14:57 +00:00
dependabot[bot] fc2c927a72 Bump antora from 3.2.0-alpha.12 to 3.2.0-rc.2 in /docs
---
updated-dependencies:
- dependency-name: antora
  dependency-version: 3.2.0-rc.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-26 01:04:48 +00:00
dependabot[bot] 40de3ed6bb Bump actions/setup-java from 5.3.0 to 5.4.0
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.3.0 to 5.4.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/ad2b38190b15e4d6bdf0c97fb4fca8412226d287...1bcf9fb12cf4aa7d266a90ae39939e61372fe520)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-26 01:02:28 +00:00
dependabot[bot] 85e8221ba0 Bump io.spring.nullability:io.spring.nullability.gradle.plugin
Bumps [io.spring.nullability:io.spring.nullability.gradle.plugin](https://github.com/spring-gradle-plugins/nullability-plugin) from 0.0.13 to 0.0.14.
- [Release notes](https://github.com/spring-gradle-plugins/nullability-plugin/releases)
- [Commits](https://github.com/spring-gradle-plugins/nullability-plugin/compare/v0.0.13...v0.0.14)

---
updated-dependencies:
- dependency-name: io.spring.nullability:io.spring.nullability.gradle.plugin
  dependency-version: 0.0.14
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-25 03:16:31 +00:00
dependabot[bot] 7f753a2433 Bump ch.qos.logback:logback-classic from 1.5.34 to 1.5.35
Bumps [ch.qos.logback:logback-classic](https://github.com/qos-ch/logback) from 1.5.34 to 1.5.35.
- [Release notes](https://github.com/qos-ch/logback/releases)
- [Commits](https://github.com/qos-ch/logback/compare/v_1.5.34...v_1.5.35)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-24 03:14:02 +00:00
dependabot[bot] ed7ae7969e Bump org.hibernate.orm:hibernate-core from 7.4.1.Final to 7.4.2.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 7.4.1.Final to 7.4.2.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.4.2/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/7.4.1...7.4.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-22 03:16:09 +00:00
dependabot[bot] 4ddef4ed83 Bump gradle-wrapper from 9.5.1 to 9.6.0
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.5.1 to 9.6.0.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.5.1...v9.6.0)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-19 03:20:36 +00:00
dependabot[bot] abb8aa54d8 Bump org-opensaml5 from 5.2.2 to 5.2.3
Bumps `org-opensaml5` from 5.2.2 to 5.2.3.

Updates `org.opensaml:opensaml-saml-api` from 5.2.2 to 5.2.3

Updates `org.opensaml:opensaml-saml-impl` from 5.2.2 to 5.2.3

---
updated-dependencies:
- dependency-name: org.opensaml:opensaml-saml-api
  dependency-version: 5.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.opensaml:opensaml-saml-impl
  dependency-version: 5.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-19 03:17:49 +00:00
dependabot[bot] deb298ee81 Bump actions/checkout from 6.0.3 to 7.0.0
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.3 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/df4cb1c069e1874edd31b4311f1884172cec0e10...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-19 01:03:52 +00:00
dependabot[bot] 3565d9bd9d Bump js-yaml from 4.1.1 to 4.2.0 in /javascript
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.1 to 4.2.0.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/commits)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.2.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-19 01:02:56 +00:00
dependabot[bot] 827a689ef8 Bump actions/setup-java from 5.2.0 to 5.3.0
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.2.0 to 5.3.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/be666c2fcd27ec809703dec50e508c2fdc7f6654...ad2b38190b15e4d6bdf0c97fb4fca8412226d287)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-19 00:46:33 +00:00
dependabot[bot] 43f958e058 Bump esbuild from 0.25.0 to 0.28.1 in /javascript
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.25.0 to 0.28.1.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2025.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.0...v0.28.1)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.28.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-14 01:16:55 +00:00
dependabot[bot] 5594fc018f Bump com.unboundid:unboundid-ldapsdk from 7.0.4 to 7.0.5
Bumps [com.unboundid:unboundid-ldapsdk](https://github.com/pingidentity/ldapsdk) from 7.0.4 to 7.0.5.
- [Release notes](https://github.com/pingidentity/ldapsdk/releases)
- [Changelog](https://github.com/pingidentity/ldapsdk/blob/master/docs/release-notes.html)
- [Commits](https://github.com/pingidentity/ldapsdk/commits)

---
updated-dependencies:
- dependency-name: com.unboundid:unboundid-ldapsdk
  dependency-version: 7.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-12 03:17:11 +00:00
dependabot[bot] bf06d19225 Bump com.webauthn4j:webauthn4j-core
Bumps [com.webauthn4j:webauthn4j-core](https://github.com/webauthn4j/webauthn4j) from 0.31.6.RELEASE to 0.31.7.RELEASE.
- [Release notes](https://github.com/webauthn4j/webauthn4j/releases)
- [Commits](https://github.com/webauthn4j/webauthn4j/compare/0.31.6.RELEASE...0.31.7.RELEASE)

---
updated-dependencies:
- dependency-name: com.webauthn4j:webauthn4j-core
  dependency-version: 0.31.7.RELEASE
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-10 03:17:13 +00:00
dependabot[bot] d511a4badb Bump org.hibernate.orm:hibernate-core from 7.4.0.Final to 7.4.1.Final
Bumps [org.hibernate.orm:hibernate-core](https://github.com/hibernate/hibernate-orm) from 7.4.0.Final to 7.4.1.Final.
- [Release notes](https://github.com/hibernate/hibernate-orm/releases)
- [Changelog](https://github.com/hibernate/hibernate-orm/blob/7.4.1/changelog.txt)
- [Commits](https://github.com/hibernate/hibernate-orm/compare/7.4.0...7.4.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-10 03:15:52 +00:00
Joe Grandja 038a6601d8 Merge branch '7.0.x' 2026-06-09 14:30:59 -04:00
Joe Grandja 57c2af4900 Merge branch '6.5.x' into 7.0.x 2026-06-09 14:30:37 -04:00
github-actions[bot] 4690f1bb8a Next development version 2026-06-09 16:39:34 +00:00
github-actions[bot] 6da4ea0d0d Next development version 2026-06-09 16:06:39 +00:00
github-actions[bot] 8f69721f24 Next development version 2026-06-09 16:02:59 +00:00
github-actions[bot] acd131c3d1 Release 7.0.6 2026-06-09 15:31:10 +00:00
github-actions[bot] 73b077790f Release 6.5.11 2026-06-09 15:25:02 +00:00
137 changed files with 3410 additions and 1084 deletions
@@ -0,0 +1,7 @@
name: Build Release
runs:
using: composite
steps:
- name: Build Release
shell: bash
run: ./gradlew -PdeploymentRepository=$(pwd)/deployment-repository publishAllPublicationsToDeploymentRepository
@@ -0,0 +1,7 @@
name: Test Release
runs:
using: composite
steps:
- name: Test Release
shell: bash
run: ./gradlew build
+17
View File
@@ -0,0 +1,17 @@
workflow:
generator:
project:
java:
versions:
primary: 25
workflows:
release-train:
build:
env:
COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME
COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD
test:
env:
COMMERCIAL_REPO_USERNAME: secrets.COMMERCIAL_ARTIFACTORY_USERNAME
COMMERCIAL_REPO_PASSWORD: secrets.COMMERCIAL_ARTIFACTORY_PASSWORD
@@ -1,17 +0,0 @@
name: Merge Dependabot PR
on:
pull_request:
branches:
- main
- '*.x'
- 'docs-build'
run-name: Merge Dependabot PR ${{ github.ref_name }}
jobs:
merge-dependabot-pr:
permissions: write-all
uses: spring-io/spring-github-workflows/.github/workflows/spring-merge-dependabot-pr.yml@0d3f15bb384839966a1ff5c4383731a2b747f24b # v7
with:
mergeArguments: --auto --rebase
-36
View File
@@ -1,36 +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@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
strategy:
matrix:
include:
- java-version: 25
toolchain: 25
with:
java-version: ${{ matrix.java-version }}
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot,https://oss.sonatype.org/content/repositories/snapshots -PisOverrideVersionCatalog -PtestToolchain=${{ matrix.toolchain }} -PspringFrameworkVersion=7.0.+ -PreactorVersion=2025.+ -PspringDataVersion=2025.+ -PmicrometerVersion=1.+ --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@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
@@ -1,23 +0,0 @@
name: Clean build artifacts
on:
schedule:
- cron: '0 10 * * *' # Once per day at 10am UTC
permissions:
contents: read
jobs:
main:
runs-on: ubuntu-latest
if: ${{ github.repository == 'spring-projects/spring-security' }}
permissions:
contents: none
steps:
- name: Delete artifacts in cron job
env:
GH_ACTIONS_REPO_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
run: |
echo "Running clean build artifacts logic"
output=$(curl -X GET -H "Authorization: token $GH_ACTIONS_REPO_TOKEN" https://api.github.com/repos/spring-projects/spring-security/actions/artifacts | grep '"id"' | cut -d : -f2 | sed 's/,*$//g')
echo Output is $output
for id in $output; do curl -X DELETE -H "Authorization: token $GH_ACTIONS_REPO_TOKEN" https://api.github.com/repos/spring-projects/spring-security/actions/artifacts/$id; done;
-17
View File
@@ -1,17 +0,0 @@
name: "CodeQL Advanced"
on:
push:
pull_request:
workflow_dispatch:
schedule:
# https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule
- cron: '0 5 * * *'
permissions: read-all
jobs:
codeql-analysis-call:
permissions:
actions: read
contents: read
security-events: write
uses: spring-io/github-actions/.github/workflows/codeql-analysis.yml@e415dadd0910c901e7a7fabd67bbb355b2324500 # 1
@@ -8,58 +8,42 @@ on:
- 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:
build:
name: Build
uses: spring-io/spring-security-release-tools/.github/workflows/build.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
jdk: [ 25 ]
uses: spring-projects/spring-security-commercial/.github/workflows/build.yml@workflows/v1
with:
runs-on: ${{ matrix.os }}
java-version: ${{ matrix.jdk }}
distribution: temurin
java-version: '25'
secrets: inherit
test:
name: Test Against Snapshots
uses: spring-projects/spring-security-commercial/.github/workflows/test.yml@workflows/v1
with:
java-version: '25'
test-args: --refresh-dependencies -PforceMavenRepositories=snapshot -PisOverrideVersionCatalog -PtestToolchain=25 -PspringFrameworkVersion=7.0.+ -PreactorVersion=2025.0.+ -PspringDataVersion=2026.0.+ -PmicrometerVersion=1.17.+ --stacktrace
secrets: inherit
compute-version:
name: Compute Version
runs-on: ubuntu-latest
outputs:
snapshot: ${{ steps.project-version.outputs.snapshot }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # 7.0.0
- id: project-version
name: Extract Project Version
uses: spring-io/spring-release-actions/compute-version@a1f321783a0769dd2aea4fad6c2ae2f95a52b885 # 0.0.5
deploy-artifacts:
name: Deploy Artifacts
needs: [ build ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-artifacts.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
with:
should-deploy-artifacts: ${{ needs.build.outputs.should-deploy-artifacts }}
default-publish-milestones-central: true
java-version: 25
secrets: inherit
deploy-schema:
name: Deploy Schema
needs: [ build ]
uses: spring-io/spring-security-release-tools/.github/workflows/deploy-schema.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
with:
should-deploy-schema: ${{ needs.build.outputs.should-deploy-artifacts }}
java-version: 25
secrets: inherit
perform-release:
name: Perform Release
needs: [ deploy-artifacts, deploy-schema ]
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
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
release-repo-url: https://repo1.maven.org/maven2
artifact-path: org/springframework/security/spring-security-core
slack-announcing-id: spring-security-announcing
java-version: 25
needs: [ build, test, compute-version ]
if: needs.compute-version.outputs.snapshot == 'true'
uses: spring-projects/spring-security-commercial/.github/workflows/deploy-artifacts.yml@workflows/v1
secrets: inherit
send-notification:
name: Send Notification
needs: [ perform-release ]
needs: [ deploy-artifacts ]
if: ${{ !success() }}
runs-on: ubuntu-latest
steps:
-76
View File
@@ -1,76 +0,0 @@
name: Defer Issues
on:
workflow_dispatch:
permissions:
contents: read
jobs:
defer-issues:
name: Defer Issues
runs-on: ubuntu-latest
if: github.repository_owner == 'spring-projects'
permissions:
issues: write
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Compute Version
id: compute-version
uses: spring-io/spring-release-actions/compute-version@a1f321783a0769dd2aea4fad6c2ae2f95a52b885 # 0.0.5
- name: Get Today's Release Version
id: todays-release
uses: spring-io/spring-release-actions/get-todays-release-version@a1f321783a0769dd2aea4fad6c2ae2f95a52b885 # 0.0.5
with:
snapshot-version: ${{ steps.compute-version.outputs.version }}
milestone-repository: ${{ github.repository }}
milestone-token: ${{ secrets.GITHUB_TOKEN }}
- name: Compute Next Version
id: next-version
uses: spring-io/spring-release-actions/compute-next-version@a1f321783a0769dd2aea4fad6c2ae2f95a52b885 # 0.0.5
with:
version: ${{ steps.todays-release.outputs.release-version }}
- name: Schedule Next Milestone
uses: spring-io/spring-release-actions/schedule-milestone@a1f321783a0769dd2aea4fad6c2ae2f95a52b885 # 0.0.5
with:
version: ${{ steps.next-version.outputs.version }}
version-date: ${{ steps.next-version.outputs.version-date }}
repository: ${{ github.repository }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: Move Open Issues to Next Milestone
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CURRENT_MILESTONE: ${{ steps.todays-release.outputs.release-version }}
NEXT_MILESTONE: ${{ steps.next-version.outputs.version }}
run: |
current_milestone_number=$(gh api repos/${{ github.repository }}/milestones \
--jq ".[] | select(.title == \"$CURRENT_MILESTONE\") | .number")
if [ -z "$current_milestone_number" ]; then
echo "No milestone found for $CURRENT_MILESTONE"
exit 0
fi
next_milestone_number=$(gh api repos/${{ github.repository }}/milestones \
--jq ".[] | select(.title == \"$NEXT_MILESTONE\") | .number")
if [ -z "$next_milestone_number" ]; then
echo "No milestone found for $NEXT_MILESTONE"
exit 1
fi
echo "Moving open issues from milestone '$CURRENT_MILESTONE' (#$current_milestone_number) to '$NEXT_MILESTONE' (#$next_milestone_number)"
page=1
while true; do
issues=$(gh api "repos/${{ github.repository }}/issues?milestone=$current_milestone_number&state=open&per_page=100&page=$page" \
--jq '.[].number')
if [ -z "$issues" ]; then
break
fi
for issue in $issues; do
echo "Moving issue/PR #$issue to milestone $NEXT_MILESTONE"
gh api repos/${{ github.repository }}/issues/$issue \
--method PATCH \
--field milestone=$next_milestone_number \
--silent
done
page=$((page + 1))
done
echo "Done."
@@ -0,0 +1,10 @@
name: Dependabot PR Build
on: pull_request_target
jobs:
build:
name: Build
uses: spring-projects/spring-security-commercial/.github/workflows/build-pull-request.yml@workflows/v1
if: ${{ github.actor == 'dependabot[bot]' }}
secrets: inherit
-33
View File
@@ -1,33 +0,0 @@
name: Deploy Docs
on:
push:
branches-ignore:
- "gh-pages"
- "dependabot/**"
tags: '**'
repository_dispatch:
types: request-build-reference # legacy
#schedule:
#- cron: '0 10 * * *' # Once per day at 10am UTC
workflow_dispatch:
permissions: read-all
jobs:
build:
runs-on: ubuntu-latest
if: github.repository_owner == 'spring-projects'
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
ref: docs-build
fetch-depth: 1
- name: Dispatch (partial build)
if: github.ref_type == 'branch'
env:
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }}
- name: Dispatch (full build)
if: github.ref_type == 'tag'
env:
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD)
-27
View File
@@ -1,27 +0,0 @@
name: Finalize Release
on:
workflow_dispatch: # Manual trigger
inputs:
version:
description: The Spring Security release to finalize (e.g. 7.0.0-RC2)
required: true
env:
DEVELOCITY_ACCESS_KEY: ${{ secrets.DEVELOCITY_ACCESS_KEY }}
permissions:
contents: read
jobs:
perform-release:
name: Perform Release
uses: spring-io/spring-security-release-tools/.github/workflows/perform-release.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
with:
should-perform-release: true
project-version: ${{ inputs.version }}
milestone-repo-url: https://repo1.maven.org/maven2
release-repo-url: https://repo1.maven.org/maven2
artifact-path: org/springframework/security/spring-security-core
slack-announcing-id: spring-security-announcing
secrets: inherit
@@ -1,34 +0,0 @@
name: Execute Gradle Wrapper Upgrade
on:
schedule:
- cron: '0 2 * * *' # 2am UTC
workflow_dispatch:
permissions:
pull-requests: write
jobs:
upgrade_wrapper:
name: Execution
if: ${{ github.repository == 'spring-projects/spring-security' }}
runs-on: ubuntu-latest
steps:
- name: Set up Git configuration
env:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config --global url."https://unused-username:${TOKEN}@github.com/".insteadOf "https://github.com/"
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Set up JDK 25
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
java-version: '25'
distribution: 'temurin'
- name: Set up Gradle
uses: gradle/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
- name: Upgrade Wrappers
run: ./gradlew clean upgradeGradleWrapperAll --continue -Porg.gradle.java.installations.auto-download=false
env:
WRAPPER_UPGRADE_GIT_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+63
View File
@@ -0,0 +1,63 @@
name: Merge Dependabot PR
on: pull_request_target
run-name: Merge Dependabot PR ${{ github.ref_name }}
permissions: write-all
jobs:
merge-dependabot-pr:
name: Merge Dependabot PR
runs-on: ubuntu-latest
if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'spring-projects/spring-security-commercial' }}
steps:
- uses: actions/checkout@v5
with:
show-progress: false
ref: ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
- name: Set Milestone to Dependabot Pull Request
id: set-milestone
run: |
if test -f pom.xml
then
CURRENT_VERSION=$(mvn help:evaluate -Dexpression="project.version" -q -DforceStdout)
else
CURRENT_VERSION=$(cat gradle.properties | sed -n '/^version=/ { s/^version=//;p }')
fi
export CANDIDATE_VERSION=${CURRENT_VERSION/-SNAPSHOT}
MILESTONE=$(gh api repos/$GITHUB_REPOSITORY/milestones --jq 'map(select(.due_on != null and (.title | startswith(env.CANDIDATE_VERSION)))) | .[0] | .title')
if [ -z $MILESTONE ]
then
gh run cancel ${{ github.run_id }}
echo "::warning title=Cannot merge::No scheduled milestone for $CURRENT_VERSION version"
else
gh pr edit ${{ github.event.pull_request.number }} --milestone $MILESTONE
echo mergeEnabled=true >> $GITHUB_OUTPUT
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Merge Dependabot pull request
if: steps.set-milestone.outputs.mergeEnabled
run: gh pr merge ${{ github.event.pull_request.number }} --auto --rebase
env:
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
send-notification:
name: Send Notification
needs: [ merge-dependabot-pr ]
if: ${{ failure() || cancelled() }}
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@v1
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
@@ -1,35 +0,0 @@
name: Check Milestone
on:
milestone:
types: [created, opened, edited]
env:
DUE_ON: ${{ github.event.milestone.due_on }}
TITLE: ${{ github.event.milestone.title }}
permissions:
contents: read
jobs:
spring-releasetrain-checks:
name: Check DueOn is on a Release Date
runs-on: ubuntu-latest
if: ${{ github.repository == 'spring-projects/spring-security' }}
permissions:
contents: none
steps:
- name: Print Milestone Being Checked
run: echo "Validating DueOn '$DUE_ON' for milestone '$TITLE'"
- name: Validate DueOn
if: env.DUE_ON != ''
run: |
export TOOL_VERSION=0.1.1
wget "https://repo.maven.apache.org/maven2/io/spring/releasetrain/spring-release-train-tools/$TOOL_VERSION/spring-release-train-tools-$TOOL_VERSION.jar"
java -cp "spring-release-train-tools-$TOOL_VERSION.jar" io.spring.releasetrain.CheckMilestoneDueOnMain --dueOn "$DUE_ON" --expectedDayOfWeek MONDAY --expectedMondayCount 3
send-notification:
name: Send Notification
needs: [ spring-releasetrain-checks ]
if: ${{ failure() || cancelled() }}
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
+3 -41
View File
@@ -8,44 +8,6 @@ permissions:
jobs:
build:
name: Build
runs-on: ubuntu-latest
if: ${{ github.repository == 'spring-projects/spring-security' }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@c8668747d7c264864c8c7f7026d0d277d14a78dc # v2.0.6
with:
java-version: '25'
distribution: 'temurin'
- name: Build with Gradle
run: ./gradlew clean build -PskipCheckExpectedBranchVersion --continue --scan
generate-docs:
name: Generate Docs
runs-on: ubuntu-latest
if: ${{ github.repository == 'spring-projects/spring-security' }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Set up gradle
uses: spring-io/spring-gradle-build-action@c8668747d7c264864c8c7f7026d0d277d14a78dc # v2.0.6
with:
java-version: '25'
distribution: 'temurin'
- name: Run Antora
run: ./gradlew -PbuildSrc.skipTests=true :spring-security-docs:antora
- name: Upload Docs
id: upload
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: docs
path: docs/build/site
overwrite: true
send-notification:
name: Send Notification
needs: [ build, generate-docs ]
if: ${{ failure() && github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'spring-projects/spring-security' }}
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
uses: spring-projects/spring-security-commercial/.github/workflows/build-pull-request.yml@workflows/v1
if: ${{ github.actor != 'dependabot[bot]' }}
secrets: inherit
-24
View File
@@ -1,24 +0,0 @@
name: Release Scheduler
on:
schedule:
- cron: '15 15 * * MON' # Every Monday at 3:15pm UTC
workflow_dispatch:
permissions: read-all
jobs:
dispatch_scheduled_releases:
name: Dispatch scheduled releases
if: github.repository_owner == 'spring-projects'
strategy:
matrix:
# List of active maintenance branches.
branch: [ main, 7.0.x, 6.5.x, 6.4.x, 6.3.x ]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 1
- name: Dispatch
env:
GH_TOKEN: ${{ secrets.GH_ACTIONS_REPO_TOKEN }}
run: gh workflow run update-scheduled-release-version.yml -r ${{ matrix.branch }}
+92
View File
@@ -0,0 +1,92 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Build"
run-name: "${{ inputs.callback-ref }} Build"
"on":
workflow_dispatch:
inputs:
callback:
description: "Repository to which a callback should be made upon completion"
required: true
type: "string"
callback-ref:
description: "Ref in the callback repository to which a callback should be made upon completion"
required: true
type: "string"
release-train-maven-repository-url:
description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train"
required: true
type: "string"
permissions:
contents: "read"
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
jobs:
build-release:
name: "Build Release"
runs-on: "ubuntu22-2-8"
steps:
- name: "Prevent Re-runs"
id: "prevent-re-runs"
run: |-
if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then
echo "Re-runs are prohibited. Use the 'Release Train  Retry' workflow to retry build failures"
exit 1
fi
- name: "Set up Java"
id: "set-up-java"
uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0
with:
distribution: "liberica"
java-version: "25"
- name: "Check Out Code"
id: "check-out-code"
uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0
- name: "Build Release"
id: "build-release"
uses: "./.github/actions/release-train-build"
env:
COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}"
COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}"
- name: "Upload Deployment Repository"
id: "upload-deployment-repository"
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
with:
name: "deployment-repository"
path: "deployment-repository/**"
- name: "Upload Deployment Spec"
id: "upload-deployment-spec"
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
with:
archive: "false"
if-no-files-found: "ignore"
name: "deployment-spec"
path: ".github/actions/release-train-build/deployment-spec.yml"
- name: "Save Build System Caches"
id: "save-build-system-caches"
uses: "actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0
with:
key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}"
path: |-
~/.gradle/caches
~/.gradle/wrapper
- name: "Send Callback"
id: "send-callback"
if: "${{ !cancelled() }}"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
gh workflow run callback \
--repo ${{ inputs.callback }} \
--ref ${{ inputs.callback-ref }} \
--field commit-hash=${{ steps.check-out-code.outputs.commit }} \
--field deployment-repository-artifact-identifier=${{ steps.upload-deployment-repository.outputs.artifact-id }} \
--field deployment-spec-artifact-identifier=${{ steps.upload-deployment-spec.outputs.artifact-id }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }} \
--field result=${{ job.status == 'success' && 'built' || 'build-failed' }} \
--field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+55
View File
@@ -0,0 +1,55 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Join"
run-name: "${{ inputs.release-train }} Join"
"on":
workflow_dispatch:
inputs:
deployment-destination:
description: "Destination to which the release should be deployed"
options:
- "Maven Central"
- "Spring Enterprise"
required: true
type: "choice"
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
join-release-train:
name: "Join Release Train"
runs-on: "ubuntu-latest"
steps:
- name: "Join Release Train"
id: "join-release-train"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
run_url=$(
gh workflow run join \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field commit-hash=${{ github.sha }} \
--field deployment-destination=${{ inputs.deployment-destination == 'Maven Central' && 'maven-central' || 'spring-enterprise' }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
)
echo "Dispatched workflow run. Waiting for $run_url to complete."
run_id=${run_url##*/}
watch_exit_code=0
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
if [[ $watch_exit_code -eq 0 ]]; then
echo "Workflow run succeeded."
else
echo "Workflow run failed."
fi
exit $watch_exit_code
+46
View File
@@ -0,0 +1,46 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Leave"
run-name: "${{ inputs.release-train }} Leave"
"on":
workflow_dispatch:
inputs:
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
leave:
name: "Leave"
runs-on: "ubuntu-latest"
steps:
- name: "Leave"
id: "leave"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
run_url=$(
gh workflow run leave \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
)
echo "Dispatched workflow run. Waiting for $run_url to complete."
run_id=${run_url##*/}
watch_exit_code=0
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
if [[ $watch_exit_code -eq 0 ]]; then
echo "Workflow run succeeded."
else
echo "Workflow run failed."
fi
exit $watch_exit_code
+47
View File
@@ -0,0 +1,47 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Ready"
run-name: "${{ inputs.release-train }} Ready"
"on":
workflow_dispatch:
inputs:
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
ready:
name: "Ready"
runs-on: "ubuntu-latest"
steps:
- name: "Ready"
id: "ready"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
run_url=$(
gh workflow run ready \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field commit-hash=${{ github.sha }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
)
echo "Dispatched workflow run. Waiting for $run_url to complete."
run_id=${run_url##*/}
watch_exit_code=0
gh run watch $run_id --repo ${{ inputs.release-train-repository }} --exit-status --interval=3 > /dev/null 2>&1 || watch_exit_code=$?
if [[ $watch_exit_code -eq 0 ]]; then
echo "Workflow run succeeded."
else
echo "Workflow run failed."
fi
exit $watch_exit_code
+34
View File
@@ -0,0 +1,34 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Retry"
run-name: "${{ inputs.release-train }} Retry"
"on":
workflow_dispatch:
inputs:
release-train:
description: "Release train"
required: true
type: "string"
release-train-repository:
default: "spring-io/release-train"
description: "Release train repository"
required: true
type: "string"
permissions:
contents: "none"
jobs:
trigger-retry:
name: "Trigger Retry"
runs-on: "ubuntu-latest"
steps:
- name: "Trigger Retry"
id: "trigger-retry"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
gh workflow run retry \
--repo ${{ inputs.release-train-repository }} \
--ref ${{ inputs.release-train }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }}
+83
View File
@@ -0,0 +1,83 @@
# This file was auto-generated by github-actions-workflow-generator 0.0.6. Do not edit.
# To update it, modify .github/workflow-generator.yml as needed and re-run the generator.
name: "Release Train Test"
run-name: "${{ inputs.callback-ref }} Test"
"on":
workflow_dispatch:
inputs:
callback:
description: "Repository to which a callback should be made upon completion"
required: true
type: "string"
callback-ref:
description: "Ref in the callback repository to which a callback should be made upon completion"
required: true
type: "string"
release-train-maven-repository-url:
description: "URL of a Maven repository to be used to resolve artifacts of projects earlier in the train"
required: true
type: "string"
permissions:
contents: "read"
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
jobs:
test-release:
name: "Test Release"
runs-on: "ubuntu22-2-8"
steps:
- name: "Prevent Re-runs"
id: "prevent-re-runs"
run: |-
if [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then
echo "Re-runs are prohibited. Use the 'Release Train  Retry' workflow to retry test failures"
exit 1
fi
- name: "Set up Java"
id: "set-up-java"
uses: "actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95" # v5.6.0
with:
distribution: "liberica"
java-version: "25"
- name: "Check Out Code"
id: "check-out-code"
uses: "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" # v7.0.0
- name: "Restore Build System Caches"
id: "restore-build-system-caches"
uses: "actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9" # v6.1.0
with:
key: "release-train-${{ inputs.callback-ref }}-${{ github.ref_name }}"
path: |-
~/.gradle/caches
~/.gradle/wrapper
- name: "Test Release"
id: "test-release"
uses: "./.github/actions/release-train-test"
env:
COMMERCIAL_REPO_PASSWORD: "${{ secrets.COMMERCIAL_ARTIFACTORY_PASSWORD }}"
COMMERCIAL_REPO_USERNAME: "${{ secrets.COMMERCIAL_ARTIFACTORY_USERNAME }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_PASSWORD }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_URL: "${{ inputs.release-train-maven-repository-url }}"
RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_MAVEN_REPOSITORY_USERNAME }}"
- name: "Send Callback"
id: "send-callback"
if: "${{ !cancelled() }}"
env:
GH_TOKEN: "${{ secrets.RELEASE_TRAIN_PARTICIPANT_GITHUB_TOKEN }}"
run: |-
gh workflow run callback \
--repo ${{ inputs.callback }} \
--ref ${{ inputs.callback-ref }} \
--field commit-hash=${{ steps.check-out-code.outputs.commit }} \
--field release-branch=${{ github.ref_name }} \
--field release-repository=${{ github.repository }} \
--field result=${{ job.status == 'success' && 'tested' || 'test-failed' }} \
--field workflow-run-url=${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- name: "Upload Build System Reports"
id: "upload-build-system-reports"
if: "${{ failure() }}"
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
with:
name: "build-system-reports"
path: "**/build/reports"
@@ -1,37 +0,0 @@
name: Update Antora UI Spring
on:
schedule:
- cron: '0 10 * * *' # Once per day at 10am UTC
workflow_dispatch:
permissions:
pull-requests: write
issues: write
contents: write
jobs:
update-antora-ui-spring:
name: Update on Supported Branches
if: ${{ github.repository == 'spring-projects/spring-security' }}
runs-on: ubuntu-latest
strategy:
matrix:
branch: [ '6.5.x', '7.0.x', 'main' ]
steps:
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf # v0.0.22
name: Update
with:
docs-branch: ${{ matrix.branch }}
token: ${{ secrets.GITHUB_TOKEN }}
antora-file-path: 'docs/antora-playbook.yml'
update-antora-ui-spring-docs-build:
name: Update on docs-build
if: ${{ github.repository == 'spring-projects/spring-security' }}
runs-on: ubuntu-latest
steps:
- uses: spring-io/spring-doc-actions/update-antora-spring-ui@415e2b11a766ba64799fffb5c97a4f7e17f677cf # v0.0.22
name: Update
with:
docs-branch: 'docs-build'
token: ${{ secrets.GITHUB_TOKEN }}
@@ -1,23 +0,0 @@
name: Update Scheduled Release Version
on:
workflow_dispatch: # Manual trigger only. Triggered by release-scheduler.yml on main.
permissions:
contents: read
jobs:
update-scheduled-release-version:
name: Update Scheduled Release Version
uses: spring-io/spring-security-release-tools/.github/workflows/update-scheduled-release-version.yml@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
secrets: inherit
send-notification:
name: Send Notification
needs: [ update-scheduled-release-version ]
if: ${{ failure() || cancelled() }}
runs-on: ubuntu-latest
steps:
- name: Send Notification
uses: spring-io/spring-security-release-tools/.github/actions/send-notification@b92832ecbc7cbe969201e6beafbde0ee400cf095 # v1.0.15
with:
webhook-url: ${{ secrets.SPRING_SECURITY_CI_GCHAT_WEBHOOK_URL }}
+5 -8
View File
@@ -27,6 +27,7 @@ apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: 'org.springframework.security.versions.verify-dependencies-versions'
apply plugin: 'org.springframework.security.check-expected-branch-version'
apply plugin: 'io.spring.security.release'
apply from: 'commercial-settings.gradle'
group = 'org.springframework.security'
description = 'Spring Security'
@@ -41,10 +42,12 @@ repositories {
}
springRelease {
repositoryOwner = "spring-projects"
repositoryName = "spring-security-commercial"
weekOfMonth = 3
dayOfWeek = 1
referenceDocUrl = "https://docs.spring.io/spring-security/reference/{version}/index.html"
apiDocUrl = "https://docs.spring.io/spring-security/reference/{version}/api/java/index.html"
referenceDocUrl = "https://docs.spring.vmware.com/spring-security/reference/{version}/index.html"
apiDocUrl = "https://docs.spring.vmware.com/spring-security/reference/{version}/api/java/index.html"
replaceSnapshotVersionInReferenceDocUrl = true
}
@@ -72,12 +75,6 @@ allprojects {
}
}
develocity {
buildScan {
termsOfUseUrl = 'https://gradle.com/help/legal-terms-of-use'
termsOfUseAgree = 'yes'
}
}
nohttp {
source.exclude "buildSrc/build/**", "**/build/**", "**/target/**", "javascript/.gradle/**", "javascript/package-lock.json", "javascript/node_modules/**", "javascript/build/**", "javascript/dist/**"
+20 -1
View File
@@ -13,7 +13,26 @@ java {
repositories {
gradlePluginPortal()
mavenCentral()
maven { url = 'https://repo.spring.io/snapshot' }
if (project.hasProperty("artifactoryUsername") && project.hasProperty("artifactoryPassword")) {
maven {
name "spring-commercial-release"
url "https://usw1.packages.broadcom.com/spring-enterprise-maven-prod-local"
credentials {
username project.artifactoryUsername
password project.artifactoryPassword
}
}
}
if (System.getenv("RELEASE_TRAIN_MAVEN_REPOSITORY_URL") != null) {
maven {
name = "Release Train"
url = System.getenv("RELEASE_TRAIN_MAVEN_REPOSITORY_URL")
credentials {
username = System.getenv("RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME")
password = System.getenv("RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD")
}
}
}
}
sourceSets {
@@ -43,6 +43,16 @@ class RepositoryConventionPlugin implements Plugin<Project> {
}
}
mavenCentral()
if (System.getenv("RELEASE_TRAIN_MAVEN_REPOSITORY_URL") != null) {
maven {
name = "Release Train"
url = System.getenv("RELEASE_TRAIN_MAVEN_REPOSITORY_URL")
credentials {
username = System.getenv("RELEASE_TRAIN_MAVEN_REPOSITORY_USERNAME")
password = System.getenv("RELEASE_TRAIN_MAVEN_REPOSITORY_PASSWORD")
}
}
}
if (isSnapshot) {
maven {
name = 'artifactory-snapshot'
@@ -22,6 +22,12 @@ public class MavenPublishingConventionsPlugin implements Plugin<Project> {
@Override
public void execute(MavenPublishPlugin mavenPublish) {
PublishingExtension publishing = project.getExtensions().getByType(PublishingExtension.class);
if (project.hasProperty("deploymentRepository")) {
publishing.getRepositories().maven((mavenRepository) -> {
mavenRepository.setUrl(project.property("deploymentRepository"));
mavenRepository.setName("deployment");
});
}
publishing.getPublications().withType(MavenPublication.class)
.all((mavenPublication) -> MavenPublishingConventionsPlugin.this.customizePom(mavenPublication.getPom(), project));
MavenPublishingConventionsPlugin.this.customizeJavaPlugin(project);
@@ -108,7 +108,7 @@ public class CasAuthenticationToken extends AbstractAuthenticationToken implemen
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.isTrue(!"".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;
@@ -34,6 +34,7 @@ import org.springframework.security.core.userdetails.UserDetails;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatNoException;
/**
* Tests {@link CasAuthenticationToken}.
@@ -182,4 +183,38 @@ public class CasAuthenticationTokenTests {
assertThat(authorities).containsExactlyInAnyOrder("FACTOR_ONE", "FACTOR_TWO");
}
@Test
public void toBuilderWhenPrincipalIsEmpty() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES,
makeUserDetails(), assertion);
assertThatIllegalArgumentException().isThrownBy(() -> token.toBuilder().principal(null).build());
assertThatIllegalArgumentException().isThrownBy(() -> token.toBuilder().principal("").build());
}
@Test
public void toBuilderWhenPrincipalIsNotEmpty() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES,
makeUserDetails(), assertion);
assertThatNoException().isThrownBy(() -> token.toBuilder().principal("principal").build());
}
@Test
public void toBuilderWhenCredentialsIsEmpty() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES,
makeUserDetails(), assertion);
assertThatIllegalArgumentException().isThrownBy(() -> token.toBuilder().credentials(null).build());
assertThatIllegalArgumentException().isThrownBy(() -> token.toBuilder().credentials("").build());
}
@Test
public void toBuilderWhenCredentialsIsNotEmpty() {
final Assertion assertion = new AssertionImpl("test");
CasAuthenticationToken token = new CasAuthenticationToken("key", makeUserDetails(), "Password", this.ROLES,
makeUserDetails(), assertion);
assertThatNoException().isThrownBy(() -> token.toBuilder().credentials("credentials").build());
}
}
+27
View File
@@ -0,0 +1,27 @@
subprojects {
repositories {
mavenCentral()
def repoUsername = project.findProperty("artifactoryUsername") ?: System.getenv("COMMERCIAL_REPO_USERNAME")
def repoPassword = project.findProperty("artifactoryPassword") ?: System.getenv("COMMERCIAL_REPO_PASSWORD")
if (repoUsername && repoPassword) {
maven {
name "spring-commercial-release"
url "https://usw1.packages.broadcom.com/spring-enterprise-maven-prod-local"
credentials {
username repoUsername
password repoPassword
}
}
if ("$version".endsWith("-SNAPSHOT")) {
maven {
name "spring-commercial-snapshot"
url "https://usw1.packages.broadcom.com/spring-enterprise-maven-dev-local"
credentials {
username repoUsername
password repoPassword
}
}
}
}
}
}
@@ -17,6 +17,7 @@
package org.springframework.security.config.annotation.authentication.configurers.ldap;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import org.springframework.ldap.core.support.BaseLdapPathContextSource;
@@ -590,7 +591,7 @@ public class LdapAuthenticationProviderConfigurer<B extends ProviderManagerBuild
}
private int getDefaultPort() {
try (ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT)) {
try (ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT, 50, InetAddress.getLoopbackAddress())) {
return serverSocket.getLocalPort();
}
catch (IOException ex) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* 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.
@@ -39,6 +39,9 @@ import org.springframework.util.ClassUtils;
@Deprecated
final class GlobalMethodSecuritySelector implements ImportSelector {
private static final boolean isAccessPresent = ClassUtils.isPresent(
"org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor", null);
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
Class<EnableGlobalMethodSecurity> annoType = EnableGlobalMethodSecurity.class;
@@ -59,16 +62,26 @@ final class GlobalMethodSecuritySelector implements ImportSelector {
boolean jsr250Enabled = attributes.getBoolean("jsr250Enabled");
List<String> classNames = new ArrayList<>(4);
if (isProxy) {
assertAccessModulePresent();
classNames.add(MethodSecurityMetadataSourceAdvisorRegistrar.class.getName());
}
classNames.add(autoProxyClassName);
if (!skipMethodSecurityConfiguration) {
assertAccessModulePresent();
classNames.add(GlobalMethodSecurityConfiguration.class.getName());
}
if (jsr250Enabled) {
assertAccessModulePresent();
classNames.add(Jsr250MetadataSourceConfiguration.class.getName());
}
return classNames.toArray(new String[0]);
}
private static void assertAccessModulePresent() {
Assert.state(isAccessPresent,
() -> "@EnableGlobalMethodSecurity requires the spring-security-access dependency on the "
+ "classpath. Please add spring-security-access, or migrate to @EnableMethodSecurity "
+ "which does not require it.");
}
}
@@ -26,6 +26,7 @@ import org.springframework.context.annotation.AutoProxyRegistrar;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
@@ -35,6 +36,8 @@ import org.springframework.util.ClassUtils;
*/
class ReactiveMethodSecuritySelector implements ImportSelector {
private static final String METHOD_SECURITY_METADATA_SOURCE_ADVISOR = "org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor";
private static final boolean isDataPresent = ClassUtils
.isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null);
@@ -56,6 +59,11 @@ class ReactiveMethodSecuritySelector implements ImportSelector {
imports.add(ReactiveAuthorizationManagerMethodSecurityConfiguration.class.getName());
}
else {
Assert.state(
ClassUtils.isPresent(METHOD_SECURITY_METADATA_SOURCE_ADVISOR, ClassUtils.getDefaultClassLoader()),
() -> "@EnableReactiveMethodSecurity(useAuthorizationManager = false) requires the "
+ "spring-security-access dependency on the classpath. Please add spring-security-access, "
+ "or use the default useAuthorizationManager = true which does not require it.");
imports.add(ReactiveMethodSecurityConfiguration.class.getName());
}
if (isDataPresent) {
@@ -379,9 +379,8 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
}
if (filter instanceof AuthorizationFilter authorization) {
AuthorizationManager<HttpServletRequest> authorizationManager = authorization.getAuthorizationManager();
builder.add(securityFilterChain::matches,
(authentication, context) -> (AuthorizationDecision) authorizationManager
.authorize(authentication, context.getRequest()));
builder.add(securityFilterChain::matches, (authentication, context) -> authorizationManager
.authorize(authentication, context.getRequest()));
mappings = true;
}
}
@@ -706,8 +706,10 @@ public final class OAuth2ResourceServerConfigurer<H extends HttpSecurityBuilder<
}
private void configure(H http) {
DPoPAuthenticationProvider authenticationProvider = new DPoPAuthenticationProvider(
getTokenAuthenticationManager(http));
http.authenticationProvider(postProcess(authenticationProvider));
AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
http.authenticationProvider(new DPoPAuthenticationProvider(getTokenAuthenticationManager(http)));
AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManager,
getAuthenticationConverter());
authenticationFilter.setRequestMatcher(getRequestMatcher());
@@ -17,6 +17,7 @@
package org.springframework.security.config.ldap;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import org.w3c.dom.Element;
@@ -188,7 +189,7 @@ public class LdapServerBeanDefinitionParser implements BeanDefinitionParser {
}
private String getDefaultPort() {
try (ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT)) {
try (ServerSocket serverSocket = new ServerSocket(DEFAULT_PORT, 50, InetAddress.getLoopbackAddress())) {
return String.valueOf(serverSocket.getLocalPort());
}
catch (IOException ex) {
@@ -304,8 +304,7 @@ class SpringSecurityCoreVersionSerializableTests {
private static String getCurrentVersion() {
String version = System.getProperty("springSecurityVersion");
String[] parts = version.split("\\.");
parts[2] = "x";
return String.join(".", parts);
return parts[0] + "." + parts[1] + ".x";
}
private static String getPreviousVersion() {
@@ -318,8 +317,7 @@ class SpringSecurityCoreVersionSerializableTests {
parts[0] = String.valueOf(Integer.parseInt(parts[0]) - 1);
parts[1] = "5"; // FIXME: this should not be hard coded
}
parts[2] = "x";
return String.join(".", parts);
return parts[0] + "." + parts[1] + ".x";
}
}
@@ -0,0 +1,138 @@
/*
* 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.method.configuration;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.test.support.ClassPathExclusions;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for gh-19441: {@code spring-security-access} moved
* {@link org.springframework.security.access.intercept.aopalliance.MethodSecurityMetadataSourceAdvisor}
* out of {@code spring-security-core} and into the optional
* {@code spring-security-access} module. {@link EnableMethodSecurity} and
* {@link EnableReactiveMethodSecurity}'s default (AuthorizationManager-based) mode never
* needed that class and continue to work without {@code spring-security-access} on the
* classpath, but the deprecated legacy method security annotations do need it and
* previously failed with a confusing {@link NoClassDefFoundError} instead of an
* actionable message.
*/
@ClassPathExclusions("spring-security-access-*.jar")
public class Gh19441Tests {
@Test
public void enableMethodSecurityWhenAccessModuleAbsentThenContextStartsCleanly() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
context.register(EnableMethodSecurityConfig.class);
context.refresh();
}
}
@Test
public void enableReactiveMethodSecurityWhenAccessModuleAbsentThenContextStartsCleanly() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
context.register(EnableReactiveMethodSecurityConfig.class);
context.refresh();
}
}
@Test
public void enableGlobalMethodSecurityWhenProxyModeAndAccessModuleAbsentThenClearException() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
context.register(EnableGlobalMethodSecurityProxyConfig.class);
assertThatExceptionOfType(Exception.class).isThrownBy(context::refresh)
.havingRootCause()
.isInstanceOf(IllegalStateException.class)
.withMessageContaining("spring-security-access");
}
}
@Test
public void enableGlobalMethodSecurityWhenAspectJModeAndAccessModuleAbsentThenClearException() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
context.register(EnableGlobalMethodSecurityAspectJConfig.class);
assertThatExceptionOfType(Exception.class).isThrownBy(context::refresh)
.havingRootCause()
.isInstanceOf(IllegalStateException.class)
.withMessageContaining("spring-security-access");
}
}
@Test
public void enableReactiveMethodSecurityWhenUseAuthorizationManagerFalseAndAccessModuleAbsentThenClearException() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
context.register(EnableReactiveMethodSecurityLegacyConfig.class);
assertThatExceptionOfType(Exception.class).isThrownBy(context::refresh)
.havingRootCause()
.isInstanceOf(IllegalStateException.class)
.withMessageContaining("spring-security-access");
}
}
@Test
public void enableGlobalMethodSecurityWhenAspectJModeAndJsr250EnabledAndConfigurationSubclassedAndAccessModuleAbsentThenClearException() {
try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {
context.register(EnableGlobalMethodSecurityAspectJJsr250SubclassedConfig.class);
assertThatExceptionOfType(Exception.class).isThrownBy(context::refresh)
.havingRootCause()
.isInstanceOf(IllegalStateException.class)
.withMessageContaining("spring-security-access");
}
}
@Configuration
@EnableMethodSecurity
static class EnableMethodSecurityConfig {
}
@Configuration
@EnableReactiveMethodSecurity
static class EnableReactiveMethodSecurityConfig {
}
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
static class EnableGlobalMethodSecurityProxyConfig {
}
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, mode = AdviceMode.ASPECTJ)
static class EnableGlobalMethodSecurityAspectJConfig {
}
@Configuration
@EnableReactiveMethodSecurity(useAuthorizationManager = false)
static class EnableReactiveMethodSecurityLegacyConfig {
}
@Configuration
@EnableGlobalMethodSecurity(jsr250Enabled = true, mode = AdviceMode.ASPECTJ)
static class EnableGlobalMethodSecurityAspectJJsr250SubclassedConfig extends GlobalMethodSecurityConfiguration {
}
}
@@ -22,6 +22,7 @@ import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Base64;
@@ -30,6 +31,7 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import com.nimbusds.jose.jwk.ECKey;
import com.nimbusds.jose.jwk.JWK;
@@ -47,20 +49,26 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.jose.TestJwks;
import org.springframework.security.oauth2.jose.TestKeys;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.DPoPProofContext;
import org.springframework.security.oauth2.jwt.DPoPProofJwtDecoderFactory;
import org.springframework.security.oauth2.jwt.DPoPProofReplayValidator;
import org.springframework.security.oauth2.jwt.JwsHeader;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;
import org.springframework.security.oauth2.server.resource.authentication.DPoPAuthenticationProvider;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -247,8 +255,8 @@ public class DPoPAuthenticationTests {
)
.oauth2ResourceServer((oauth2) -> oauth2
.jwt(Customizer.withDefaults())
.dPoP(Customizer.withDefaults()));
.dPoP(Customizer.withDefaults())
.withObjectPostProcessor(dPoPProofVerifierFactoryCustomizer()));
// @formatter:on
return http.build();
}
@@ -258,6 +266,25 @@ public class DPoPAuthenticationTests {
return NimbusJwtDecoder.withPublicKey(PROVIDER_RSA_PUBLIC_KEY).build();
}
private ObjectPostProcessor<DPoPAuthenticationProvider> dPoPProofVerifierFactoryCustomizer() {
return new ObjectPostProcessor<>() {
@Override
public <O extends DPoPAuthenticationProvider> O postProcess(O authenticationProvider) {
DPoPProofReplayValidator.InMemoryCache inMemoryCache = new DPoPProofReplayValidator.InMemoryCache();
inMemoryCache.setMaxSize(50_000);
inMemoryCache.setMaxRequestsPerKey(500);
DPoPProofReplayValidator dPoPProofReplayValidator = new DPoPProofReplayValidator(inMemoryCache);
dPoPProofReplayValidator.setClockSkew(Duration.ofSeconds(60));
Function<DPoPProofContext, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = DPoPProofJwtDecoderFactory
.createDefaultJwtValidatorFactory(Collections.singletonList(dPoPProofReplayValidator));
DPoPProofJwtDecoderFactory dPoPProofJwtDecoderFactory = new DPoPProofJwtDecoderFactory();
dPoPProofJwtDecoderFactory.setJwtValidatorFactory(jwtValidatorFactory);
authenticationProvider.setDPoPProofVerifierFactory(dPoPProofJwtDecoderFactory);
return authenticationProvider;
}
};
}
}
@RestController
@@ -78,9 +78,7 @@ public abstract class AbstractSecurityExpressionHandler<T>
public final EvaluationContext createEvaluationContext(@Nullable Authentication authentication, T invocation) {
SecurityExpressionOperations root = createSecurityExpressionRoot(authentication, invocation);
StandardEvaluationContext ctx = createEvaluationContextInternal(authentication, invocation);
if (this.beanResolver != null) {
ctx.setBeanResolver(this.beanResolver);
}
ctx.setBeanResolver(this.beanResolver);
ctx.setRootObject(root);
return ctx;
}
@@ -23,7 +23,6 @@ import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
@@ -89,13 +88,12 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
}
@Override
@SuppressWarnings("NullAway") // FIXME: Dataflow analysis limitation
public EvaluationContext createEvaluationContext(Supplier<? extends @Nullable Authentication> authentication,
MethodInvocation mi) {
MethodSecurityExpressionOperations root = createSecurityExpressionRoot(authentication, mi);
MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(root, mi,
getParameterNameDiscoverer());
Optional.ofNullable(getBeanResolver()).ifPresent(ctx::setBeanResolver);
ctx.setBeanResolver(getBeanResolver());
return ctx;
}
@@ -184,7 +184,7 @@ public abstract class AbstractUserDetailsReactiveAuthenticationManager
* @since 5.2
*/
public void setPostAuthenticationChecks(UserDetailsChecker postAuthenticationChecks) {
Assert.notNull(this.postAuthenticationChecks, "postAuthenticationChecks cannot be null");
Assert.notNull(postAuthenticationChecks, "postAuthenticationChecks cannot be null");
this.postAuthenticationChecks = postAuthenticationChecks;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* 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.
@@ -132,7 +132,7 @@ public class KeyBasedPersistenceTokenService implements TokenService, Initializi
// Verification
String content = creationTime + ":" + pseudoRandomNumber + ":" + extendedInfo.toString();
String expectedSha512Hex = Sha512DigestUtils.shaHex(content + ":" + serverSecret);
Assert.isTrue(expectedSha512Hex.equals(sha1Hex), "Key verification failure");
Assert.isTrue(Utf8.isEqual(expectedSha512Hex, sha1Hex), "Key verification failure");
return new DefaultToken(key, creationTime, extendedInfo.toString());
}
@@ -150,7 +150,7 @@ public class InMemoryUserDetailsManager implements UserDetailsManager, UserDetai
else {
this.logger.debug("No authentication manager set. Password won't be re-checked.");
}
MutableUserDetails user = this.users.get(username);
MutableUserDetails user = this.users.get(username.toLowerCase(Locale.ROOT));
Assert.state(user != null, "Current user doesn't exist in database.");
user.setPassword(newPassword);
}
@@ -259,7 +259,9 @@ public final class InetAddressMatchers {
if (address.isLoopbackAddress() || address.isLinkLocalAddress() || address.isSiteLocalAddress()) {
return true;
}
if (address.isAnyLocalAddress()) {
return true;
}
byte[] rawAddress = address.getAddress();
if (rawAddress.length == 16) {
@@ -335,6 +337,9 @@ public final class InetAddressMatchers {
@Override
public boolean matches(@Nullable InetAddress address) {
if (address == null) {
return false;
}
return !this.internalMatcher.matches(address);
}
@@ -90,6 +90,12 @@ public class UserDetailsRepositoryReactiveAuthenticationManagerTests {
assertThatExceptionOfType(IllegalArgumentException.class).isThrownBy(() -> this.manager.setScheduler(null));
}
@Test
public void setPostAuthenticationChecksWhenNullThenIllegalArgumentException() {
assertThatExceptionOfType(IllegalArgumentException.class)
.isThrownBy(() -> this.manager.setPostAuthenticationChecks(null));
}
@Test
public void authenticateWhenCustomSchedulerThenUsed() {
given(this.scheduler.schedule(any())).willAnswer((a) -> {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-present the original author or authors.
* 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.
@@ -123,6 +123,22 @@ public class InMemoryUserDetailsManagerTests {
verify(strategy).getContext();
}
@Test
public void changePasswordWhenCurrentUsernameIsNotInLowercaseThenChangesPassword() {
UserDetails userNotLowerCase = User.withUserDetails(PasswordEncodedUser.user()).username("User").build();
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(userNotLowerCase);
Authentication authentication = new UsernamePasswordAuthenticationToken("User", userNotLowerCase.getPassword(),
userNotLowerCase.getAuthorities());
SecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);
given(strategy.getContext()).willReturn(new SecurityContextImpl(authentication));
manager.setSecurityContextHolderStrategy(strategy);
String newPassword = "newPassword";
manager.changePassword(userNotLowerCase.getPassword(), newPassword);
assertThat(manager.loadUserByUsername("User").getPassword()).isEqualTo(newPassword);
}
@Test
public void createUserWhenUserAlreadyExistsThenException() {
assertThatIllegalArgumentException().isThrownBy(() -> this.manager.createUser(this.user))
@@ -276,16 +276,17 @@ class InetAddressMatchersTests {
}
@ParameterizedTest
@ValueSource(strings = { "127.0.0.1", "127.0.0.255" })
@ValueSource(strings = { "127.0.0.1", "127.0.0.255", "0.0.0.0" })
void matchesWhenIpv4LoopbackThenReturnsTrue(String address) throws Exception {
InetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();
assertThat(matcher.matches(InetAddress.getByName(address))).isTrue();
}
@Test
void matchesWhenIpv6LoopbackThenReturnsTrue() throws Exception {
@ParameterizedTest
@ValueSource(strings = { "::1", "::" })
void matchesWhenIpv6LoopbackThenReturnsTrue(String address) throws Exception {
InetAddressMatcher matcher = InetAddressMatchers.matchInternal().build();
assertThat(matcher.matches(InetAddress.getByName("::1"))).isTrue();
assertThat(matcher.matches(InetAddress.getByName(address))).isTrue();
}
@ParameterizedTest
@@ -415,6 +416,12 @@ class InetAddressMatchersTests {
@Nested
class ExternalInetAddressMatcherTests {
@Test
void matchesWhenInetAddressNullThenReturnsFalse() {
InetAddressMatcher matcher = InetAddressMatchers.matchExternal().build();
assertThat(matcher.matches((InetAddress) null)).isFalse();
}
@ParameterizedTest
@ValueSource(strings = { "8.8.8.8", "1.1.1.1" })
void matchesWhenIpv4PublicThenReturnsTrue(String address) throws Exception {
@@ -21,6 +21,9 @@ import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import org.jspecify.annotations.Nullable;
/**
* UTF-8 Charset encoder/decoder.
@@ -66,4 +69,21 @@ public final class Utf8 {
}
}
/**
* Constant time comparison to prevent against timing attacks.
* @param expected the expected {@link CharSequence}
* @param actual the actual {@link CharSequence}
* @return true if {@code expected} and {@code actual} are equal, false otherwise
* @since 5.7.26
*/
public static boolean isEqual(@Nullable CharSequence expected, @Nullable CharSequence actual) {
byte[] expectedBytes = bytesUtf8(expected);
byte[] actualBytes = bytesUtf8(actual);
return MessageDigest.isEqual(expectedBytes, actualBytes);
}
private static byte @Nullable [] bytesUtf8(@Nullable CharSequence s) {
return (s != null) ? Utf8.encode(s) : null;
}
}
@@ -37,7 +37,9 @@ import org.springframework.security.crypto.util.EncodingUtils;
*
* @author Keith Donald
* @author Dave Syer
* @deprecated Use {@link AesCbcBytesEncryptor} or {@link AesGcmBytesEncryptor} instead.
*/
@Deprecated
public final class AesBytesEncryptor implements BytesEncryptor {
private final SecretKey secretKey;
@@ -0,0 +1,165 @@
/*
* 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.crypto.encrypt;
import java.util.Objects;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.security.crypto.keygen.KeyGenerators;
import org.springframework.security.crypto.util.EncodingUtils;
/**
* {@link BytesEncryptor} that uses 256-bit AES/CBC/PKCS5Padding with a random 16-byte
* initialization vector. The IV is prepended to the ciphertext on encrypt and stripped on
* decrypt.
*
* <p>
* Note that CBC mode provides confidentiality but <em>not</em> integrity or authenticity.
* Applications that require authenticated encryption should prefer
* {@link AesGcmBytesEncryptor}. See the <a href=
* "https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html">
* OWASP Cryptographic Storage Cheat Sheet</a> for guidance on choosing a cipher mode.
*
* <p>
* When key derivation is used via {@link #withPassword(String, CharSequence)}, the key is
* derived using PBKDF2WithHmacSHA256 with {@code DEFAULT_PBKDF2_ITERATIONS} iterations
* per the <a href=
* "https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html">
* OWASP Password Storage Cheat Sheet</a>. Because derivation is intentionally expensive,
* the encryptor instance should be created once and reused rather than constructed
* per-operation.
*
* @author Josh Cummings
* @since 5.7.26
* @see AesGcmBytesEncryptor
* @see AesBytesEncryptor
*/
public final class AesCbcBytesEncryptor implements BytesEncryptor {
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
private static final int IV_LENGTH_BYTES = 16;
private static final int DEFAULT_PBKDF2_ITERATIONS = 600_000;
private final SecretKey secretKey;
private final Cipher encryptor;
private final Cipher decryptor;
private final BytesKeyGenerator ivGenerator;
private AesCbcBytesEncryptor(SecretKey secretKey, BytesKeyGenerator ivGenerator) {
this.secretKey = new SecretKeySpec(secretKey.getEncoded(), "AES");
this.encryptor = CipherUtils.newCipher(ALGORITHM);
this.decryptor = CipherUtils.newCipher(ALGORITHM);
this.ivGenerator = ivGenerator;
}
/**
* Creates an encryptor that derives its key from the given password and hex-encoded
* salt using PBKDF2WithHmacSHA1.
* @param password the password value
* @param salt the hex-encoded salt value
*/
public static Builder withPassword(String password, CharSequence salt) {
return new Builder(password, salt);
}
/**
* Creates an encryptor using the supplied {@link SecretKey}.
* @param secretKey the secret (symmetric) key
*/
public static Builder withSecretKey(SecretKey secretKey) {
return new Builder(secretKey);
}
@Override
public byte[] encrypt(byte[] bytes) {
synchronized (this.encryptor) {
byte[] iv = this.ivGenerator.generateKey();
CipherUtils.initCipher(this.encryptor, Cipher.ENCRYPT_MODE, this.secretKey, new IvParameterSpec(iv));
byte[] ciphertext = CipherUtils.doFinal(this.encryptor, bytes);
return EncodingUtils.concatenate(iv, ciphertext);
}
}
@Override
public byte[] decrypt(byte[] encryptedBytes) {
int ivLength = this.ivGenerator.getKeyLength();
byte[] iv = EncodingUtils.subArray(encryptedBytes, 0, ivLength);
byte[] ciphertext = EncodingUtils.subArray(encryptedBytes, ivLength, encryptedBytes.length);
synchronized (this.decryptor) {
CipherUtils.initCipher(this.decryptor, Cipher.DECRYPT_MODE, this.secretKey, new IvParameterSpec(iv));
return CipherUtils.doFinal(this.decryptor, ciphertext);
}
}
private static SecretKey deriveKey(String password, CharSequence salt) {
return CipherUtils.newSecretKey("PBKDF2WithHmacSHA256",
new PBEKeySpec(password.toCharArray(), Hex.decode(salt), DEFAULT_PBKDF2_ITERATIONS, 256));
}
/**
* A Builder for {@link AesCbcBytesEncryptor}.
*/
public static final class Builder {
private final SecretKey secretKey;
private BytesKeyGenerator ivGenerator = KeyGenerators.secureRandom(IV_LENGTH_BYTES);
private Builder(SecretKey secretKey) {
this.secretKey = secretKey;
}
private Builder(String password, CharSequence salt) {
this.secretKey = deriveKey(password, salt);
}
/**
* Sets the {@link BytesKeyGenerator} to use for generating the initialization
* vector.
* @param ivGenerator the {@link BytesKeyGenerator} to use for generating the
* initialization vector
* @return this builder
*/
public Builder ivGenerator(BytesKeyGenerator ivGenerator) {
Objects.requireNonNull(ivGenerator, "ivGenerator cannot be null");
this.ivGenerator = ivGenerator;
return this;
}
/**
* Builds the {@link AesCbcBytesEncryptor}.
* @return the {@link AesCbcBytesEncryptor}
*/
public AesCbcBytesEncryptor build() {
return new AesCbcBytesEncryptor(this.secretKey, this.ivGenerator);
}
}
}
@@ -0,0 +1,172 @@
/*
* 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.crypto.encrypt;
import java.util.Objects;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.security.crypto.keygen.KeyGenerators;
import org.springframework.security.crypto.util.EncodingUtils;
/**
* {@link BytesEncryptor} that uses 256-bit AES/GCM/NoPadding with a random 16-byte
* initialization vector and a 128-bit authentication tag. The IV is prepended to the
* ciphertext on encrypt and stripped on decrypt. GCM provides authenticated encryption
* (AEAD): both confidentiality and integrity are protected, and decryption throws if the
* ciphertext has been tampered with.
*
* <p>
* This class uses a 16-byte (128-bit) IV rather than the 12-byte (96-bit) IV recommended
* by NIST SP 800-38D for GCM. Both lengths are cryptographically valid; the 16-byte
* choice maintains consistency with the rest of the Spring Security crypto module. For
* additional guidance, please see the <a href=
* "https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html">
* OWASP Cryptographic Storage Cheat Sheet</a>.
*
* <p>
* When key derivation is used via {@link #withPassword(String, CharSequence)}, the key is
* derived using PBKDF2WithHmacSHA256 with {@code DEFAULT_PBKDF2_ITERATIONS} iterations
* per the <a href=
* "https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html">
* OWASP Password Storage Cheat Sheet</a>. Because derivation is intentionally expensive,
* the encryptor instance should be created once and reused rather than constructed
* per-operation.
*
* @author Josh Cummings
* @since 5.7.26
* @see AesCbcBytesEncryptor
* @see AesBytesEncryptor
*/
public final class AesGcmBytesEncryptor implements BytesEncryptor {
private static final String ALGORITHM = "AES/GCM/NoPadding";
private static final int IV_LENGTH_BYTES = 16;
private static final int TAG_LENGTH_BITS = 128;
private static final int DEFAULT_PBKDF2_ITERATIONS = 600_000;
private final SecretKey secretKey;
private final Cipher encryptor;
private final Cipher decryptor;
private final BytesKeyGenerator ivGenerator;
private AesGcmBytesEncryptor(SecretKey secretKey, BytesKeyGenerator ivGenerator) {
this.secretKey = new SecretKeySpec(secretKey.getEncoded(), "AES");
this.encryptor = CipherUtils.newCipher(ALGORITHM);
this.decryptor = CipherUtils.newCipher(ALGORITHM);
this.ivGenerator = ivGenerator;
}
/**
* Creates an encryptor that derives its key from the given password and hex-encoded
* salt using PBKDF2WithHmacSHA1.
* @param password the password value
* @param salt the hex-encoded salt value
*/
public static Builder withPassword(String password, CharSequence salt) {
return new Builder(password, salt);
}
/**
* Creates an encryptor using the supplied {@link SecretKey}.
* @param secretKey the secret (symmetric) key
*/
public static Builder withSecretKey(SecretKey secretKey) {
return new Builder(secretKey);
}
@Override
public byte[] encrypt(byte[] bytes) {
synchronized (this.encryptor) {
byte[] iv = this.ivGenerator.generateKey();
CipherUtils.initCipher(this.encryptor, Cipher.ENCRYPT_MODE, this.secretKey,
new GCMParameterSpec(TAG_LENGTH_BITS, iv));
byte[] ciphertext = CipherUtils.doFinal(this.encryptor, bytes);
return EncodingUtils.concatenate(iv, ciphertext);
}
}
@Override
public byte[] decrypt(byte[] encryptedBytes) {
int ivLength = this.ivGenerator.getKeyLength();
byte[] iv = EncodingUtils.subArray(encryptedBytes, 0, ivLength);
byte[] ciphertext = EncodingUtils.subArray(encryptedBytes, ivLength, encryptedBytes.length);
synchronized (this.decryptor) {
CipherUtils.initCipher(this.decryptor, Cipher.DECRYPT_MODE, this.secretKey,
new GCMParameterSpec(TAG_LENGTH_BITS, iv));
return CipherUtils.doFinal(this.decryptor, ciphertext);
}
}
private static SecretKey deriveKey(String password, CharSequence salt) {
return CipherUtils.newSecretKey("PBKDF2WithHmacSHA256",
new PBEKeySpec(password.toCharArray(), Hex.decode(salt), DEFAULT_PBKDF2_ITERATIONS, 256));
}
/**
* A Builder for {@link AesGcmBytesEncryptor}.
*/
public static final class Builder {
private final SecretKey secretKey;
private BytesKeyGenerator ivGenerator = KeyGenerators.secureRandom(IV_LENGTH_BYTES);
private Builder(SecretKey secretKey) {
this.secretKey = secretKey;
}
private Builder(String password, CharSequence salt) {
this.secretKey = deriveKey(password, salt);
}
/**
* Sets the {@link BytesKeyGenerator} to use for generating the initialization
* vector.
* @param ivGenerator the {@link BytesKeyGenerator} to use for generating the
* initialization vector
* @return this builder
*/
public Builder ivGenerator(BytesKeyGenerator ivGenerator) {
Objects.requireNonNull(ivGenerator, "ivGenerator cannot be null");
this.ivGenerator = ivGenerator;
return this;
}
/**
* Builds the {@link AesGcmBytesEncryptor}.
* @return the {@link AesGcmBytesEncryptor}
*/
public AesGcmBytesEncryptor build() {
return new AesGcmBytesEncryptor(this.secretKey, this.ivGenerator);
}
}
}
@@ -16,7 +16,6 @@
package org.springframework.security.crypto.encrypt;
import org.springframework.security.crypto.encrypt.AesBytesEncryptor.CipherAlgorithm;
import org.springframework.security.crypto.keygen.KeyGenerators;
/**
@@ -42,9 +41,14 @@ public final class Encryptors {
* not be shared
* @param salt a hex-encoded, random, site-global salt value to use to generate the
* key
* @deprecated Use {@link AesGcmBytesEncryptor#withPassword(String, CharSequence)}
* instead.
*/
@Deprecated
@SuppressWarnings("deprecation")
public static BytesEncryptor stronger(CharSequence password, CharSequence salt) {
return new AesBytesEncryptor(password.toString(), salt, KeyGenerators.secureRandom(16), CipherAlgorithm.GCM);
return new AesBytesEncryptor(password.toString(), salt, KeyGenerators.secureRandom(16),
AesBytesEncryptor.CipherAlgorithm.GCM);
}
/**
@@ -53,18 +57,16 @@ public final class Encryptors {
* Function #2). Salts the password to prevent dictionary attacks against the key. The
* provided salt is expected to be hex-encoded; it should be random and at least 8
* bytes in length. Also applies a random 16-byte initialization vector to ensure each
* encrypted message will be unique. Requires Java 6. NOTE: This mode is not
* <a href="https://en.wikipedia.org/wiki/Authenticated_encryption">authenticated</a>
* and does not provide any guarantees about the authenticity of the data. For a more
* secure alternative, users should prefer
* {@link #stronger(CharSequence, CharSequence)}.
* encrypted message will be unique. Requires Java 6.
* @param password the password used to generate the encryptor's secret key; should
* not be shared
* @param salt a hex-encoded, random, site-global salt value to use to generate the
* key
*
* @see Encryptors#stronger(CharSequence, CharSequence)
* @deprecated Use {@link AesCbcBytesEncryptor#withPassword(String, CharSequence)}
* instead.
*/
@Deprecated
@SuppressWarnings("deprecation")
public static BytesEncryptor standard(CharSequence password, CharSequence salt) {
return new AesBytesEncryptor(password.toString(), salt, KeyGenerators.secureRandom(16));
}
@@ -74,8 +76,12 @@ public final class Encryptors {
* text is hex-encoded.
* @param password the password used to generate the encryptor's secret key; should
* not be shared
* @deprecated Use {@link AesGcmBytesEncryptor#withPassword(String, CharSequence)}
* instead.
* @see Encryptors#stronger(CharSequence, CharSequence)
*/
@Deprecated
@SuppressWarnings("deprecation")
public static TextEncryptor delux(CharSequence password, CharSequence salt) {
return new HexEncodingTextEncryptor(stronger(password, salt));
}
@@ -85,8 +91,12 @@ public final class Encryptors {
* text is hex-encoded.
* @param password the password used to generate the encryptor's secret key; should
* not be shared
* @deprecated Use {@link AesCbcBytesEncryptor#withPassword(String, CharSequence)}
* instead.
* @see Encryptors#standard(CharSequence, CharSequence)
*/
@Deprecated
@SuppressWarnings("deprecation")
public static TextEncryptor text(CharSequence password, CharSequence salt) {
return new HexEncodingTextEncryptor(standard(password, salt));
}
@@ -16,8 +16,6 @@
package org.springframework.security.crypto.password;
import java.security.MessageDigest;
import org.jspecify.annotations.Nullable;
import org.springframework.security.crypto.codec.Utf8;
@@ -39,15 +37,7 @@ final class PasswordEncoderUtils {
* @return
*/
static boolean equals(String expected, @Nullable String actual) {
byte[] expectedBytes = bytesUtf8(expected);
byte[] actualBytes = bytesUtf8(actual);
return MessageDigest.isEqual(expectedBytes, actualBytes);
}
private static byte @Nullable [] bytesUtf8(@Nullable String s) {
// need to check if Utf8.encode() runs in constant time (probably not).
// This may leak length of string.
return (s != null) ? Utf8.encode(s) : null;
return Utf8.isEqual(expected, actual);
}
}
@@ -24,6 +24,7 @@ import com.password4j.BalloonHashingFunction;
import com.password4j.Hash;
import com.password4j.Password;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.security.crypto.password.AbstractValidatingPasswordEncoder;
import org.springframework.util.Assert;
@@ -140,7 +141,7 @@ public class BalloonHashingPassword4jPasswordEncoder extends AbstractValidatingP
String expectedHash = parts[1];
Hash hash = Password.hash(rawPassword).addSalt(salt).with(this.balloonHashingFunction);
return expectedHash.equals(hash.getResult());
return Utf8.isEqual(expectedHash, hash.getResult());
}
catch (IllegalArgumentException ex) {
// Invalid Base64 encoding
@@ -24,6 +24,7 @@ import com.password4j.Hash;
import com.password4j.PBKDF2Function;
import com.password4j.Password;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.security.crypto.password.AbstractValidatingPasswordEncoder;
import org.springframework.util.Assert;
@@ -138,7 +139,7 @@ public class Pbkdf2Password4jPasswordEncoder extends AbstractValidatingPasswordE
String expectedHash = parts[1];
Hash hash = Password.hash(rawPassword).addSalt(salt).with(this.pbkdf2Function);
return expectedHash.equals(hash.getResult());
return Utf8.isEqual(expectedHash, hash.getResult());
}
catch (IllegalArgumentException ex) {
// Invalid Base64 encoding
@@ -37,4 +37,48 @@ public class Utf8Tests {
assertThat(decoded).isEqualTo("6048b75ed560785c");
}
@Test
public void isEqualWhenDifferentLengthThenFalse() {
assertThat(Utf8.isEqual("abc", "a")).isFalse();
assertThat(Utf8.isEqual("a", "abc")).isFalse();
}
@Test
public void isEqualWhenNullAndNotEmptyThenFalse() {
assertThat(Utf8.isEqual(null, "a")).isFalse();
assertThat(Utf8.isEqual("a", null)).isFalse();
}
@Test
public void isEqualWhenNullAndNullThenTrue() {
assertThat(Utf8.isEqual(null, null)).isTrue();
}
@Test
public void isEqualWhenNullAndEmptyThenFalse() {
assertThat(Utf8.isEqual(null, "")).isFalse();
assertThat(Utf8.isEqual("", null)).isFalse();
}
@Test
public void isEqualWhenNotEmptyAndEmptyThenFalse() {
assertThat(Utf8.isEqual("abc", "")).isFalse();
assertThat(Utf8.isEqual("", "abc")).isFalse();
}
@Test
public void isEqualWhenEmptyAndEmptyThenTrue() {
assertThat(Utf8.isEqual("", "")).isTrue();
}
@Test
public void isEqualWhenDifferentCaseThenFalse() {
assertThat(Utf8.isEqual("aBc", "abc")).isFalse();
}
@Test
public void isEqualWhenSameThenTrue() {
assertThat(Utf8.isEqual("abcdef", "abcdef")).isTrue();
}
}
@@ -0,0 +1,203 @@
/*
* 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.crypto.encrypt;
import java.nio.charset.StandardCharsets;
import javax.crypto.SecretKey;
import javax.crypto.spec.PBEKeySpec;
import org.junit.jupiter.api.Test;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.security.crypto.keygen.KeyGenerators;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm;
import org.springframework.security.crypto.util.EncodingUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link AesCbcBytesEncryptor}.
*/
class AesCbcBytesEncryptorTests {
private final String secret = "value";
private final String password = "password";
private final String hexSalt = "deadbeef";
@Test
void roundtripWhenUsingPasswordAndSaltThenEncryptsAndDecrypts() {
CryptoAssumptions.assumeCBCJCE();
AesCbcBytesEncryptor encryptor = AesCbcBytesEncryptor.withPassword(this.password, this.hexSalt).build();
byte[] encrypted = encryptor.encrypt(this.secret.getBytes());
assertThat(new String(encryptor.decrypt(encrypted))).isEqualTo(this.secret);
}
@Test
void roundtripWhenUsingSecretKeyThenEncryptsAndDecrypts() {
CryptoAssumptions.assumeCBCJCE();
PBEKeySpec keySpec = new PBEKeySpec(this.password.toCharArray(), Hex.decode(this.hexSalt), 1024, 256);
SecretKey secretKey = CipherUtils.newSecretKey(SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.name(), keySpec);
AesCbcBytesEncryptor encryptor = AesCbcBytesEncryptor.withSecretKey(secretKey).build();
byte[] encrypted = encryptor.encrypt(this.secret.getBytes());
assertThat(new String(encryptor.decrypt(encrypted))).isEqualTo(this.secret);
}
@Test
void encryptWhenUsingMockIvThenProducesKnownCiphertext() {
CryptoAssumptions.assumeCBCJCE();
BytesKeyGenerator mockGenerator = mock(BytesKeyGenerator.class);
given(mockGenerator.generateKey()).willReturn(Hex.decode("4b0febebd439db7ca77153cb254520c3"));
given(mockGenerator.getKeyLength()).willReturn(16);
AesCbcBytesEncryptor encryptor = AesCbcBytesEncryptor.withPassword(this.password, this.hexSalt)
.ivGenerator(mockGenerator)
.build();
byte[] encrypted = encryptor.encrypt(this.secret.getBytes());
assertThat(Hex.encode(encrypted)).isEqualTo("4b0febebd439db7ca77153cb254520c3b7232ac29355d07869433f1ecf55fe94");
assertThat(new String(encryptor.decrypt(encrypted))).isEqualTo(this.secret);
}
@Test
void encryptProducesUniqueOutputAndIvIsPrePended() {
CryptoAssumptions.assumeCBCJCE();
AesCbcBytesEncryptor encryptor = AesCbcBytesEncryptor.withPassword(this.password, this.hexSalt).build();
byte[] first = encryptor.encrypt(this.secret.getBytes());
byte[] second = encryptor.encrypt(this.secret.getBytes());
assertThat(first).isNotEqualTo(second);
assertThat(first.length).isGreaterThan(16);
}
@Test
@SuppressWarnings("deprecation")
void migratesFromDeprecatedNullIvCbcToAesCbcBytesEncryptor() {
CryptoAssumptions.assumeCBCJCE();
AesBytesEncryptor deprecated = new AesBytesEncryptor(this.password, this.hexSalt);
byte[] encrypted = deprecated.encrypt(this.secret.getBytes());
AesCbcBytesEncryptor modern = AesCbcBytesEncryptor.withPassword(this.password, this.hexSalt).build();
BytesEncryptor migrating = new MigratingBytesEncryptor("{CBC}", modern, deprecated);
assertThat(new String(migrating.decrypt(encrypted))).isEqualTo(this.secret);
byte[] migrated = migrating.encrypt(this.secret.getBytes());
assertThat(migrated[0]).isEqualTo((byte) '{');
assertThat(migrated[1]).isEqualTo((byte) 'C');
assertThat(migrated[2]).isEqualTo((byte) 'B');
assertThat(migrated[3]).isEqualTo((byte) 'C');
assertThat(migrated[4]).isEqualTo((byte) '}');
assertThat(new String(migrating.decrypt(migrated))).isEqualTo(this.secret);
}
@Test
@SuppressWarnings("deprecation")
void withSecretWhenAesBytesEncryptorEncryptsThenDecrypts() {
CryptoAssumptions.assumeCBCJCE();
PBEKeySpec keySpec = new PBEKeySpec(this.password.toCharArray(), Hex.decode(this.hexSalt), 1024, 256);
SecretKey key = CipherUtils.newSecretKey(SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.name(), keySpec);
AesBytesEncryptor deprecated = new AesBytesEncryptor(key, KeyGenerators.secureRandom(16),
AesBytesEncryptor.CipherAlgorithm.CBC);
AesCbcBytesEncryptor encryptor = AesCbcBytesEncryptor.withSecretKey(key).build();
byte[] encrypted = deprecated.encrypt(this.secret.getBytes());
assertThat(new String(encryptor.decrypt(encrypted))).isEqualTo(this.secret);
}
@Test
@SuppressWarnings("deprecation")
void aesBytesEncryptorWhenEncryptsThenAesCbcBytesEncryptorDecrypts() {
CryptoAssumptions.assumeCBCJCE();
PBEKeySpec keySpec = new PBEKeySpec(this.password.toCharArray(), Hex.decode(this.hexSalt), 1024, 256);
SecretKey key = CipherUtils.newSecretKey(SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.name(), keySpec);
AesCbcBytesEncryptor encryptor = AesCbcBytesEncryptor.withSecretKey(key).build();
AesBytesEncryptor deprecated = new AesBytesEncryptor(key, KeyGenerators.secureRandom(16),
AesBytesEncryptor.CipherAlgorithm.CBC);
byte[] encrypted = encryptor.encrypt(this.secret.getBytes());
assertThat(new String(deprecated.decrypt(encrypted))).isEqualTo(this.secret);
}
@Test
void roundtripWhenUsingCustomIvGeneratorThenEncryptsAndDecrypts() {
CryptoAssumptions.assumeCBCJCE();
BytesKeyGenerator customIvGenerator = mock(BytesKeyGenerator.class);
given(customIvGenerator.generateKey()).willReturn(Hex.decode("4b0febebd439db7ca77153cb254520c3"));
given(customIvGenerator.getKeyLength()).willReturn(16);
AesCbcBytesEncryptor encryptor = AesCbcBytesEncryptor.withPassword(this.password, this.hexSalt)
.ivGenerator(customIvGenerator)
.build();
byte[] encrypted = encryptor.encrypt(this.secret.getBytes());
assertThat(new String(encryptor.decrypt(encrypted))).isEqualTo(this.secret);
}
@Test
@SuppressWarnings("deprecation")
void withPasswordDerivesADifferentKeyThanAesBytesEncryptor() {
CryptoAssumptions.assumeCBCJCE();
AesBytesEncryptor deprecated = new AesBytesEncryptor(this.password, this.hexSalt,
KeyGenerators.secureRandom(16));
AesCbcBytesEncryptor encryptor = AesCbcBytesEncryptor.withPassword(this.password, this.hexSalt).build();
byte[] encrypted = deprecated.encrypt(this.secret.getBytes());
assertThatIllegalStateException().isThrownBy(() -> encryptor.decrypt(encrypted));
}
private static final class MigratingBytesEncryptor implements BytesEncryptor {
private final byte[] prefix;
private final BytesEncryptor write;
private final BytesEncryptor deprecated;
MigratingBytesEncryptor(String prefix, BytesEncryptor write, BytesEncryptor deprecated) {
this.prefix = prefix.getBytes(StandardCharsets.US_ASCII);
this.write = write;
this.deprecated = deprecated;
}
@Override
public byte[] encrypt(byte[] bytes) {
return EncodingUtils.concatenate(this.prefix, this.write.encrypt(bytes));
}
@Override
public byte[] decrypt(byte[] encryptedBytes) {
if (startsWith(encryptedBytes, this.prefix)) {
byte[] bytes = EncodingUtils.subArray(encryptedBytes, this.prefix.length, encryptedBytes.length);
return this.write.decrypt(bytes);
}
return this.deprecated.decrypt(encryptedBytes);
}
private static boolean startsWith(byte[] data, byte[] prefix) {
if (data.length < prefix.length) {
return false;
}
for (int i = 0; i < prefix.length; i++) {
if (data[i] != prefix[i]) {
return false;
}
}
return true;
}
}
}
@@ -0,0 +1,146 @@
/*
* 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.crypto.encrypt;
import javax.crypto.SecretKey;
import javax.crypto.spec.PBEKeySpec;
import org.junit.jupiter.api.Test;
import org.springframework.security.crypto.codec.Hex;
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
import org.springframework.security.crypto.keygen.KeyGenerators;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder.SecretKeyFactoryAlgorithm;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link AesGcmBytesEncryptor}.
*/
class AesGcmBytesEncryptorTests {
private final String secret = "value";
private final String password = "password";
private final String hexSalt = "deadbeef";
@Test
void roundtripWhenUsingPasswordAndSaltThenEncryptsAndDecrypts() {
CryptoAssumptions.assumeGCMJCE();
AesGcmBytesEncryptor encryptor = AesGcmBytesEncryptor.withPassword(this.password, this.hexSalt).build();
byte[] encrypted = encryptor.encrypt(this.secret.getBytes());
assertThat(new String(encryptor.decrypt(encrypted))).isEqualTo(this.secret);
}
@Test
void roundtripWhenUsingSecretKeyThenEncryptsAndDecrypts() {
CryptoAssumptions.assumeGCMJCE();
PBEKeySpec keySpec = new PBEKeySpec(this.password.toCharArray(), Hex.decode(this.hexSalt), 1024, 256);
SecretKey secretKey = CipherUtils.newSecretKey(SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.name(), keySpec);
AesGcmBytesEncryptor encryptor = AesGcmBytesEncryptor.withSecretKey(secretKey).build();
byte[] encrypted = encryptor.encrypt(this.secret.getBytes());
assertThat(new String(encryptor.decrypt(encrypted))).isEqualTo(this.secret);
}
@Test
void encryptWhenUsingMockIvThenProducesKnownCiphertext() {
CryptoAssumptions.assumeGCMJCE();
BytesKeyGenerator mockGenerator = mock(BytesKeyGenerator.class);
given(mockGenerator.generateKey()).willReturn(Hex.decode("4b0febebd439db7ca77153cb254520c3"));
given(mockGenerator.getKeyLength()).willReturn(16);
AesGcmBytesEncryptor encryptor = AesGcmBytesEncryptor.withPassword(this.password, this.hexSalt)
.ivGenerator(mockGenerator)
.build();
byte[] encrypted = encryptor.encrypt(this.secret.getBytes());
assertThat(Hex.encode(encrypted))
.isEqualTo("4b0febebd439db7ca77153cb254520c3e4d61ae38207b4e42b820d311dc3d4e0e2f37ed5ee");
assertThat(new String(encryptor.decrypt(encrypted))).isEqualTo(this.secret);
}
@Test
void encryptProducesUniqueOutputAndIvIsPrepended() {
CryptoAssumptions.assumeGCMJCE();
AesGcmBytesEncryptor encryptor = AesGcmBytesEncryptor.withPassword(this.password, this.hexSalt).build();
byte[] first = encryptor.encrypt(this.secret.getBytes());
byte[] second = encryptor.encrypt(this.secret.getBytes());
assertThat(first).isNotEqualTo(second);
assertThat(first.length).isGreaterThan(32);
}
@Test
@SuppressWarnings("deprecation")
void withSecretWhenAesBytesEncryptorEncryptsThenDecrypts() {
CryptoAssumptions.assumeGCMJCE();
PBEKeySpec keySpec = new PBEKeySpec(this.password.toCharArray(), Hex.decode(this.hexSalt), 1024, 256);
SecretKey key = CipherUtils.newSecretKey(SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.name(), keySpec);
AesBytesEncryptor deprecated = new AesBytesEncryptor(key, KeyGenerators.secureRandom(16),
AesBytesEncryptor.CipherAlgorithm.GCM);
AesGcmBytesEncryptor encryptor = AesGcmBytesEncryptor.withSecretKey(key).build();
byte[] encrypted = deprecated.encrypt(this.secret.getBytes());
assertThat(new String(encryptor.decrypt(encrypted))).isEqualTo(this.secret);
}
@Test
@SuppressWarnings("deprecation")
void aesBytesEncryptorWhenEncryptsThenAesGcmBytesEncryptorDecrypts() {
CryptoAssumptions.assumeGCMJCE();
PBEKeySpec keySpec = new PBEKeySpec(this.password.toCharArray(), Hex.decode(this.hexSalt), 1024, 256);
SecretKey key = CipherUtils.newSecretKey(SecretKeyFactoryAlgorithm.PBKDF2WithHmacSHA1.name(), keySpec);
AesGcmBytesEncryptor encryptor = AesGcmBytesEncryptor.withSecretKey(key)
.ivGenerator(KeyGenerators.secureRandom(12))
.build();
AesBytesEncryptor deprecated = new AesBytesEncryptor(key, KeyGenerators.secureRandom(12),
AesBytesEncryptor.CipherAlgorithm.GCM);
byte[] encrypted = encryptor.encrypt(this.secret.getBytes());
assertThat(new String(deprecated.decrypt(encrypted))).isEqualTo(this.secret);
}
@Test
void roundtripWhenUsingCustomIvGeneratorLengthThenEncryptsAndDecrypts() {
CryptoAssumptions.assumeGCMJCE();
AesGcmBytesEncryptor encryptor = AesGcmBytesEncryptor.withPassword(this.password, this.hexSalt)
.ivGenerator(KeyGenerators.secureRandom(12))
.build();
byte[] encrypted = encryptor.encrypt(this.secret.getBytes());
assertThat(new String(encryptor.decrypt(encrypted))).isEqualTo(this.secret);
}
@Test
@SuppressWarnings("deprecation")
void withPasswordDerivesADifferentKeyThanAesBytesEncryptor() {
CryptoAssumptions.assumeGCMJCE();
AesBytesEncryptor deprecated = new AesBytesEncryptor(this.password, this.hexSalt,
KeyGenerators.secureRandom(16), AesBytesEncryptor.CipherAlgorithm.GCM);
AesGcmBytesEncryptor encryptor = AesGcmBytesEncryptor.withPassword(this.password, this.hexSalt).build();
byte[] encrypted = deprecated.encrypt(this.secret.getBytes());
assertThatIllegalStateException().isThrownBy(() -> encryptor.decrypt(encrypted));
}
@Test
void decryptDetectsAuthenticationTagTampering() {
CryptoAssumptions.assumeGCMJCE();
AesGcmBytesEncryptor encryptor = AesGcmBytesEncryptor.withPassword(this.password, this.hexSalt).build();
byte[] encrypted = encryptor.encrypt(this.secret.getBytes());
encrypted[17] ^= 0xFF;
assertThatIllegalStateException().isThrownBy(() -> encryptor.decrypt(encrypted));
}
}
@@ -24,25 +24,23 @@ import javax.crypto.NoSuchPaddingException;
import org.junit.jupiter.api.Assumptions;
import org.opentest4j.TestAbortedException;
import org.springframework.security.crypto.encrypt.AesBytesEncryptor.CipherAlgorithm;
public final class CryptoAssumptions {
private CryptoAssumptions() {
}
public static void assumeGCMJCE() {
assumeAes256(CipherAlgorithm.GCM);
assumeAes256("GCM");
}
public static void assumeCBCJCE() {
assumeAes256(CipherAlgorithm.CBC);
assumeAes256("CBC");
}
private static void assumeAes256(CipherAlgorithm cipherAlgorithm) {
private static void assumeAes256(String cipherAlgorithm) {
boolean aes256Available = false;
try {
Cipher.getInstance(cipherAlgorithm.toString());
Cipher.getInstance(cipherAlgorithm);
aes256Available = Cipher.getMaxAllowedKeyLength("AES") >= 256;
}
catch (NoSuchAlgorithmException ex) {
+5 -1
View File
@@ -76,7 +76,11 @@ dependencies {
api libs.org.skyscreamer.jsonassert
api libs.org.slf4j.log4j.over.slf4j
api libs.org.slf4j.slf4j.api
api libs.org.springframework.ldap.spring.ldap.core
if (project.hasProperty("isOverrideVersionCatalog") && project.hasProperty("springLdapVersion")) {
api "org.springframework.ldap:spring-ldap-core:${springLdapVersion}"
} else {
api libs.org.springframework.ldap.spring.ldap.core
}
api libs.org.synchronoss.cloud.nio.multipart.parser
api libs.org.apache.maven.resolver.maven.resolver.connector.basic
api libs.org.apache.maven.resolver.maven.resolver.impl
@@ -23,10 +23,28 @@ When using a proxy server, it is important to ensure that you have configured yo
For example, many applications have a load balancer that responds to request for `\https://example.com/` by forwarding the request to an application server at `\https://192.168.0.107`
Without proper configuration, the application server can not know that the load balancer exists and treats the request as though `\https://192.168.0.107:8080` was requested by the client.
To fix this, you can use https://tools.ietf.org/html/rfc7239[RFC 7239] to specify that a load balancer is being used.
To make the application aware of this, you need to configure your application server to be aware of the X-Forwarded headers.
To fix this, the proxy needs to pass on the details of the original request, and the application needs to be configured to use them.
Two kinds of headers are used for this, and it is important to know which of them applies in your deployment:
* The standard `Forwarded` header, defined by https://tools.ietf.org/html/rfc7239[RFC 7239], which carries the original host, protocol, and client in a single header.
* The non-standard `X-Forwarded-*` headers, such as `X-Forwarded-Host`, `X-Forwarded-Proto`, and `X-Forwarded-For`, which predate RFC 7239.
Most proxies still send the `X-Forwarded-*` headers rather than the standard `Forwarded` header, while Spring Framework and servers such as Reactor Netty and Jetty understand both.
Do not assume that only one of them is in use.
[NOTE]
====
Both kinds of headers are supplied by the client unless a proxy overwrites them, so an application that trusts them without a trusted proxy in front of it can be made to believe a request arrived over a different host, protocol, or client address than it really did.
====
For this reason, the proxy at the edge of your network must be configured to remove or overwrite any forwarded headers that arrive from the outside, for _both_ kinds of headers.
Dropping only the `Forwarded` header while passing through `X-Forwarded-*` (or the reverse) leaves the application open to the same spoofing through the other set.
Only headers added by a proxy you control should reach the application.
Once untrusted values are handled at the edge, the application server can be configured to apply the headers.
For example, Tomcat uses https://tomcat.apache.org/tomcat-10.1-doc/api/org/apache/catalina/valves/RemoteIpValve.html[`RemoteIpValve`] and Jetty uses https://eclipse.dev/jetty/javadoc/jetty-11/org/eclipse/jetty/server/ForwardedRequestCustomizer.html[`ForwardedRequestCustomizer`].
Alternatively, Spring users can use {spring-framework-reference-url}web/webmvc/filters.html#filters-forwarded-headers[`ForwardedHeaderFilter`] with the Servlet stack or {spring-framework-reference-url}web/webflux/reactive-spring.html#webflux-forwarded-headers[`ForwardedHeaderTransformer`] with the Reactive stack.
Both handle the `Forwarded` header and the `X-Forwarded-*` headers, and both can be configured to remove the headers instead of applying them, which is useful when the application is not behind a proxy.
Spring Boot users can use the `server.forward-headers-strategy` property to configure the application.
See the {spring-boot-reference-url}how-to/webserver.html#howto.webserver.use-behind-a-proxy-server[Spring Boot documentation] for further details.
@@ -8,8 +8,8 @@ The code is distributed as part of the core module but has no dependencies on an
[[spring-security-crypto-encryption]]
== Encryptors
The javadoc:org.springframework.security.crypto.encrypt.Encryptors[] class provides factory methods for constructing symmetric encryptors.
This class lets you create javadoc:org.springframework.security.crypto.encrypt.BytesEncryptor[] instances to encrypt data in raw `byte[]` form.
Spring Security provides javadoc:org.springframework.security.crypto.encrypt.AesGcmBytesEncryptor[] and javadoc:org.springframework.security.crypto.encrypt.AesCbcBytesEncryptor[] for constructing symmetric encryptors.
These can be used to encrypt data in raw `byte[]` form.
You can also construct javadoc:org.springframework.security.crypto.encrypt.TextEncryptor[] instances to encrypt text strings.
Encryptors are thread-safe.
@@ -20,7 +20,7 @@ Both `BytesEncryptor` and `TextEncryptor` are interfaces. `BytesEncryptor` has m
[[spring-security-crypto-encryption-bytes]]
=== BytesEncryptor
You can use the `Encryptors.stronger` factory method to construct a `BytesEncryptor`:
Use `AesGcmBytesEncryptor` to construct a BytesEncryptor with authenticated encryption:
.BytesEncryptor
[tabs]
@@ -29,24 +29,22 @@ Java::
+
[source,java,role="primary"]
----
Encryptors.stronger("password", "salt");
AesGcmBytesEncryptor.withPassword("password", "salt").build();
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
Encryptors.stronger("password", "salt")
AesGcmBytesEncryptor.withPassword("password", "salt").build()
----
======
The `stronger` encryption method creates an encryptor by using 256-bit AES encryption with
Galois Counter Mode (GCM).
It derives the secret key by using PKCS #5's PBKDF2 (Password-Based Key Derivation Function #2).
This method requires Java 6.
The password used to generate the `SecretKey` should be kept in a secure place and should not be shared.
The salt is used to prevent dictionary attacks against the key in the event that your encrypted data is compromised.
A 16-byte random initialization vector is also applied so that each encrypted message is unique.
`AesGcmBytesEncryptor` uses 256-bit AES encryption with Galois Counter Mode (GCM), providing https://en.wikipedia.org/wiki/Authenticated_encryption[authenticated encryption] (AEAD).
It derives the secret key using PKCS #5's PBKDF2 (Password-Based Key Derivation Function #2).
The password used to generate the SecretKey should be kept in a secure place and not be shared.
The salt is used to prevent dictionary attacks against the key in the event your encrypted data is compromised.
A 16-byte random initialization vector is also applied so each encrypted message is unique.
The provided salt should be in hex-encoded String form, be random, and be at least 8 bytes in length.
You can generate such a salt by using a `KeyGenerator`:
@@ -69,14 +67,33 @@ val salt = KeyGenerators.string().generateKey() // generates a random 8-byte sal
----
======
You can also use the `standard` encryption method, which is 256-bit AES in Cipher Block Chaining (CBC) Mode.
Users who require CBC mode may use `AesCbcBytesEncryptor`:
.AesCbcBytesEncryptor
[tabs]
======
Java::
+
[source,java,role="primary"]
----
AesCbcBytesEncryptor.withPassword("password", "salt").build();
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
AesCbcBytesEncryptor.withPassword("password", "salt").build()
----
======
This mode is not https://en.wikipedia.org/wiki/Authenticated_encryption[authenticated] and does not provide any
guarantees about the authenticity of the data.
For a more secure alternative, use `Encryptors.stronger`.
For a more secure alternative, users should prefer `AesGcmBytesEncryptor`.
[[spring-security-crypto-encryption-text]]
=== TextEncryptor
You can use the `Encryptors.text` factory method to construct a standard TextEncryptor:
Use `AesCbcBytesEncryptor` to encrypt text data:
.TextEncryptor
[tabs]
@@ -85,19 +102,22 @@ Java::
+
[source,java,role="primary"]
----
Encryptors.text("password", "salt");
AesCbcBytesEncryptor.withPassword("password", "salt").build();
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
Encryptors.text("password", "salt")
AesCbcBytesEncryptor.withPassword("password", "salt").build()
----
======
A `TextEncryptor` uses a standard `BytesEncryptor` to encrypt text data.
Encrypted results are returned as hex-encoded strings for easy storage on the filesystem or in a database.
`AesCbcBytesEncryptor` encrypts data as raw bytes.
You can hex-encode the result for easy storage on the filesystem or in the database.
NOTE: Queryable text encryption (encrypting such that the same plaintext always produces the same ciphertext) is no longer recommended, as it relies on a fixed initialization vector and does not provide adequate security.
Instead, look to your data store for a mechanism to query encrypted data.
[[spring-security-crypto-keygenerators]]
== Key Generators
@@ -132,4 +132,11 @@ authenticationConverter.setBearerTokenResolver(myBearerTokenResolver)
authenticationConverter.setAuthenticationDetailsSource(myAuthenticationDetailsSource)
val filter = BearerTokenAuthenticationFilter(authenticationManager, authenticationConverter)
----
== `NimbusJwtDecoder`'s Default Connect and Read Timeouts Are Now 30 Seconds
`NimbusJwtDecoder`'s default `RestOperations`, used to fetch a JWK Set when no `RestOperations` is otherwise configured, previously used a 500 millisecond connect and read timeout.
This value was too short for many deployments and is now 30 seconds, aligning with the connect and read timeouts already used elsewhere in the OAuth2 Client and Resource Server support, such as xref:servlet/oauth2/client/index.adoc[`ClientRegistrations`].
If your application relies on the previous, shorter timeout -- for example, expecting a fast failure when the authorization server is unreachable -- you can restore it either by setting the JDK's `sun.net.client.defaultConnectTimeout` and `sun.net.client.defaultReadTimeout` system properties (in milliseconds), or by providing your own `RestOperations`, as described in xref:servlet/oauth2/resource-server/jwt.adoc#oauth2resourceserver-jwt-timeouts[Configuring Timeouts].
======
@@ -695,7 +695,7 @@ class MyAuthoritiesPopulator : LdapAuthoritiesPopulator {
You would then add a bean of this type to your application context and inject it into the `LdapAuthenticationProvider`. This is covered in the section on configuring LDAP by using explicit Spring beans in the LDAP chapter of the reference manual.
Note that you cannot use the namespace for configuration in this case.
You should also consult the {security-api-url}[Javadoc] for the relevant classes and interfaces.
You should also consult the xref:attachment$api/java/index.html[Javadoc] for the relevant classes and interfaces.
[[appendix-faq-namespace-post-processor]]
@@ -42,7 +42,7 @@ In the following sections we will explore how to configure OTT Login for your ne
[[default-pages]]
== Default Login Page and Default One-Time Token Submit Page
When the `oneTimeTokenLogin()` DSL is used, by default the One-Time Token Login Page is auto-generated by the org.springframework.security.web.authentication.ui:DefaultLoginPageGeneratingFilter[].
When the `oneTimeTokenLogin()` DSL is used, by default the One-Time Token Login Page is auto-generated by the javadoc:org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter[].
The DSL will also set up the javadoc:org.springframework.security.web.authentication.ui.DefaultOneTimeTokenSubmitPageGeneratingFilter[] to generate a default One-Time Token submit page.
[[sending-token-to-user]]
@@ -89,7 +89,7 @@ public class MagicLinkOneTimeTokenGenerationSuccessHandler implements OneTimeTok
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, OneTimeToken oneTimeToken) throws IOException, ServletException {
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(request.getRequestURL().toString())
.replacePath(request.getContextPath())
.replaceQuery(null)
.fragment(null)
@@ -147,7 +147,7 @@ class MagicLinkOneTimeTokenGenerationSuccessHandler(
) : OneTimeTokenGenerationSuccessHandler {
override fun handle(request: HttpServletRequest, response: HttpServletResponse, oneTimeToken: OneTimeToken) {
val builder = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
val builder = UriComponentsBuilder.fromUriString(request.getRequestURL().toString())
.replacePath(request.contextPath)
.replaceQuery(null)
.fragment(null)
@@ -211,7 +211,7 @@ public class SecurityConfig {
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.generateTokenUrl("/ott/my-generate-url")
.tokenGeneratingUrl("/ott/my-generate-url")
);
return http.build();
}
@@ -238,7 +238,7 @@ class SecurityConfig {
//...
formLogin { }
oneTimeTokenLogin {
generateTokenUrl = "/ott/my-generate-url"
tokenGeneratingUrl = "/ott/my-generate-url"
}
}
return http.build()
@@ -282,7 +282,7 @@ public class SecurityConfig {
// ...
.formLogin(Customizer.withDefaults())
.oneTimeTokenLogin((ott) -> ott
.submitPageUrl("/ott/submit")
.defaultSubmitPageUrl("/ott/submit")
);
return http.build();
}
@@ -309,7 +309,7 @@ class SecurityConfig {
//...
formLogin { }
oneTimeTokenLogin {
submitPageUrl = "/ott/submit"
defaultSubmitPageUrl = "/ott/submit"
}
}
return http.build()
@@ -150,7 +150,7 @@ This means that we are running our `Runnable` with the same user that was used t
== Spring Security Concurrency Classes
See the {security-api-url}index.html[Javadoc] for additional integrations with both the Java concurrent APIs and the Spring Task abstractions.
See the xref:attachment$api/java/index.html[Javadoc] for additional integrations with both the Java concurrent APIs and the Spring Task abstractions.
They are self-explanatory once you understand the previous code.
* javadoc:org.springframework.security.concurrent.DelegatingSecurityContextCallable[]
@@ -178,13 +178,13 @@ An `OAuth2Authorization` is a representation of an OAuth2 authorization, which h
[TIP]
The corresponding authorization model in Spring Security's OAuth2 Client support is xref:servlet/oauth2/client/core.adoc#oauth2Client-authorized-client[OAuth2AuthorizedClient].
After the successful completion of an authorization grant flow, an `OAuth2Authorization` is created and associates an {security-api-url}/org/springframework/security/oauth2/core/OAuth2AccessToken.html[`OAuth2AccessToken`], an (optional) {security-api-url}/org/springframework/security/oauth2/core/OAuth2RefreshToken.html[`OAuth2RefreshToken`], and additional state specific to the executed authorization grant type.
After the successful completion of an authorization grant flow, an `OAuth2Authorization` is created and associates an javadoc:org.springframework.security.oauth2.core.OAuth2AccessToken[], an (optional) javadoc:org.springframework.security.oauth2.core.OAuth2RefreshToken[], and additional state specific to the executed authorization grant type.
The {security-api-url}/org/springframework/security/oauth2/core/OAuth2Token.html[`OAuth2Token`] instances associated with an `OAuth2Authorization` vary, depending on the authorization grant type.
The javadoc:org.springframework.security.oauth2.core.OAuth2Token[] instances associated with an `OAuth2Authorization` vary, depending on the authorization grant type.
For the OAuth2 https://datatracker.ietf.org/doc/html/rfc6749#section-4.1[authorization_code grant], an `OAuth2AuthorizationCode`, an `OAuth2AccessToken`, and an (optional) `OAuth2RefreshToken` are associated.
For the OpenID Connect 1.0 https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth[authorization_code grant], an `OAuth2AuthorizationCode`, an {security-api-url}/org/springframework/security/oauth2/core/oidc/OidcIdToken.html[`OidcIdToken`], an `OAuth2AccessToken`, and an (optional) `OAuth2RefreshToken` are associated.
For the OpenID Connect 1.0 https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth[authorization_code grant], an `OAuth2AuthorizationCode`, an javadoc:org.springframework.security.oauth2.core.oidc.OidcIdToken[], an `OAuth2AccessToken`, and an (optional) `OAuth2RefreshToken` are associated.
For the OAuth2 https://datatracker.ietf.org/doc/html/rfc6749#section-4.4[client_credentials grant], only an `OAuth2AccessToken` is associated.
@@ -277,9 +277,9 @@ This is a minimal configuration for getting started quickly. To understand what
<1> A Spring Security filter chain for the xref:servlet/oauth2/authorization-server/protocol-endpoints.adoc[Protocol Endpoints].
<2> A Spring Security filter chain for xref:servlet/authentication/index.adoc#servlet-authentication[authentication].
<3> An instance of {security-api-url}/org/springframework/security/core/userdetails/UserDetailsService.html[`UserDetailsService`] for retrieving users to authenticate.
<3> An instance of javadoc:org.springframework.security.core.userdetails.UserDetailsService[] for retrieving users to authenticate.
<4> An instance of xref:servlet/oauth2/authorization-server/core-model-components.adoc#oauth2AuthorizationServer-registered-client-repository[`RegisteredClientRepository`] for managing clients.
<5> An instance of `com.nimbusds.jose.jwk.source.JWKSource` for signing access tokens.
<6> An instance of `java.security.KeyPair` with keys generated on startup used to create the `JWKSource` above.
<7> An instance of {security-api-url}/org/springframework/security/oauth2/jwt/JwtDecoder.html[`JwtDecoder`] for decoding signed access tokens.
<7> An instance of javadoc:org.springframework.security.oauth2.jwt.JwtDecoder[] for decoding signed access tokens.
<8> An instance of xref:servlet/oauth2/authorization-server/configuration-model.adoc#oauth2AuthorizationServer-configuring-authorization-server-settings[`AuthorizationServerSettings`] to configure Spring Security Authorization Server.
@@ -944,5 +944,52 @@ For MAC-based algorithms (such as `HS256`, `HS384`, or `HS512`), the `client-sec
If more than one `ClientRegistration` is configured for OpenID Connect 1.0 Authentication, the JWS algorithm resolver may evaluate the provided `ClientRegistration` to determine which algorithm to return.
====
[[oauth2login-advanced-idtoken-decoder-factory]]
== Providing a Custom JwtDecoderFactory
`OidcIdTokenDecoderFactory` is deliberately narrow: it exists to make it easy to support multiple `ClientRegistration` instances, each potentially needing a different `JwtDecoder`, and its configuration surface is limited to what most applications need for that purpose, such as the JWS algorithm resolver shown above.
If your application needs deeper control over how the ID Token's `JwtDecoder` is constructed -- for example, providing your own `RestOperations` -- you can instead provide your own `JwtDecoderFactory<ClientRegistration>` `@Bean`.
Because OAuth2 Login does not use a resource server `JwtDecoder` bean for this purpose, this is the supported way to reach the same level of control that `NimbusJwtDecoder`'s builder already offers, without introducing additional configuration properties on `OidcIdTokenDecoderFactory` itself.
For a single `ClientRegistration`, this can be as simple as:
[tabs]
======
Java::
+
[source,java,role="primary"]
----
@Bean
JwtDecoderFactory<ClientRegistration> idTokenDecoderFactory(RestOperations rest) {
return (clientRegistration) -> {
String issuerUri = clientRegistration.getProviderDetails().getIssuerUri();
NimbusJwtDecoder decoder = NimbusJwtDecoder.withIssuerLocation(issuerUri).restOperations(rest).build();
decoder.setJwtValidator(JwtValidators.createDefaultWithValidators(new OidcIdTokenValidator(clientRegistration)));
decoder.setClaimTypeConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverter());
return decoder;
};
}
----
Kotlin::
+
[source,kotlin,role="secondary"]
----
@Bean
fun idTokenDecoderFactory(rest: RestOperations): JwtDecoderFactory<ClientRegistration> {
return JwtDecoderFactory { clientRegistration ->
val issuerUri = clientRegistration.providerDetails.issuerUri
val decoder = NimbusJwtDecoder.withIssuerLocation(issuerUri).restOperations(rest).build()
decoder.setJwtValidator(JwtValidators.createDefaultWithValidators(OidcIdTokenValidator(clientRegistration)))
decoder.setClaimTypeConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverter())
decoder
}
}
----
======
If you have multiple `ClientRegistration` instances and want to avoid rebuilding a `JwtDecoder` on every request, cache the decoder per registration, for example by annotating the factory method with `@Cacheable` or by keying a `Map` on `ClientRegistration#getRegistrationId()`.
[[oauth2login-advanced-oidc-logout]]
Then, you can proceed to configure xref:servlet/oauth2/login/logout.adoc[logout]
@@ -1622,6 +1622,7 @@ fun jwtDecoder(): JwtDecoder {
== Configuring Timeouts
By default, Resource Server uses connection and socket timeouts of 30 seconds each for coordinating with the authorization server.
You can override these defaults without changing any code by setting the JDK's `sun.net.client.defaultConnectTimeout` and `sun.net.client.defaultReadTimeout` system properties (in milliseconds).
This may be too short in some scenarios.
Further, it doesn't take into account more sophisticated patterns like back-off and discovery.
+2 -2
View File
@@ -1,10 +1,10 @@
{
"dependencies": {
"antora": "3.2.0-alpha.12",
"antora": "3.2.0-rc.2",
"@antora/atlas-extension": "1.0.0-alpha.5",
"@antora/collector-extension": "1.0.3",
"@asciidoctor/tabs": "1.0.0-beta.6",
"@springio/antora-extensions": "1.14.12",
"@springio/antora-extensions": "1.14.13",
"@springio/asciidoctor-extensions": "1.0.0-alpha.18"
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2026-present the original author or authors.
* 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2026-present the original author or authors.
* 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2026-present the original author or authors.
* 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2026-present the original author or authors.
* 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2026-present the original author or authors.
* 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2026-present the original author or authors.
* 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2026-present the original author or authors.
* 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2026-present the original author or authors.
* 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.
@@ -1,5 +1,5 @@
/*
* Copyright 2026-present the original author or authors.
* 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.
+1 -1
View File
@@ -14,7 +14,7 @@
# limitations under the License.
#
springBootVersion=4.1.0-SNAPSHOT
version=7.1.0
version=7.1.1
samplesBranch=main
org.gradle.jvmargs=-Xmx3g -XX:+HeapDumpOnOutOfMemoryError
org.gradle.parallel=true
+20 -20
View File
@@ -1,39 +1,39 @@
[versions]
com-squareup-okhttp3 = "3.14.9"
io-micrometer = "1.17.0"
io-micrometer = "1.17.1"
io-rsocket = "1.1.5"
io-spring-javaformat = "0.0.47"
io-spring-nohttp = "0.0.11"
jakarta-websocket = "2.2.0"
org-apache-maven-resolver = "1.9.27"
org-aspectj = "1.9.25.1"
org-bouncycastle = "1.84"
org-bouncycastle = "1.85"
org-eclipse-jetty = "11.0.26"
org-jetbrains-dokka = "2.2.0"
org-jetbrains-kotlin = "2.4.0"
org-jetbrains-kotlin = "2.4.10"
org-jetbrains-kotlinx = "1.11.0"
org-mockito = "5.23.0"
org-opensaml5 = "5.2.2"
org-springframework = "7.0.8"
org-opensaml5 = "5.2.3"
org-springframework = "7.0.9"
com-password4j = "1.8.4"
[libraries]
ch-qos-logback-logback-classic = "ch.qos.logback:logback-classic:1.5.34"
com-fasterxml-jackson-jackson-bom = "com.fasterxml.jackson:jackson-bom:2.22.0"
ch-qos-logback-logback-classic = "ch.qos.logback:logback-classic:1.6.1"
com-fasterxml-jackson-jackson-bom = "com.fasterxml.jackson:jackson-bom:2.22.1"
com-google-inject-guice = "com.google.inject:guice:3.0"
com-netflix-nebula-nebula-project-plugin = "com.netflix.nebula:nebula-project-plugin:8.2.0"
com-nimbusds-nimbus-jose-jwt = "com.nimbusds:nimbus-jose-jwt:10.9"
com-nimbusds-oauth2-oidc-sdk = "com.nimbusds:oauth2-oidc-sdk:11.37.2"
com-nimbusds-nimbus-jose-jwt = "com.nimbusds:nimbus-jose-jwt:10.9.1"
com-nimbusds-oauth2-oidc-sdk = "com.nimbusds:oauth2-oidc-sdk:11.38.2"
com-squareup-okhttp3-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "com-squareup-okhttp3" }
com-squareup-okhttp3-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "com-squareup-okhttp3" }
com-unboundid-unboundid-ldapsdk = "com.unboundid:unboundid-ldapsdk:7.0.4"
com-unboundid-unboundid-ldapsdk = "com.unboundid:unboundid-ldapsdk:7.0.5"
com-jayway-jsonpath-json-path = "com.jayway.jsonpath:json-path:2.10.0"
commons-collections = "commons-collections:commons-collections:3.2.2"
io-micrometer-context-propagation = "io.micrometer:context-propagation:1.2.1"
io-micrometer-micrometer-bom = { module = "io.micrometer:micrometer-bom", version.ref = "io-micrometer" }
io-micrometer-micrometer-observation = { module = "io.micrometer:micrometer-observation", version.ref = "io-micrometer" }
io-mockk = "io.mockk:mockk:1.14.11"
io-projectreactor-reactor-bom = "io.projectreactor:reactor-bom:2025.0.6"
io-projectreactor-reactor-bom = "io.projectreactor:reactor-bom:2025.0.7"
io-rsocket-rsocket-bom = { module = "io.rsocket:rsocket-bom", version.ref = "io-rsocket" }
io-spring-javaformat-spring-javaformat-checkstyle = { module = "io.spring.javaformat:spring-javaformat-checkstyle", version.ref = "io-spring-javaformat" }
io-spring-javaformat-spring-javaformat-gradle-plugin = { module = "io.spring.javaformat:spring-javaformat-gradle-plugin", version.ref = "io-spring-javaformat" }
@@ -52,8 +52,8 @@ jakarta-xml-bind-jakarta-xml-bind-api = "jakarta.xml.bind:jakarta.xml.bind-api:4
ldapsdk = "ldapsdk:ldapsdk:4.1"
net-sourceforge-htmlunit = "net.sourceforge.htmlunit:htmlunit:2.70.0"
org-htmlunit-htmlunit = "org.htmlunit:htmlunit:4.21.0"
org-apache-httpcomponents-httpclient = "org.apache.httpcomponents.client5:httpclient5:5.6.1"
org-apache-kerby-simplekdc='org.apache.kerby:kerb-simplekdc:2.1.1'
org-apache-httpcomponents-httpclient = "org.apache.httpcomponents.client5:httpclient5:5.6.4"
org-apache-kerby-simplekdc='org.apache.kerby:kerb-simplekdc:2.1.2'
org-apache-maven-maven-resolver-provider = "org.apache.maven:maven-resolver-provider:3.9.16"
org-apache-maven-resolver-maven-resolver-connector-basic = { module = "org.apache.maven.resolver:maven-resolver-connector-basic", version.ref = "org-apache-maven-resolver" }
org-apache-maven-resolver-maven-resolver-impl = { module = "org.apache.maven.resolver:maven-resolver-impl", version.ref = "org-apache-maven-resolver" }
@@ -68,13 +68,13 @@ org-bouncycastle-bcprov-jdk15on = { module = "org.bouncycastle:bcprov-jdk18on",
org-eclipse-jetty-jetty-server = { module = "org.eclipse.jetty:jetty-server", version.ref = "org-eclipse-jetty" }
org-eclipse-jetty-jetty-servlet = { module = "org.eclipse.jetty:jetty-servlet", version.ref = "org-eclipse-jetty" }
org-hamcrest = "org.hamcrest:hamcrest:2.2"
org-hibernate-orm-hibernate-core = "org.hibernate.orm:hibernate-core:7.4.0.Final"
org-hibernate-orm-hibernate-core = "org.hibernate.orm:hibernate-core:7.4.5.Final"
org-hsqldb = "org.hsqldb:hsqldb:2.7.4"
org-jetbrains-dokka-dokka-gradle-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "org-jetbrains-dokka" }
org-jetbrains-kotlin-kotlin-bom = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "org-jetbrains-kotlin" }
org-jetbrains-kotlin-kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "org-jetbrains-kotlin" }
org-jetbrains-kotlinx-kotlinx-coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version.ref = "org-jetbrains-kotlinx" }
org-junit-junit-bom = "org.junit:junit-bom:6.1.0"
org-junit-junit-bom = "org.junit:junit-bom:6.1.3"
org-mockito-mockito-bom = { module = "org.mockito:mockito-bom", version.ref = "org-mockito" }
org-opensaml-opensaml5-saml-api = { module = "org.opensaml:opensaml-saml-api", version.ref = "org-opensaml5" }
org-opensaml-opensaml5-saml-impl = { module = "org.opensaml:opensaml-saml-impl", version.ref = "org-opensaml5" }
@@ -85,11 +85,11 @@ org-seleniumhq-selenium-selenium-support = "org.seleniumhq.selenium:selenium-sup
org-skyscreamer-jsonassert = "org.skyscreamer:jsonassert:1.5.3"
org-slf4j-log4j-over-slf4j = "org.slf4j:log4j-over-slf4j:1.7.36"
org-slf4j-slf4j-api = "org.slf4j:slf4j-api:2.0.18"
org-springframework-data-spring-data-bom = "org.springframework.data:spring-data-bom:2026.0.0"
org-springframework-ldap-spring-ldap-core = "org.springframework.ldap:spring-ldap-core:4.1.0"
org-springframework-data-spring-data-bom = "org.springframework.data:spring-data-bom:2026.0.1"
org-springframework-ldap-spring-ldap-core = "org.springframework.ldap:spring-ldap-core:4.1.1"
org-springframework-spring-framework-bom = { module = "org.springframework:spring-framework-bom", version.ref = "org-springframework" }
org-synchronoss-cloud-nio-multipart-parser = "org.synchronoss.cloud:nio-multipart-parser:1.1.0"
tools-jackson-jackson-bom = "tools.jackson:jackson-bom:3.2.0"
tools-jackson-jackson-bom = "tools.jackson:jackson-bom:3.2.1"
com-google-code-gson-gson = "com.google.code.gson:gson:2.14.0"
com-thaiopensource-trag = "com.thaiopensource:trang:20091111"
@@ -105,8 +105,8 @@ org-jfrog-buildinfo-build-info-extractor-gradle = "org.jfrog.buildinfo:build-inf
org-sonarsource-scanner-gradle-sonarqube-gradle-plugin = "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.8.0.1969"
org-instancio-instancio-junit = "org.instancio:instancio-junit:3.7.1"
spring-nullability = 'io.spring.nullability:io.spring.nullability.gradle.plugin:0.0.13'
webauthn4j-core = 'com.webauthn4j:webauthn4j-core:0.31.6.RELEASE'
spring-nullability = 'io.spring.nullability:io.spring.nullability.gradle.plugin:0.0.14'
webauthn4j-core = 'com.webauthn4j:webauthn4j-core:0.31.9.RELEASE'
com-password4j-password4j = { module = "com.password4j:password4j", version.ref = "com-password4j" }
[plugins]
Binary file not shown.
+2 -2
View File
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=bafc141b619ad6350fd975fc903156dd5c151998cc8b058e8c1044ab5f7b031f
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
distributionSha256Sum=84fbba45c7f4c64abc77460e1c00f541e9f960e3c7ed2538f1ede19eacd873ae
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.0-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
Vendored
+2 -2
View File
@@ -20,7 +20,7 @@
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
# gradlew start up script for POSIX generated by Gradle.
#
# Important for running:
#
@@ -29,7 +29,7 @@
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
# ksh gradlew
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
Vendored
+2 -2
View File
@@ -19,7 +19,7 @@
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem gradlew startup script for Windows
@rem
@rem ##########################################################################
@@ -72,7 +72,7 @@ echo location of your Java installation. 1>&2
@rem Execute Gradle
@rem Execute gradlew
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
@rem which allows us to clear the local environment before executing the java command
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
+260 -252
View File
@@ -12,7 +12,7 @@
"@eslint/js": "^9.6.0",
"@types/sinon": "^17.0.3",
"chai": "~4.3",
"esbuild": "^0.25.0",
"esbuild": "^0.28.1",
"eslint": "^9.6.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
@@ -27,14 +27,13 @@
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
"integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
"integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
@@ -44,14 +43,13 @@
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz",
"integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
"integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
@@ -61,14 +59,13 @@
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz",
"integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
"integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
@@ -78,14 +75,13 @@
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz",
"integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
"integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
@@ -95,14 +91,13 @@
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz",
"integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
"integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -112,14 +107,13 @@
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz",
"integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
"integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -129,14 +123,13 @@
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz",
"integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
"integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
@@ -146,14 +139,13 @@
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz",
"integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
"integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
@@ -163,14 +155,13 @@
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz",
"integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
"integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -180,14 +171,13 @@
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz",
"integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
"integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -197,14 +187,13 @@
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz",
"integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
"integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -214,14 +203,13 @@
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz",
"integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
"integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -231,14 +219,13 @@
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz",
"integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
"integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -248,14 +235,13 @@
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz",
"integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
"integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -265,14 +251,13 @@
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz",
"integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
"integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -282,14 +267,13 @@
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz",
"integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
"integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -299,14 +283,13 @@
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz",
"integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
"integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
@@ -316,14 +299,13 @@
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz",
"integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
"integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
@@ -333,14 +315,13 @@
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz",
"integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
"integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
@@ -350,14 +331,13 @@
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz",
"integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
"integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
@@ -367,14 +347,13 @@
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz",
"integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
"integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
@@ -383,15 +362,30 @@
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
"integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz",
"integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
"integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
@@ -401,14 +395,13 @@
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz",
"integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
"integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
@@ -418,14 +411,13 @@
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz",
"integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
"integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
@@ -435,14 +427,13 @@
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz",
"integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
"integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
@@ -972,9 +963,9 @@
}
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"version": "1.1.18",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
"integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
"dev": true,
"dependencies": {
"balanced-match": "^1.0.0",
@@ -1233,12 +1224,11 @@
"dev": true
},
"node_modules/esbuild": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz",
"integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
"integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
@@ -1246,31 +1236,32 @@
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.25.0",
"@esbuild/android-arm": "0.25.0",
"@esbuild/android-arm64": "0.25.0",
"@esbuild/android-x64": "0.25.0",
"@esbuild/darwin-arm64": "0.25.0",
"@esbuild/darwin-x64": "0.25.0",
"@esbuild/freebsd-arm64": "0.25.0",
"@esbuild/freebsd-x64": "0.25.0",
"@esbuild/linux-arm": "0.25.0",
"@esbuild/linux-arm64": "0.25.0",
"@esbuild/linux-ia32": "0.25.0",
"@esbuild/linux-loong64": "0.25.0",
"@esbuild/linux-mips64el": "0.25.0",
"@esbuild/linux-ppc64": "0.25.0",
"@esbuild/linux-riscv64": "0.25.0",
"@esbuild/linux-s390x": "0.25.0",
"@esbuild/linux-x64": "0.25.0",
"@esbuild/netbsd-arm64": "0.25.0",
"@esbuild/netbsd-x64": "0.25.0",
"@esbuild/openbsd-arm64": "0.25.0",
"@esbuild/openbsd-x64": "0.25.0",
"@esbuild/sunos-x64": "0.25.0",
"@esbuild/win32-arm64": "0.25.0",
"@esbuild/win32-ia32": "0.25.0",
"@esbuild/win32-x64": "0.25.0"
"@esbuild/aix-ppc64": "0.28.1",
"@esbuild/android-arm": "0.28.1",
"@esbuild/android-arm64": "0.28.1",
"@esbuild/android-x64": "0.28.1",
"@esbuild/darwin-arm64": "0.28.1",
"@esbuild/darwin-x64": "0.28.1",
"@esbuild/freebsd-arm64": "0.28.1",
"@esbuild/freebsd-x64": "0.28.1",
"@esbuild/linux-arm": "0.28.1",
"@esbuild/linux-arm64": "0.28.1",
"@esbuild/linux-ia32": "0.28.1",
"@esbuild/linux-loong64": "0.28.1",
"@esbuild/linux-mips64el": "0.28.1",
"@esbuild/linux-ppc64": "0.28.1",
"@esbuild/linux-riscv64": "0.28.1",
"@esbuild/linux-s390x": "0.28.1",
"@esbuild/linux-x64": "0.28.1",
"@esbuild/netbsd-arm64": "0.28.1",
"@esbuild/netbsd-x64": "0.28.1",
"@esbuild/openbsd-arm64": "0.28.1",
"@esbuild/openbsd-x64": "0.28.1",
"@esbuild/openharmony-arm64": "0.28.1",
"@esbuild/sunos-x64": "0.28.1",
"@esbuild/win32-arm64": "0.28.1",
"@esbuild/win32-ia32": "0.28.1",
"@esbuild/win32-x64": "0.28.1"
}
},
"node_modules/escalade": {
@@ -1944,10 +1935,20 @@
"dev": true
},
"node_modules/js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/nodeca"
}
],
"dependencies": {
"argparse": "^2.0.1"
},
@@ -2217,11 +2218,10 @@
}
},
"node_modules/mocha/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
"integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
"dev": true,
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0"
}
@@ -3272,177 +3272,184 @@
},
"dependencies": {
"@esbuild/aix-ppc64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
"integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
"integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
"dev": true,
"optional": true
},
"@esbuild/android-arm": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz",
"integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
"integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
"dev": true,
"optional": true
},
"@esbuild/android-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz",
"integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
"integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
"dev": true,
"optional": true
},
"@esbuild/android-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz",
"integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
"integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
"dev": true,
"optional": true
},
"@esbuild/darwin-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz",
"integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
"integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
"dev": true,
"optional": true
},
"@esbuild/darwin-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz",
"integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
"integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
"dev": true,
"optional": true
},
"@esbuild/freebsd-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz",
"integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
"integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
"dev": true,
"optional": true
},
"@esbuild/freebsd-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz",
"integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
"integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
"dev": true,
"optional": true
},
"@esbuild/linux-arm": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz",
"integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
"integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
"dev": true,
"optional": true
},
"@esbuild/linux-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz",
"integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
"integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
"dev": true,
"optional": true
},
"@esbuild/linux-ia32": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz",
"integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
"integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
"dev": true,
"optional": true
},
"@esbuild/linux-loong64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz",
"integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
"integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
"dev": true,
"optional": true
},
"@esbuild/linux-mips64el": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz",
"integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
"integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
"dev": true,
"optional": true
},
"@esbuild/linux-ppc64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz",
"integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
"integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
"dev": true,
"optional": true
},
"@esbuild/linux-riscv64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz",
"integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
"integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
"dev": true,
"optional": true
},
"@esbuild/linux-s390x": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz",
"integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
"integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
"dev": true,
"optional": true
},
"@esbuild/linux-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz",
"integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
"integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
"dev": true,
"optional": true
},
"@esbuild/netbsd-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz",
"integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
"integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
"dev": true,
"optional": true
},
"@esbuild/netbsd-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz",
"integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
"integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
"dev": true,
"optional": true
},
"@esbuild/openbsd-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz",
"integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
"integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
"dev": true,
"optional": true
},
"@esbuild/openbsd-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz",
"integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
"integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
"dev": true,
"optional": true
},
"@esbuild/openharmony-arm64": {
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
"integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
"dev": true,
"optional": true
},
"@esbuild/sunos-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz",
"integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
"integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
"dev": true,
"optional": true
},
"@esbuild/win32-arm64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz",
"integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
"integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
"dev": true,
"optional": true
},
"@esbuild/win32-ia32": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz",
"integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
"integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
"dev": true,
"optional": true
},
"@esbuild/win32-x64": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz",
"integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
"integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
"dev": true,
"optional": true
},
@@ -3818,9 +3825,9 @@
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"version": "1.1.18",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
"integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0",
@@ -4019,36 +4026,37 @@
"dev": true
},
"esbuild": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz",
"integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==",
"version": "0.28.1",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
"integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
"dev": true,
"requires": {
"@esbuild/aix-ppc64": "0.25.0",
"@esbuild/android-arm": "0.25.0",
"@esbuild/android-arm64": "0.25.0",
"@esbuild/android-x64": "0.25.0",
"@esbuild/darwin-arm64": "0.25.0",
"@esbuild/darwin-x64": "0.25.0",
"@esbuild/freebsd-arm64": "0.25.0",
"@esbuild/freebsd-x64": "0.25.0",
"@esbuild/linux-arm": "0.25.0",
"@esbuild/linux-arm64": "0.25.0",
"@esbuild/linux-ia32": "0.25.0",
"@esbuild/linux-loong64": "0.25.0",
"@esbuild/linux-mips64el": "0.25.0",
"@esbuild/linux-ppc64": "0.25.0",
"@esbuild/linux-riscv64": "0.25.0",
"@esbuild/linux-s390x": "0.25.0",
"@esbuild/linux-x64": "0.25.0",
"@esbuild/netbsd-arm64": "0.25.0",
"@esbuild/netbsd-x64": "0.25.0",
"@esbuild/openbsd-arm64": "0.25.0",
"@esbuild/openbsd-x64": "0.25.0",
"@esbuild/sunos-x64": "0.25.0",
"@esbuild/win32-arm64": "0.25.0",
"@esbuild/win32-ia32": "0.25.0",
"@esbuild/win32-x64": "0.25.0"
"@esbuild/aix-ppc64": "0.28.1",
"@esbuild/android-arm": "0.28.1",
"@esbuild/android-arm64": "0.28.1",
"@esbuild/android-x64": "0.28.1",
"@esbuild/darwin-arm64": "0.28.1",
"@esbuild/darwin-x64": "0.28.1",
"@esbuild/freebsd-arm64": "0.28.1",
"@esbuild/freebsd-x64": "0.28.1",
"@esbuild/linux-arm": "0.28.1",
"@esbuild/linux-arm64": "0.28.1",
"@esbuild/linux-ia32": "0.28.1",
"@esbuild/linux-loong64": "0.28.1",
"@esbuild/linux-mips64el": "0.28.1",
"@esbuild/linux-ppc64": "0.28.1",
"@esbuild/linux-riscv64": "0.28.1",
"@esbuild/linux-s390x": "0.28.1",
"@esbuild/linux-x64": "0.28.1",
"@esbuild/netbsd-arm64": "0.28.1",
"@esbuild/netbsd-x64": "0.28.1",
"@esbuild/openbsd-arm64": "0.28.1",
"@esbuild/openbsd-x64": "0.28.1",
"@esbuild/openharmony-arm64": "0.28.1",
"@esbuild/sunos-x64": "0.28.1",
"@esbuild/win32-arm64": "0.28.1",
"@esbuild/win32-ia32": "0.28.1",
"@esbuild/win32-x64": "0.28.1"
}
},
"escalade": {
@@ -4523,9 +4531,9 @@
"dev": true
},
"js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
"integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
"dev": true,
"requires": {
"argparse": "^2.0.1"
@@ -4738,9 +4746,9 @@
},
"dependencies": {
"brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz",
"integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==",
"dev": true,
"requires": {
"balanced-match": "^1.0.0"
+1 -1
View File
@@ -37,7 +37,7 @@
"@eslint/js": "^9.6.0",
"@types/sinon": "^17.0.3",
"chai": "~4.3",
"esbuild": "^0.25.0",
"esbuild": "^0.28.1",
"eslint": "^9.6.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
@@ -21,9 +21,11 @@ import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.List;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
@@ -48,6 +50,24 @@ public class UnboundIdContainerTests {
}
}
@Test
public void startLdapServerThenListenerBindsToLoopbackAddressOnly() throws Exception {
UnboundIdContainer server = new UnboundIdContainer("dc=springframework,dc=org", null);
server.setApplicationContext(new GenericApplicationContext());
List<Integer> ports = getDefaultPorts(1);
server.setPort(ports.get(0));
try {
server.afterPropertiesSet();
InMemoryDirectoryServer directoryServer = (InMemoryDirectoryServer) ReflectionTestUtils.getField(server,
"directoryServer");
assertThat(directoryServer.getListenAddress().isLoopbackAddress()).isTrue();
}
finally {
server.destroy();
}
}
@Test
public void afterPropertiesSetWhenPortIsZeroThenRandomPortIsSelected() throws Exception {
UnboundIdContainer server = new UnboundIdContainer("dc=springframework,dc=org", null);
@@ -17,6 +17,7 @@
package org.springframework.security.ldap.server;
import java.io.InputStream;
import java.net.InetAddress;
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
@@ -98,7 +99,8 @@ public class UnboundIdContainer
try {
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(this.defaultPartitionSuffix);
config.addAdditionalBindCredentials("uid=admin,ou=system", "secret");
config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("LDAP", this.port));
config.setListenerConfigs(
InMemoryListenerConfig.createLDAPConfig("LDAP", InetAddress.getLoopbackAddress(), this.port, null));
config.setEnforceSingleStructuralObjectClass(false);
config.setEnforceAttributeSyntaxCompliance(true);
DN dn = new DN(this.defaultPartitionSuffix);
@@ -20,7 +20,6 @@ import java.util.function.Supplier;
import org.jspecify.annotations.Nullable;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.messaging.Message;
@@ -47,11 +46,7 @@ public class DefaultMessageSecurityExpressionHandler<T> extends AbstractSecurity
Message<T> message) {
MessageSecurityExpressionRoot<T> root = createSecurityExpressionRoot(authentication, message);
StandardEvaluationContext ctx = new StandardEvaluationContext(root);
BeanResolver beanResolver = getBeanResolver();
if (beanResolver != null) {
// https://github.com/spring-projects/spring-framework/issues/35371
ctx.setBeanResolver(beanResolver);
}
ctx.setBeanResolver(getBeanResolver());
return ctx;
}
@@ -175,10 +175,7 @@ public class CurrentSecurityContextArgumentResolver implements HandlerMethodArgu
StandardEvaluationContext context = new StandardEvaluationContext();
context.setRootObject(securityContext);
context.setVariable("this", securityContext);
if (this.beanResolver != null) {
// https://github.com/spring-projects/spring-framework/issues/35371
context.setBeanResolver(this.beanResolver);
}
context.setBeanResolver(this.beanResolver);
Expression expression = this.parser.parseExpression(expressionToParse);
securityContext = expression.getValue(context);
}

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