Skip to content

Commit f7e9566

Browse files
committed
Add OpenSamlAssertingPartyMetadataRepository
Closes spring-projectsgh-12116 Closes spring-projectsgh-15395
1 parent fba6e31 commit f7e9566

File tree

6 files changed

+917
-4
lines changed

6 files changed

+917
-4
lines changed

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

Lines changed: 118 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,127 @@ Kotlin::
2727
[source,kotlin,role="secondary"]
2828
----
2929
val details: OpenSamlAssertingPartyDetails =
30-
registration.getAssertingPartyDetails() as OpenSamlAssertingPartyDetails;
31-
val openSamlEntityDescriptor: EntityDescriptor = details.getEntityDescriptor();
30+
registration.getAssertingPartyDetails() 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+
.fromMetadataLocation("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+
AssertingPartyDetails details = (AssertingPartyDetails) metadata;
80+
return RelyingPartyRegistration.withAssertingPartyDetails(details)
81+
// apply any relying party configuration
82+
.build();
83+
}
84+
85+
}
86+
----
87+
88+
Kotlin::
89+
+
90+
[source,kotlin,role="secondary"]
91+
----
92+
@Component
93+
class RefreshableRelyingPartyRegistrationRepository : IterableRelyingPartyRegistrationRepository {
94+
95+
private val metadata: AssertingPartyMetadataRepository =
96+
OpenSamlAssertingPartyMetadataRepository.fromMetadataLocation(
97+
"https://idp.example.org/metadata").build()
98+
99+
fun findByRegistrationId(registrationId:String?): RelyingPartyRegistration {
100+
val metadata = this.metadata.findByEntityId(registrationId)
101+
if (metadata == null) {
102+
return null
103+
}
104+
return applyRelyingParty(metadata)
105+
}
106+
107+
fun iterator(): Iterator<RelyingPartyRegistration> {
108+
return StreamSupport.stream(this.metadata.spliterator(), false)
109+
.map(this::applyRelyingParty).iterator()
110+
}
111+
112+
private fun applyRelyingParty(metadata: AssertingPartyMetadata): RelyingPartyRegistration {
113+
val details: AssertingPartyDetails = metadata as AssertingPartyDetails
114+
return RelyingPartyRegistration.withAssertingPartyDetails(details)
115+
// apply any relying party configuration
116+
.build()
117+
}
118+
}
119+
----
120+
======
121+
122+
=== Verifying Metadata Signatures
123+
124+
You can also verify metadata signatures using `OpenSamlAssertingPartyMetadataRepository` by providing the appropriate set of ``Saml2X509Credential``s as follows:
125+
126+
[tabs]
127+
======
128+
Java::
129+
+
130+
[source,java,role="primary"]
131+
----
132+
OpenSamlAssertingPartyMetadataRepository.withMetadataLocation("https://idp.example.org/metadata")
133+
.verificationCredentials((c) -> c.add(myVerificationCredential))
134+
.build();
135+
----
136+
137+
Kotlin::
138+
+
139+
[source,kotlin,role="secondary"]
140+
----
141+
OpenSamlAssertingPartyMetadataRepository.withMetadataLocation("https://idp.example.org/metadata")
142+
.verificationCredentials({ c : Collection<Saml2X509Credential> ->
143+
c.add(myVerificationCredential) })
144+
.build()
145+
----
146+
======
147+
148+
[NOTE]
149+
If no credentials are provided, the component will not perform signature validation.
150+
35151
[[publishing-relying-party-metadata]]
36152
== Producing `<saml2:SPSSODescriptor>` Metadata
37153

saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/metadata/OpenSamlSigningUtils.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,13 @@ static String serialize(XMLObject object) {
8080
}
8181

8282
static <O extends SignableXMLObject> O sign(O object, RelyingPartyRegistration relyingPartyRegistration) {
83-
SignatureSigningParameters parameters = resolveSigningParameters(relyingPartyRegistration);
83+
List<String> algorithms = relyingPartyRegistration.getAssertingPartyDetails().getSigningAlgorithms();
84+
List<Credential> credentials = resolveSigningCredentials(relyingPartyRegistration);
85+
return sign(object, algorithms, credentials);
86+
}
87+
88+
static <O extends SignableXMLObject> O sign(O object, List<String> algorithms, List<Credential> credentials) {
89+
SignatureSigningParameters parameters = resolveSigningParameters(algorithms, credentials);
8490
try {
8591
SignatureSupport.signObject(object, parameters);
8692
return object;
@@ -98,6 +104,11 @@ private static SignatureSigningParameters resolveSigningParameters(
98104
RelyingPartyRegistration relyingPartyRegistration) {
99105
List<Credential> credentials = resolveSigningCredentials(relyingPartyRegistration);
100106
List<String> algorithms = relyingPartyRegistration.getAssertingPartyDetails().getSigningAlgorithms();
107+
return resolveSigningParameters(algorithms, credentials);
108+
}
109+
110+
private static SignatureSigningParameters resolveSigningParameters(List<String> algorithms,
111+
List<Credential> credentials) {
101112
List<String> digests = Collections.singletonList(SignatureConstants.ALGO_ID_DIGEST_SHA256);
102113
String canonicalization = SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS;
103114
SignatureSigningParametersResolver resolver = new SAMLMetadataSignatureSigningParametersResolver();

0 commit comments

Comments
 (0)