Skip to content

Commit a1b282f

Browse files
committed
Merge branch '5.7.x' into 5.8.x
Closes gh-12693
2 parents 0baf650 + 2db4430 commit a1b282f

File tree

6 files changed

+154
-23
lines changed

6 files changed

+154
-23
lines changed

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,36 @@
11
[[servlet-saml2login-metadata]]
2-
= Producing `<saml2:SPSSODescriptor>` Metadata
2+
= Saml 2.0 Metadata
3+
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.
5+
6+
[[parsing-asserting-party-metadata]]
7+
== Parsing `<saml2:IDPSSODescriptor>` metadata
8+
9+
You can parse an asserting party's metadata xref:servlet/saml2/login/overview.adoc#servlet-saml2login-relyingpartyregistrationrepository[using `RelyingPartyRegistrations`].
10+
11+
When using the OpenSAML vendor support, the resulting `AssertingPartyDetails` will be of type `OpenSamlAssertingPartyDetails`.
12+
This means you'll be able to do get the underlying OpenSAML XMLObject by doing the following:
13+
14+
====
15+
.Java
16+
[source,java,role="primary"]
17+
----
18+
OpenSamlAssertingPartyDetails details = (OpenSamlAssertingPartyDetails)
19+
registration.getAssertingPartyDetails();
20+
EntityDescriptor openSamlEntityDescriptor = details.getEntityDescriptor();
21+
----
22+
23+
.Kotlin
24+
[source,kotlin,role="secondary"]
25+
----
26+
val details: OpenSamlAssertingPartyDetails =
27+
registration.getAssertingPartyDetails() as OpenSamlAssertingPartyDetails;
28+
val openSamlEntityDescriptor: EntityDescriptor = details.getEntityDescriptor();
29+
----
30+
====
31+
32+
[[publishing-relying-party-metadata]]
33+
== Producing `<saml2:SPSSODescriptor>` Metadata
334

435
You can publish a metadata endpoint by adding the `Saml2MetadataFilter` to the filter chain, as you'll see below:
536

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2002-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.saml2.provider.service.registration;
18+
19+
import java.io.InputStream;
20+
import java.util.ArrayList;
21+
import java.util.Collection;
22+
23+
class OpenSamlMetadataRelyingPartyRegistrationConverter {
24+
25+
private final OpenSamlMetadataAssertingPartyDetailsConverter converter = new OpenSamlMetadataAssertingPartyDetailsConverter();
26+
27+
Collection<RelyingPartyRegistration.Builder> convert(InputStream source) {
28+
Collection<RelyingPartyRegistration.Builder> builders = new ArrayList<>();
29+
for (RelyingPartyRegistration.AssertingPartyDetails.Builder builder : this.converter.convert(source)) {
30+
builders.add(new RelyingPartyRegistration.Builder(builder));
31+
}
32+
return builders;
33+
}
34+
35+
}

saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -89,8 +89,7 @@ public List<MediaType> getSupportedMediaTypes() {
8989
@Override
9090
public RelyingPartyRegistration.Builder read(Class<? extends RelyingPartyRegistration.Builder> clazz,
9191
HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
92-
return RelyingPartyRegistration
93-
.withAssertingPartyDetails(this.converter.convert(inputMessage.getBody()).iterator().next().build());
92+
return new RelyingPartyRegistration.Builder(this.converter.convert(inputMessage.getBody()).iterator().next());
9493
}
9594

9695
@Override

saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistration.java

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
3030

3131
import org.opensaml.xmlsec.signature.support.SignatureConstants;
3232

33+
import org.springframework.core.convert.converter.Converter;
3334
import org.springframework.security.saml2.core.Saml2X509Credential;
3435
import org.springframework.util.Assert;
3536
import org.springframework.util.CollectionUtils;
@@ -986,6 +987,14 @@ public static final class Builder {
986987

987988
private AssertingPartyDetails.Builder assertingPartyDetailsBuilder = new AssertingPartyDetails.Builder();
988989

990+
private Builder() {
991+
992+
}
993+
994+
private Builder(AssertingPartyDetails.Builder assertingPartyDetailsBuilder) {
995+
this.assertingPartyDetailsBuilder = assertingPartyDetailsBuilder;
996+
}
997+
989998
/**
990999
* Set the asserting party's <a href=
9911000
* "https://www.oasis-open.org/committees/download.php/51890/SAML%20MD%20simplified%20overview.pdf#2.9%20EntityDescriptor">EntityID</a>.
@@ -1048,7 +1057,7 @@ public ProviderDetails build() {
10481057

10491058
public static final class Builder {
10501059

1051-
private String registrationId;
1060+
private Converter<ProviderDetails, String> registrationId = ProviderDetails::getEntityId;
10521061

10531062
private String entityId = "{baseUrl}/saml2/service-provider-metadata/{registrationId}";
10541063

@@ -1068,12 +1077,17 @@ public static final class Builder {
10681077

10691078
private String nameIdFormat = null;
10701079

1071-
private ProviderDetails.Builder providerDetails = new ProviderDetails.Builder();
1080+
private ProviderDetails.Builder providerDetails;
10721081

10731082
private Collection<org.springframework.security.saml2.credentials.Saml2X509Credential> credentials = new LinkedHashSet<>();
10741083

10751084
private Builder(String registrationId) {
1076-
this.registrationId = registrationId;
1085+
this.registrationId = (party) -> registrationId;
1086+
this.providerDetails = new ProviderDetails.Builder();
1087+
}
1088+
1089+
Builder(AssertingPartyDetails.Builder builder) {
1090+
this.providerDetails = new ProviderDetails.Builder(builder);
10771091
}
10781092

10791093
/**
@@ -1082,7 +1096,7 @@ private Builder(String registrationId) {
10821096
* @return this object
10831097
*/
10841098
public Builder registrationId(String id) {
1085-
this.registrationId = id;
1099+
this.registrationId = (party) -> id;
10861100
return this;
10871101
}
10881102

@@ -1405,11 +1419,12 @@ public RelyingPartyRegistration build() {
14051419
this.singleLogoutServiceBindings.add(Saml2MessageBinding.POST);
14061420
}
14071421

1408-
return new RelyingPartyRegistration(this.registrationId, this.entityId,
1409-
this.assertionConsumerServiceLocation, this.assertionConsumerServiceBinding,
1410-
this.singleLogoutServiceLocation, this.singleLogoutServiceResponseLocation,
1411-
this.singleLogoutServiceBindings, this.providerDetails.build(), this.nameIdFormat, this.credentials,
1412-
this.decryptionX509Credentials, this.signingX509Credentials);
1422+
ProviderDetails party = this.providerDetails.build();
1423+
String registrationId = this.registrationId.convert(party);
1424+
return new RelyingPartyRegistration(registrationId, this.entityId, this.assertionConsumerServiceLocation,
1425+
this.assertionConsumerServiceBinding, this.singleLogoutServiceLocation,
1426+
this.singleLogoutServiceResponseLocation, this.singleLogoutServiceBindings, party,
1427+
this.nameIdFormat, this.credentials, this.decryptionX509Credentials, this.signingX509Credentials);
14131428
}
14141429

14151430
}

saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrations.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,13 +18,11 @@
1818

1919
import java.io.IOException;
2020
import java.io.InputStream;
21-
import java.util.ArrayList;
2221
import java.util.Collection;
2322

2423
import org.springframework.core.io.DefaultResourceLoader;
2524
import org.springframework.core.io.ResourceLoader;
2625
import org.springframework.security.saml2.Saml2Exception;
27-
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration.AssertingPartyDetails;
2826

2927
/**
3028
* A utility class for constructing instances of {@link RelyingPartyRegistration}
@@ -36,7 +34,7 @@
3634
*/
3735
public final class RelyingPartyRegistrations {
3836

39-
private static final OpenSamlMetadataAssertingPartyDetailsConverter assertingPartyMetadataConverter = new OpenSamlMetadataAssertingPartyDetailsConverter();
37+
private static final OpenSamlMetadataRelyingPartyRegistrationConverter relyingPartyRegistrationConverter = new OpenSamlMetadataRelyingPartyRegistrationConverter();
4038

4139
private static final ResourceLoader resourceLoader = new DefaultResourceLoader();
4240

@@ -215,11 +213,7 @@ public static Collection<RelyingPartyRegistration.Builder> collectionFromMetadat
215213
* @since 5.7
216214
*/
217215
public static Collection<RelyingPartyRegistration.Builder> collectionFromMetadata(InputStream source) {
218-
Collection<RelyingPartyRegistration.Builder> builders = new ArrayList<>();
219-
for (AssertingPartyDetails.Builder builder : assertingPartyMetadataConverter.convert(source)) {
220-
builders.add(RelyingPartyRegistration.withAssertingPartyDetails(builder.build()));
221-
}
222-
return builders;
216+
return relyingPartyRegistrationConverter.convert(source);
223217
}
224218

225219
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2002-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.saml2.provider.service.registration;
18+
19+
import java.io.BufferedReader;
20+
import java.io.ByteArrayInputStream;
21+
import java.io.InputStream;
22+
import java.io.InputStreamReader;
23+
import java.nio.charset.StandardCharsets;
24+
import java.util.stream.Collectors;
25+
26+
import org.junit.jupiter.api.BeforeEach;
27+
import org.junit.jupiter.api.Test;
28+
29+
import org.springframework.core.io.ClassPathResource;
30+
31+
import static org.assertj.core.api.Assertions.assertThat;
32+
33+
public class OpenSamlMetadataRelyingPartyRegistrationConverterTests {
34+
35+
private OpenSamlMetadataRelyingPartyRegistrationConverter converter = new OpenSamlMetadataRelyingPartyRegistrationConverter();
36+
37+
private String metadata;
38+
39+
@BeforeEach
40+
public void setup() throws Exception {
41+
ClassPathResource resource = new ClassPathResource("test-metadata.xml");
42+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getInputStream()))) {
43+
this.metadata = reader.lines().collect(Collectors.joining());
44+
}
45+
}
46+
47+
// gh-12667
48+
@Test
49+
public void convertWhenDefaultsThenAssertingPartyInstanceOfOpenSaml() throws Exception {
50+
try (InputStream source = new ByteArrayInputStream(this.metadata.getBytes(StandardCharsets.UTF_8))) {
51+
this.converter.convert(source)
52+
.forEach((registration) -> assertThat(registration.build().getAssertingPartyDetails())
53+
.isInstanceOf(OpenSamlAssertingPartyDetails.class));
54+
}
55+
}
56+
57+
}

0 commit comments

Comments
 (0)