Merge pull request #35902 from lasselindqvist
* pr/35902: Polish 'Choose SAML party based on entity ID rather than always using first' Choose SAML party based on entity ID rather than always using first Closes gh-35902
This commit is contained in:
commit
094cc5563e
|
@ -20,6 +20,7 @@ import java.io.InputStream;
|
|||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.security.interfaces.RSAPrivateKey;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
@ -63,6 +64,7 @@ import org.springframework.util.StringUtils;
|
|||
* @author Madhura Bhave
|
||||
* @author Phillip Webb
|
||||
* @author Moritz Halbritter
|
||||
* @author Lasse Lindqvist
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Conditional(RegistrationConfiguredCondition.class)
|
||||
|
@ -88,9 +90,8 @@ class Saml2RelyingPartyRegistrationConfiguration {
|
|||
private RelyingPartyRegistration asRegistration(String id, Registration properties) {
|
||||
AssertingPartyProperties assertingParty = new AssertingPartyProperties(properties, id);
|
||||
boolean usingMetadata = StringUtils.hasText(assertingParty.getMetadataUri());
|
||||
Builder builder = (usingMetadata)
|
||||
? RelyingPartyRegistrations.fromMetadataLocation(assertingParty.getMetadataUri()).registrationId(id)
|
||||
: RelyingPartyRegistration.withRegistrationId(id);
|
||||
Builder builder = (!usingMetadata) ? RelyingPartyRegistration.withRegistrationId(id)
|
||||
: createBuilderUsingMetadata(id, assertingParty).registrationId(id);
|
||||
builder.assertionConsumerServiceLocation(properties.getAcs().getLocation());
|
||||
builder.assertionConsumerServiceBinding(properties.getAcs().getBinding());
|
||||
builder.assertingPartyDetails(mapAssertingParty(properties, id, usingMetadata));
|
||||
|
@ -119,6 +120,25 @@ class Saml2RelyingPartyRegistrationConfiguration {
|
|||
return registration;
|
||||
}
|
||||
|
||||
private RelyingPartyRegistration.Builder createBuilderUsingMetadata(String id,
|
||||
AssertingPartyProperties properties) {
|
||||
String requiredEntityId = properties.getEntityId();
|
||||
Collection<Builder> candidates = RelyingPartyRegistrations
|
||||
.collectionFromMetadataLocation(properties.getMetadataUri());
|
||||
for (RelyingPartyRegistration.Builder candidate : candidates) {
|
||||
if (requiredEntityId == null || requiredEntityId.equals(getEntityId(candidate))) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("No relying party with Entity ID '" + requiredEntityId + "' found");
|
||||
}
|
||||
|
||||
private Object getEntityId(RelyingPartyRegistration.Builder candidate) {
|
||||
String[] result = new String[1];
|
||||
candidate.assertingPartyDetails((builder) -> result[0] = builder.build().getEntityId());
|
||||
return result[0];
|
||||
}
|
||||
|
||||
private Consumer<AssertingPartyDetails.Builder> mapAssertingParty(Registration registration, String id,
|
||||
boolean usingMetadata) {
|
||||
return (details) -> {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.security.saml2;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -55,6 +56,7 @@ import static org.mockito.Mockito.mock;
|
|||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Moritz Halbritter
|
||||
* @author Lasse Lindqvist
|
||||
*/
|
||||
class Saml2RelyingPartyAutoConfigurationTests {
|
||||
|
||||
|
@ -403,6 +405,39 @@ class Saml2RelyingPartyAutoConfigurationTests {
|
|||
.run((context) -> assertThat(hasFilter(context, Saml2LogoutRequestFilter.class)).isTrue());
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoconfigurationWhenMultipleProvidersAndNoSpecifiedEntityId() throws Exception {
|
||||
testMultipleProviders(null, "https://idp.example.com/idp/shibboleth");
|
||||
}
|
||||
|
||||
@Test
|
||||
void autoconfigurationWhenMultipleProvidersAndSpecifiedEntityId() throws Exception {
|
||||
testMultipleProviders("https://idp.example.com/idp/shibboleth", "https://idp.example.com/idp/shibboleth");
|
||||
testMultipleProviders("https://idp2.example.com/idp/shibboleth", "https://idp2.example.com/idp/shibboleth");
|
||||
}
|
||||
|
||||
private void testMultipleProviders(String specifiedEntityId, String expected) throws IOException, Exception {
|
||||
try (MockWebServer server = new MockWebServer()) {
|
||||
server.start();
|
||||
String metadataUrl = server.url("").toString();
|
||||
setupMockResponse(server, new ClassPathResource("saml/idp-metadata-with-multiple-providers"));
|
||||
WebApplicationContextRunner contextRunner = this.contextRunner
|
||||
.withPropertyValues(PREFIX + ".foo.assertingparty.metadata-uri=" + metadataUrl);
|
||||
if (specifiedEntityId != null) {
|
||||
contextRunner = contextRunner
|
||||
.withPropertyValues(PREFIX + ".foo.assertingparty.entity-id=" + specifiedEntityId);
|
||||
}
|
||||
contextRunner.run((context) -> {
|
||||
assertThat(context).hasSingleBean(RelyingPartyRegistrationRepository.class);
|
||||
assertThat(server.getRequestCount()).isOne();
|
||||
RelyingPartyRegistrationRepository repository = context
|
||||
.getBean(RelyingPartyRegistrationRepository.class);
|
||||
RelyingPartyRegistration registration = repository.findByRegistrationId("foo");
|
||||
assertThat(registration.getAssertingPartyDetails().getEntityId()).isEqualTo(expected);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private String[] getPropertyValuesWithoutSigningCredentials(boolean signRequests, boolean useDeprecated) {
|
||||
String assertingParty = useDeprecated ? "identityprovider" : "assertingparty";
|
||||
return new String[] {
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ID="virtu-20230614094100" Name="virtu" validUntil="2023-07-12T06:41:00Z" xsi:schemaLocation="urn:oasis:names:tc:SAML:2.0:metadata saml-schema-metadata-2.0.xsd http://www.w3.org/2000/09/xmldsig# xmldsig-core-schema.xsd">
|
||||
<md:EntityDescriptor entityID="https://idp.example.com/idp/shibboleth"
|
||||
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:shibmd="urn:mace:shibboleth:metadata:1.0"
|
||||
xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui">
|
||||
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||
<md:KeyDescriptor>
|
||||
<ds:KeyInfo>
|
||||
<ds:X509Data>
|
||||
<ds:X509Certificate>
|
||||
MIIDZjCCAk6gAwIBAgIVAL9O+PA7SXtlwZZY8MVSE9On1cVWMA0GCSqGSIb3DQEB
|
||||
BQUAMCkxJzAlBgNVBAMTHmlkZW0tcHVwYWdlbnQuZG16LWludC51bmltby5pdDAe
|
||||
Fw0xMzA3MjQwMDQ0MTRaFw0zMzA3MjQwMDQ0MTRaMCkxJzAlBgNVBAMTHmlkZW0t
|
||||
cHVwYWdlbnQuZG16LWludC51bmltby5pdDCCASIwDQYJKoZIhvcNAMIIDQADggEP
|
||||
ADCCAQoCggEBAIAcp/VyzZGXUF99kwj4NvL/Rwv4YvBgLWzpCuoxqHZ/hmBwJtqS
|
||||
v0y9METBPFbgsF3hCISnxbcmNVxf/D0MoeKtw1YPbsUmow/bFe+r72hZ+IVAcejN
|
||||
iDJ7t5oTjsRN1t1SqvVVk6Ryk5AZhpFW+W9pE9N6c7kJ16Rp2/mbtax9OCzxpece
|
||||
byi1eiLfIBmkcRawL/vCc2v6VLI18i6HsNVO3l2yGosKCbuSoGDx2fCdAOk/rgdz
|
||||
cWOvFsIZSKuD+FVbSS/J9GVs7yotsS4PRl4iX9UMnfDnOMfO7bcBgbXtDl4SCU1v
|
||||
dJrRw7IL/pLz34Rv9a8nYitrzrxtLOp3nYUCAwEAAaOBhDCBgTBgBgMIIDEEWTBX
|
||||
gh5pZGVtLXB1cGFnZW50LmRtei1pbnQudW5pbW8uaXSGNWh0dHBzOi8vaWRlbS1w
|
||||
dXBhZ2VudC5kbXotaW50LnVuaW1vLml0L2lkcC9zaGliYm9sZXRoMB0GA1UdDgQW
|
||||
BBT8PANzz+adGnTRe8ldcyxAwe4VnzANBgkqhkiG9w0BAQUFAAOCAQEAOEnO8Clu
|
||||
9z/Lf/8XOOsTdxJbV29DIF3G8KoQsB3dBsLwPZVEAQIP6ceS32Xaxrl6FMTDDNkL
|
||||
qUvvInUisw0+I5zZwYHybJQCletUWTnz58SC4C9G7FpuXHFZnOGtRcgGD1NOX4UU
|
||||
duus/4nVcGSLhDjszZ70Xtj0gw2Sn46oQPHTJ81QZ3Y9ih+Aj1c9OtUSBwtWZFkU
|
||||
yooAKoR8li68Yb21zN2N65AqV+ndL98M8xUYMKLONuAXStDeoVCipH6PJ09Z5U2p
|
||||
V5p4IQRV6QBsNw9CISJFuHzkVYTH5ZxzN80Ru46vh4y2M0Nu8GQ9I085KoZkrf5e
|
||||
Cq53OZt9ISjHEw==
|
||||
</ds:X509Certificate>
|
||||
</ds:X509Data>
|
||||
</ds:KeyInfo>
|
||||
</md:KeyDescriptor>
|
||||
<md:SingleSignOnService
|
||||
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
||||
Location="https://idp.example.com/sso"/>
|
||||
</md:IDPSSODescriptor>
|
||||
<md:ContactPerson contactType="technical">
|
||||
<md:EmailAddress>mailto:technical.contact@example.com</md:EmailAddress>
|
||||
</md:ContactPerson>
|
||||
</md:EntityDescriptor>
|
||||
<md:EntityDescriptor entityID="https://idp2.example.com/idp/shibboleth"
|
||||
xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:shibmd="urn:mace:shibboleth:metadata:1.0"
|
||||
xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
|
||||
xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui">
|
||||
<md:IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
||||
<md:KeyDescriptor>
|
||||
<ds:KeyInfo>
|
||||
<ds:X509Data>
|
||||
<ds:X509Certificate>
|
||||
MIIDZjCCAk6gAwIBAgIVAL9O+PA7SXtlwZZY8MVSE9On1cVWMA0GCSqGSIb3DQEB
|
||||
BQUAMCkxJzAlBgNVBAMTHmlkZW0tcHVwYWdlbnQuZG16LWludC51bmltby5pdDAe
|
||||
Fw0xMzA3MjQwMDQ0MTRaFw0zMzA3MjQwMDQ0MTRaMCkxJzAlBgNVBAMTHmlkZW0t
|
||||
cHVwYWdlbnQuZG16LWludC51bmltby5pdDCCASIwDQYJKoZIhvcNAMIIDQADggEP
|
||||
ADCCAQoCggEBAIAcp/VyzZGXUF99kwj4NvL/Rwv4YvBgLWzpCuoxqHZ/hmBwJtqS
|
||||
v0y9METBPFbgsF3hCISnxbcmNVxf/D0MoeKtw1YPbsUmow/bFe+r72hZ+IVAcejN
|
||||
iDJ7t5oTjsRN1t1SqvVVk6Ryk5AZhpFW+W9pE9N6c7kJ16Rp2/mbtax9OCzxpece
|
||||
byi1eiLfIBmkcRawL/vCc2v6VLI18i6HsNVO3l2yGosKCbuSoGDx2fCdAOk/rgdz
|
||||
cWOvFsIZSKuD+FVbSS/J9GVs7yotsS4PRl4iX9UMnfDnOMfO7bcBgbXtDl4SCU1v
|
||||
dJrRw7IL/pLz34Rv9a8nYitrzrxtLOp3nYUCAwEAAaOBhDCBgTBgBgMIIDEEWTBX
|
||||
gh5pZGVtLXB1cGFnZW50LmRtei1pbnQudW5pbW8uaXSGNWh0dHBzOi8vaWRlbS1w
|
||||
dXBhZ2VudC5kbXotaW50LnVuaW1vLml0L2lkcC9zaGliYm9sZXRoMB0GA1UdDgQW
|
||||
BBT8PANzz+adGnTRe8ldcyxAwe4VnzANBgkqhkiG9w0BAQUFAAOCAQEAOEnO8Clu
|
||||
9z/Lf/8XOOsTdxJbV29DIF3G8KoQsB3dBsLwPZVEAQIP6ceS32Xaxrl6FMTDDNkL
|
||||
qUvvInUisw0+I5zZwYHybJQCletUWTnz58SC4C9G7FpuXHFZnOGtRcgGD1NOX4UU
|
||||
duus/4nVcGSLhDjszZ70Xtj0gw2Sn46oQPHTJ81QZ3Y9ih+Aj1c9OtUSBwtWZFkU
|
||||
yooAKoR8li68Yb21zN2N65AqV+ndL98M8xUYMKLONuAXStDeoVCipH6PJ09Z5U2p
|
||||
V5p4IQRV6QBsNw9CISJFuHzkVYTH5ZxzN80Ru46vh4y2M0Nu8GQ9I085KoZkrf5e
|
||||
Cq53OZt9ISjHEw==
|
||||
</ds:X509Certificate>
|
||||
</ds:X509Data>
|
||||
</ds:KeyInfo>
|
||||
</md:KeyDescriptor>
|
||||
<md:SingleSignOnService
|
||||
Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
|
||||
Location="https://idp2.example.com/sso"/>
|
||||
</md:IDPSSODescriptor>
|
||||
<md:ContactPerson contactType="technical">
|
||||
<md:EmailAddress>mailto:technical.contact2@example.com</md:EmailAddress>
|
||||
</md:ContactPerson>
|
||||
</md:EntityDescriptor>
|
||||
</EntitiesDescriptor>
|
Loading…
Reference in New Issue