Skip to content

Commit 65f2a34

Browse files
authored
Merge branch 'spring-projects:main' into gh-15286
2 parents 6f8cf72 + e2f98db commit 65f2a34

File tree

42 files changed

+1690
-128
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1690
-128
lines changed

config/src/main/java/org/springframework/security/config/saml2/RelyingPartyRegistrationsBeanDefinitionParser.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.springframework.core.io.ResourceLoader;
4040
import org.springframework.security.converter.RsaKeyConverters;
4141
import org.springframework.security.saml2.core.Saml2X509Credential;
42+
import org.springframework.security.saml2.provider.service.registration.AssertingPartyMetadata;
4243
import org.springframework.security.saml2.provider.service.registration.InMemoryRelyingPartyRegistrationRepository;
4344
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
4445
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations;
@@ -153,7 +154,7 @@ private static Map<String, Map<String, Object>> getAssertingParties(Element elem
153154
}
154155

155156
private static void addVerificationCredentials(Map<String, Object> assertingParty,
156-
RelyingPartyRegistration.AssertingPartyDetails.Builder builder) {
157+
AssertingPartyMetadata.Builder<?> builder) {
157158
List<String> verificationCertificateLocations = (List<String>) assertingParty.get(ELT_VERIFICATION_CREDENTIAL);
158159
List<Saml2X509Credential> verificationCredentials = new ArrayList<>();
159160
for (String certificateLocation : verificationCertificateLocations) {
@@ -163,7 +164,7 @@ private static void addVerificationCredentials(Map<String, Object> assertingPart
163164
}
164165

165166
private static void addEncryptionCredentials(Map<String, Object> assertingParty,
166-
RelyingPartyRegistration.AssertingPartyDetails.Builder builder) {
167+
AssertingPartyMetadata.Builder<?> builder) {
167168
List<String> encryptionCertificateLocations = (List<String>) assertingParty.get(ELT_ENCRYPTION_CREDENTIAL);
168169
List<Saml2X509Credential> encryptionCredentials = new ArrayList<>();
169170
for (String certificateLocation : encryptionCertificateLocations) {
@@ -220,8 +221,8 @@ private static RelyingPartyRegistration.Builder getBuilderFromMetadataLocationIf
220221
}
221222
else {
222223
builder = RelyingPartyRegistration.withRegistrationId(registrationId)
223-
.assertingPartyDetails((apBuilder) -> buildAssertingParty(relyingPartyRegistrationElt, assertingParties,
224-
apBuilder, parserContext));
224+
.assertingPartyMetadata((apBuilder) -> buildAssertingParty(relyingPartyRegistrationElt,
225+
assertingParties, apBuilder, parserContext));
225226
}
226227
addRemainingProperties(relyingPartyRegistrationElt, builder);
227228
return builder;
@@ -260,7 +261,7 @@ private static void addRemainingProperties(Element relyingPartyRegistrationElt,
260261
}
261262

262263
private static void buildAssertingParty(Element relyingPartyElt, Map<String, Map<String, Object>> assertingParties,
263-
RelyingPartyRegistration.AssertingPartyDetails.Builder builder, ParserContext parserContext) {
264+
AssertingPartyMetadata.Builder<?> builder, ParserContext parserContext) {
264265
String assertingPartyId = relyingPartyElt.getAttribute(ATT_ASSERTING_PARTY_ID);
265266
if (!assertingParties.containsKey(assertingPartyId)) {
266267
Object source = parserContext.extractSource(relyingPartyElt);
@@ -293,7 +294,7 @@ private static void buildAssertingParty(Element relyingPartyElt, Map<String, Map
293294
}
294295

295296
private static void addSigningAlgorithms(Map<String, Object> assertingParty,
296-
RelyingPartyRegistration.AssertingPartyDetails.Builder builder) {
297+
AssertingPartyMetadata.Builder<?> builder) {
297298
String signingAlgorithmsAttr = getAsString(assertingParty, ATT_SIGNING_ALGORITHMS);
298299
if (StringUtils.hasText(signingAlgorithmsAttr)) {
299300
List<String> signingAlgorithms = Arrays.asList(signingAlgorithmsAttr.split(","));

core/src/main/java/org/springframework/security/authentication/AuthenticationManager.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323
* Processes an {@link Authentication} request.
2424
*
2525
* @author Ben Alex
26+
* @author KyeongHoon Lee
2627
*/
28+
@FunctionalInterface
2729
public interface AuthenticationManager {
2830

2931
/**

docs/modules/ROOT/pages/servlet/saml2/login/authentication-requests.adoc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ Java::
114114
----
115115
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta")
116116
// ...
117-
.assertingPartyDetails(party -> party
117+
.assertingPartyMetadata(party -> party
118118
// ...
119119
.wantAuthnRequestsSigned(false)
120120
)
@@ -128,7 +128,7 @@ Kotlin::
128128
var relyingPartyRegistration: RelyingPartyRegistration =
129129
RelyingPartyRegistration.withRegistrationId("okta")
130130
// ...
131-
.assertingPartyDetails { party: AssertingPartyDetails.Builder -> party
131+
.assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party
132132
// ...
133133
.wantAuthnRequestsSigned(false)
134134
}
@@ -154,7 +154,7 @@ Java::
154154
String metadataLocation = "classpath:asserting-party-metadata.xml";
155155
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)
156156
// ...
157-
.assertingPartyDetails((party) -> party
157+
.assertingPartyMetadata((party) -> party
158158
// ...
159159
.signingAlgorithms((sign) -> sign.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512))
160160
)
@@ -169,7 +169,7 @@ var metadataLocation = "classpath:asserting-party-metadata.xml"
169169
var relyingPartyRegistration: RelyingPartyRegistration =
170170
RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)
171171
// ...
172-
.assertingPartyDetails { party: AssertingPartyDetails.Builder -> party
172+
.assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party
173173
// ...
174174
.signingAlgorithms { sign: MutableList<String?> ->
175175
sign.add(
@@ -197,7 +197,7 @@ Java::
197197
----
198198
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta")
199199
// ...
200-
.assertingPartyDetails(party -> party
200+
.assertingPartyMetadata(party -> party
201201
// ...
202202
.singleSignOnServiceBinding(Saml2MessageBinding.POST)
203203
)
@@ -211,7 +211,7 @@ Kotlin::
211211
var relyingPartyRegistration: RelyingPartyRegistration? =
212212
RelyingPartyRegistration.withRegistrationId("okta")
213213
// ...
214-
.assertingPartyDetails { party: AssertingPartyDetails.Builder -> party
214+
.assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party
215215
// ...
216216
.singleSignOnServiceBinding(Saml2MessageBinding.POST)
217217
}

docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,7 @@ public RelyingPartyRegistrationRepository relyingPartyRegistrations() throws Exc
484484
Saml2X509Credential credential = Saml2X509Credential.verification(certificate);
485485
RelyingPartyRegistration registration = RelyingPartyRegistration
486486
.withRegistrationId("example")
487-
.assertingPartyDetails(party -> party
487+
.assertingPartyMetadata(party -> party
488488
.entityId("https://idp.example.com/issuer")
489489
.singleSignOnServiceLocation("https://idp.example.com/SSO.saml2")
490490
.wantAuthnRequestsSigned(false)
@@ -508,7 +508,7 @@ open fun relyingPartyRegistrations(): RelyingPartyRegistrationRepository {
508508
val credential: Saml2X509Credential = Saml2X509Credential.verification(certificate)
509509
val registration = RelyingPartyRegistration
510510
.withRegistrationId("example")
511-
.assertingPartyDetails { party: AssertingPartyDetails.Builder ->
511+
.assertingPartyMetadata { party: AssertingPartyMetadata.Builder ->
512512
party
513513
.entityId("https://idp.example.com/issuer")
514514
.singleSignOnServiceLocation("https://idp.example.com/SSO.saml2")
@@ -699,7 +699,7 @@ RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.wit
699699
.entityId("{baseUrl}/{registrationId}")
700700
.decryptionX509Credentials(c -> c.add(relyingPartyDecryptingCredential()))
701701
.assertionConsumerServiceLocation("/my-login-endpoint/{registrationId}")
702-
.assertingPartyDetails(party -> party
702+
.assertingPartyMetadata(party -> party
703703
.entityId("https://ap.example.org")
704704
.verificationX509Credentials(c -> c.add(assertingPartyVerifyingCredential()))
705705
.singleSignOnServiceLocation("https://ap.example.org/SSO.saml2")
@@ -718,7 +718,7 @@ val relyingPartyRegistration =
718718
c.add(relyingPartyDecryptingCredential())
719719
}
720720
.assertionConsumerServiceLocation("/my-login-endpoint/{registrationId}")
721-
.assertingPartyDetails { party -> party
721+
.assertingPartyMetadata { party -> party
722722
.entityId("https://ap.example.org")
723723
.verificationX509Credentials { c -> c.add(assertingPartyVerifyingCredential()) }
724724
.singleSignOnServiceLocation("https://ap.example.org/SSO.saml2")
@@ -730,7 +730,7 @@ val relyingPartyRegistration =
730730
[TIP]
731731
====
732732
The top-level metadata methods are details about the relying party.
733-
The methods inside `assertingPartyDetails` are details about the asserting party.
733+
The methods inside `AssertingPartyMetadata` are details about the asserting party.
734734
====
735735

736736
[NOTE]

docs/modules/ROOT/pages/servlet/saml2/logout.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ It's common to need to set other values in the `<saml2:LogoutRequest>` than the
339339

340340
By default, Spring Security will issue a `<saml2:LogoutRequest>` and supply:
341341

342-
* The `Destination` attribute - from `RelyingPartyRegistration#getAssertingPartyDetails#getSingleLogoutServiceLocation`
342+
* The `Destination` attribute - from `RelyingPartyRegistration#getAssertingPartyMetadata#getSingleLogoutServiceLocation`
343343
* The `ID` attribute - a GUID
344344
* The `<Issuer>` element - from `RelyingPartyRegistration#getEntityId`
345345
* The `<NameID>` element - from `Authentication#getName`
@@ -424,7 +424,7 @@ It's common to need to set other values in the `<saml2:LogoutResponse>` than the
424424

425425
By default, Spring Security will issue a `<saml2:LogoutResponse>` and supply:
426426

427-
* The `Destination` attribute - from `RelyingPartyRegistration#getAssertingPartyDetails#getSingleLogoutServiceResponseLocation`
427+
* The `Destination` attribute - from `RelyingPartyRegistration#getAssertingPartyMetadata#getSingleLogoutServiceResponseLocation`
428428
* The `ID` attribute - a GUID
429429
* The `<Issuer>` element - from `RelyingPartyRegistration#getEntityId`
430430
* The `<Status>` element - `SUCCESS`

docs/modules/ROOT/pages/servlet/saml2/metadata.adoc

Lines changed: 123 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
[[servlet-saml2login-metadata]]
22
= Saml 2.0 Metadata
33

4-
Spring Security can <<parsing-asserting-party-metadata,parse asserting party metadata>> to produce an `AssertingPartyDetails` instance as well as <<publishing-relying-party-metadata,publish relying party metadata>> from a `RelyingPartyRegistration` instance.
4+
Spring Security can <<parsing-asserting-party-metadata,parse asserting party metadata>> to produce an `AssertingPartyMetadata` instance as well as <<publishing-relying-party-metadata,publish relying party metadata>> from a `RelyingPartyRegistration` instance.
55

66
[[parsing-asserting-party-metadata]]
77
== Parsing `<saml2:IDPSSODescriptor>` metadata
88

99
You can parse an asserting party's metadata xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistrationrepository[using `RelyingPartyRegistrations`].
1010

11-
When using the OpenSAML vendor support, the resulting `AssertingPartyDetails` will be of type `OpenSamlAssertingPartyDetails`.
11+
When using the OpenSAML vendor support, the resulting `AssertingPartyMetadata` will be of type `OpenSamlAssertingPartyDetails`.
1212
This means you'll be able to do get the underlying OpenSAML XMLObject by doing the following:
1313

1414
[tabs]
@@ -18,7 +18,7 @@ Java::
1818
[source,java,role="primary"]
1919
----
2020
OpenSamlAssertingPartyDetails details = (OpenSamlAssertingPartyDetails)
21-
registration.getAssertingPartyDetails();
21+
registration.getAssertingPartyMetadata();
2222
EntityDescriptor openSamlEntityDescriptor = details.getEntityDescriptor();
2323
----
2424
@@ -27,11 +27,129 @@ Kotlin::
2727
[source,kotlin,role="secondary"]
2828
----
2929
val details: OpenSamlAssertingPartyDetails =
30-
registration.getAssertingPartyDetails() as OpenSamlAssertingPartyDetails;
31-
val openSamlEntityDescriptor: EntityDescriptor = details.getEntityDescriptor();
30+
registration.getAssertingPartyMetadata() as OpenSamlAssertingPartyDetails
31+
val openSamlEntityDescriptor: EntityDescriptor = details.getEntityDescriptor()
3232
----
3333
======
3434

35+
=== Using `AssertingPartyMetadataRepository`
36+
37+
You can also be more targeted than `RelyingPartyRegistrations` by using `AssertingPartyMetadataRepository`, an interface that allows for only retrieving the asserting party metadata.
38+
39+
This allows three valuable features:
40+
41+
* Implementations can refresh asserting party metadata in an expiry-aware fashion
42+
* Implementations of `RelyingPartyRegistrationRepository` can more easily articulate a relationship between a relying party and its one or many corresponding asserting parties
43+
* Implementations can verify metadata signatures
44+
45+
For example, `OpenSamlAssertingPartyMetadataRepository` uses OpenSAML's `MetadataResolver`, and API whose implementations regularly refresh the underlying metadata in an expiry-aware fashion.
46+
47+
This means that you can now create a refreshable `RelyingPartyRegistrationRepository` in just a few lines of code:
48+
49+
[tabs]
50+
======
51+
Java::
52+
+
53+
[source,java,role="primary"]
54+
----
55+
@Component
56+
public class RefreshableRelyingPartyRegistrationRepository
57+
implements IterableRelyingPartyRegistrationRepository {
58+
59+
private final AssertingPartyMetadataRepository metadata =
60+
OpenSamlAssertingPartyMetadataRepository
61+
.fromTrustedMetadataLocation("https://idp.example.org/metadata").build();
62+
63+
@Override
64+
public RelyingPartyRegistration findByRegistrationId(String registrationId) {
65+
AssertingPartyMetadata metadata = this.metadata.findByEntityId(registrationId);
66+
if (metadata == null) {
67+
return null;
68+
}
69+
return applyRelyingParty(metadata);
70+
}
71+
72+
@Override
73+
public Iterator<RelyingPartyRegistration> iterator() {
74+
return StreamSupport.stream(this.metadata.spliterator(), false)
75+
.map(this::applyRelyingParty).iterator();
76+
}
77+
78+
private RelyingPartyRegistration applyRelyingParty(AssertingPartyMetadata metadata) {
79+
return RelyingPartyRegistration.withAssertingPartyMetadata(metadata)
80+
// apply any relying party configuration
81+
.build();
82+
}
83+
84+
}
85+
----
86+
87+
Kotlin::
88+
+
89+
[source,kotlin,role="secondary"]
90+
----
91+
@Component
92+
class RefreshableRelyingPartyRegistrationRepository : IterableRelyingPartyRegistrationRepository {
93+
94+
private val metadata: AssertingPartyMetadataRepository =
95+
OpenSamlAssertingPartyMetadataRepository.fromTrustedMetadataLocation(
96+
"https://idp.example.org/metadata").build()
97+
98+
fun findByRegistrationId(registrationId:String?): RelyingPartyRegistration {
99+
val metadata = this.metadata.findByEntityId(registrationId)
100+
if (metadata == null) {
101+
return null
102+
}
103+
return applyRelyingParty(metadata)
104+
}
105+
106+
fun iterator(): Iterator<RelyingPartyRegistration> {
107+
return StreamSupport.stream(this.metadata.spliterator(), false)
108+
.map(this::applyRelyingParty).iterator()
109+
}
110+
111+
private fun applyRelyingParty(metadata: AssertingPartyMetadata): RelyingPartyRegistration {
112+
val details: AssertingPartyMetadata = metadata as AssertingPartyMetadata
113+
return RelyingPartyRegistration.withAssertingPartyMetadata(details)
114+
// apply any relying party configuration
115+
.build()
116+
}
117+
}
118+
----
119+
======
120+
121+
[TIP]
122+
`OpenSamlAssertingPartyMetadataRepository` also ships with a constructor so you can provide a custom `MetadataResolver`. Since the underlying `MetadataResolver` is doing the expirying and refreshing, if you use the constructor directly, you will only get these features by providing an implementation that does so.
123+
124+
=== Verifying Metadata Signatures
125+
126+
You can also verify metadata signatures using `OpenSamlAssertingPartyMetadataRepository` by providing the appropriate set of ``Saml2X509Credential``s as follows:
127+
128+
[tabs]
129+
======
130+
Java::
131+
+
132+
[source,java,role="primary"]
133+
----
134+
OpenSamlAssertingPartyMetadataRepository.withMetadataLocation("https://idp.example.org/metadata")
135+
.verificationCredentials((c) -> c.add(myVerificationCredential))
136+
.build();
137+
----
138+
139+
Kotlin::
140+
+
141+
[source,kotlin,role="secondary"]
142+
----
143+
OpenSamlAssertingPartyMetadataRepository.withMetadataLocation("https://idp.example.org/metadata")
144+
.verificationCredentials({ c : Collection<Saml2X509Credential> ->
145+
c.add(myVerificationCredential) })
146+
.build()
147+
----
148+
======
149+
150+
[NOTE]
151+
If no credentials are provided, the component will not perform signature validation.
152+
35153
[[publishing-relying-party-metadata]]
36154
== Producing `<saml2:SPSSODescriptor>` Metadata
37155

docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"dependencies": {
33
"antora": "3.2.0-alpha.5",
44
"@antora/atlas-extension": "1.0.0-alpha.2",
5-
"@antora/collector-extension": "1.0.0-alpha.4",
5+
"@antora/collector-extension": "1.0.0-alpha.6",
66
"@asciidoctor/tabs": "1.0.0-beta.6",
77
"@springio/antora-extensions": "1.12.0",
88
"@springio/asciidoctor-extensions": "1.0.0-alpha.11"

gradle/libs.versions.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ org-apache-maven-resolver = "1.9.21"
99
org-aspectj = "1.9.22.1"
1010
org-bouncycastle = "1.78.1"
1111
org-eclipse-jetty = "11.0.22"
12-
org-jetbrains-kotlin = "1.9.24"
12+
org-jetbrains-kotlin = "1.9.25"
1313
org-jetbrains-kotlinx = "1.8.1"
1414
org-mockito = "5.11.0"
1515
org-opensaml = "4.3.2"
16-
org-springframework = "6.2.0-M5"
16+
org-springframework = "6.2.0-M6"
1717

1818
[libraries]
1919
ch-qos-logback-logback-classic = "ch.qos.logback:logback-classic:1.5.6"
@@ -72,7 +72,7 @@ org-hamcrest = "org.hamcrest:hamcrest:2.2"
7272
org-hibernate-orm-hibernate-core = "org.hibernate.orm:hibernate-core:6.4.9.Final"
7373
org-hsqldb = "org.hsqldb:hsqldb:2.7.3"
7474
org-jetbrains-kotlin-kotlin-bom = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "org-jetbrains-kotlin" }
75-
org-jetbrains-kotlin-kotlin-gradle-plugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.24"
75+
org-jetbrains-kotlin-kotlin-gradle-plugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.25"
7676
org-jetbrains-kotlinx-kotlinx-coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version.ref = "org-jetbrains-kotlinx" }
7777
org-junit-junit-bom = "org.junit:junit-bom:5.10.3"
7878
org-mockito-mockito-bom = { module = "org.mockito:mockito-bom", version.ref = "org-mockito" }

oauth2/oauth2-resource-server/src/main/java/org/springframework/security/oauth2/server/resource/web/reactive/function/client/ServletBearerExchangeFilterFunction.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,6 @@
5050
* To locate the bearer token, this looks in the Reactor {@link Context} for a key of type
5151
* {@link Authentication}.
5252
*
53-
* Registering
54-
* {@see org.springframework.security.config.annotation.web.configuration.OAuth2ResourceServerConfiguration.OAuth2ResourceServerWebFluxSecurityConfiguration.BearerRequestContextSubscriberRegistrar},
55-
* as a {@code @Bean} will take care of this automatically, but certainly an application
56-
* can supply a {@link Context} of its own to override.
57-
*
5853
* @author Josh Cummings
5954
* @since 5.2
6055
*/

0 commit comments

Comments
 (0)