From 2ee455b7bf11999bd9d9d054732f3c2cd89f4d04 Mon Sep 17 00:00:00 2001 From: Josh Cummings Date: Fri, 25 Sep 2020 16:23:18 -0600 Subject: [PATCH] Add EntitiesDescriptor Support Closes gh-9051 --- ...gistrationBuilderHttpMessageConverter.java | 38 +++++++++++++---- ...ationBuilderHttpMessageConverterTests.java | 41 ++++++++++++++++++- 2 files changed, 68 insertions(+), 11 deletions(-) diff --git a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter.java index 1aefa5489d..167a19b8cf 100644 --- a/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter.java @@ -26,13 +26,15 @@ import java.util.List; import net.shibboleth.utilities.java.support.xml.ParserPool; import org.opensaml.core.config.ConfigurationService; +import org.opensaml.core.xml.XMLObject; import org.opensaml.core.xml.config.XMLObjectProviderRegistry; +import org.opensaml.core.xml.io.Unmarshaller; import org.opensaml.saml.common.xml.SAMLConstants; +import org.opensaml.saml.saml2.metadata.EntitiesDescriptor; import org.opensaml.saml.saml2.metadata.EntityDescriptor; import org.opensaml.saml.saml2.metadata.IDPSSODescriptor; import org.opensaml.saml.saml2.metadata.KeyDescriptor; import org.opensaml.saml.saml2.metadata.SingleSignOnService; -import org.opensaml.saml.saml2.metadata.impl.EntityDescriptorUnmarshaller; import org.opensaml.security.credential.UsageType; import org.opensaml.xmlsec.keyinfo.KeyInfoSupport; import org.w3c.dom.Document; @@ -82,7 +84,7 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter OpenSamlInitializationService.initialize(); } - private final EntityDescriptorUnmarshaller unmarshaller; + private final XMLObjectProviderRegistry registry; private final ParserPool parserPool; @@ -90,10 +92,8 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter * Creates a {@link OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter} */ public OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter() { - XMLObjectProviderRegistry registry = ConfigurationService.get(XMLObjectProviderRegistry.class); - this.unmarshaller = (EntityDescriptorUnmarshaller) registry.getUnmarshallerFactory() - .getUnmarshaller(EntityDescriptor.DEFAULT_ELEMENT_NAME); - this.parserPool = registry.getParserPool(); + this.registry = ConfigurationService.get(XMLObjectProviderRegistry.class); + this.parserPool = this.registry.getParserPool(); } @Override @@ -207,10 +207,30 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverter } private EntityDescriptor entityDescriptor(InputStream inputStream) { + Document document = document(inputStream); + Element element = document.getDocumentElement(); + Unmarshaller unmarshaller = this.registry.getUnmarshallerFactory().getUnmarshaller(element); + if (unmarshaller == null) { + throw new Saml2Exception("Unsupported element of type " + element.getTagName()); + } try { - Document document = this.parserPool.parse(inputStream); - Element element = document.getDocumentElement(); - return (EntityDescriptor) this.unmarshaller.unmarshall(element); + XMLObject object = unmarshaller.unmarshall(element); + if (object instanceof EntitiesDescriptor) { + return ((EntitiesDescriptor) object).getEntityDescriptors().get(0); + } + if (object instanceof EntityDescriptor) { + return (EntityDescriptor) object; + } + } + catch (Exception ex) { + throw new Saml2Exception(ex); + } + throw new Saml2Exception("Unsupported element of type " + element.getTagName()); + } + + private Document document(InputStream inputStream) { + try { + return this.parserPool.parse(inputStream); } catch (Exception ex) { throw new Saml2Exception(ex); diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests.java index 30ce7e3261..072f1cc472 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests.java +++ b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests.java @@ -36,8 +36,10 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests { private static final String CERTIFICATE = "MIIEEzCCAvugAwIBAgIJAIc1qzLrv+5nMA0GCSqGSIb3DQEBCwUAMIGfMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ08xFDASBgNVBAcMC0Nhc3RsZSBSb2NrMRwwGgYDVQQKDBNTYW1sIFRlc3RpbmcgU2VydmVyMQswCQYDVQQLDAJJVDEgMB4GA1UEAwwXc2ltcGxlc2FtbHBocC5jZmFwcHMuaW8xIDAeBgkqhkiG9w0BCQEWEWZoYW5pa0BwaXZvdGFsLmlvMB4XDTE1MDIyMzIyNDUwM1oXDTI1MDIyMjIyNDUwM1owgZ8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDTzEUMBIGA1UEBwwLQ2FzdGxlIFJvY2sxHDAaBgNVBAoME1NhbWwgVGVzdGluZyBTZXJ2ZXIxCzAJBgNVBAsMAklUMSAwHgYDVQQDDBdzaW1wbGVzYW1scGhwLmNmYXBwcy5pbzEgMB4GCSqGSIb3DQEJARYRZmhhbmlrQHBpdm90YWwuaW8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4cn62E1xLqpN34PmbrKBbkOXFjzWgJ9b+pXuaRft6A339uuIQeoeH5qeSKRVTl32L0gdz2ZivLwZXW+cqvftVW1tvEHvzJFyxeTW3fCUeCQsebLnA2qRa07RkxTo6Nf244mWWRDodcoHEfDUSbxfTZ6IExSojSIU2RnD6WllYWFdD1GFpBJOmQB8rAc8wJIBdHFdQnX8Ttl7hZ6rtgqEYMzYVMuJ2F2r1HSU1zSAvwpdYP6rRGFRJEfdA9mm3WKfNLSc5cljz0X/TXy0vVlAV95l9qcfFzPmrkNIst9FZSwpvB49LyAVke04FQPPwLgVH4gphiJH3jvZ7I+J5lS8VAgMBAAGjUDBOMB0GA1UdDgQWBBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAfBgNVHSMEGDAWgBTTyP6Cc5HlBJ5+ucVCwGc5ogKNGzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAvMS4EQeP/ipV4jOG5lO6/tYCb/iJeAduOnRhkJk0DbX329lDLZhTTL/x/w/9muCVcvLrzEp6PN+VWfw5E5FWtZN0yhGtP9R+vZnrV+oc2zGD+no1/ySFOe3EiJCO5dehxKjYEmBRv5sU/LZFKZpozKN/BMEa6CqLuxbzb7ykxVr7EVFXwltPxzE9TmL9OACNNyF5eJHWMRMllarUvkcXlh4pux4ks9e6zV9DQBy2zds9f1I3qxg0eX6JnGrXi/ZiCT+lJgVe3ZFXiejiLAiKB04sXW3ti0LW3lx13Y1YlQ4/tlpgTgfIJxKV6nyPiLoK0nywbMd+vpAirDt2Oc+hk"; - private static final String ENTITY_DESCRIPTOR_TEMPLATE = "\n" - + "\n%s"; + + private static final String ENTITY_DESCRIPTOR_TEMPLATE = "\n%s" + ""; @@ -112,6 +114,31 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests { .isEqualTo(x509Certificate(CERTIFICATE)); } + // gh-9051 + @Test + public void readWhenEntitiesDescriptorThenConfigures() throws Exception { + String payload = String.format(ENTITIES_DESCRIPTOR_TEMPLATE, + String.format(ENTITY_DESCRIPTOR_TEMPLATE, + String.format(IDP_SSO_DESCRIPTOR_TEMPLATE, + String.format(KEY_DESCRIPTOR_TEMPLATE, "use=\"signing\"") + + String.format(KEY_DESCRIPTOR_TEMPLATE, "use=\"encryption\"") + + String.format(SINGLE_SIGN_ON_SERVICE_TEMPLATE)))); + MockClientHttpResponse response = new MockClientHttpResponse(payload.getBytes(), HttpStatus.OK); + RelyingPartyRegistration registration = this.converter.read(RelyingPartyRegistration.Builder.class, response) + .registrationId("one").build(); + RelyingPartyRegistration.AssertingPartyDetails details = registration.getAssertingPartyDetails(); + assertThat(details.getWantAuthnRequestsSigned()).isFalse(); + assertThat(details.getSingleSignOnServiceLocation()).isEqualTo("sso-location"); + assertThat(details.getSingleSignOnServiceBinding()).isEqualTo(Saml2MessageBinding.REDIRECT); + assertThat(details.getEntityId()).isEqualTo("entity-id"); + assertThat(details.getVerificationX509Credentials()).hasSize(1); + assertThat(details.getVerificationX509Credentials().iterator().next().getCertificate()) + .isEqualTo(x509Certificate(CERTIFICATE)); + assertThat(details.getEncryptionX509Credentials()).hasSize(1); + assertThat(details.getEncryptionX509Credentials().iterator().next().getCertificate()) + .isEqualTo(x509Certificate(CERTIFICATE)); + } + @Test public void readWhenKeyDescriptorHasNoUseThenConfiguresBothKeyTypes() throws Exception { String payload = String.format(ENTITY_DESCRIPTOR_TEMPLATE, String.format(IDP_SSO_DESCRIPTOR_TEMPLATE, @@ -137,4 +164,14 @@ public class OpenSamlRelyingPartyRegistrationBuilderHttpMessageConverterTests { } } + // gh-9051 + @Test + public void readWhenUnsupportedElementThenSaml2Exception() { + String payload = ""; + MockClientHttpResponse response = new MockClientHttpResponse(payload.getBytes(), HttpStatus.OK); + assertThatExceptionOfType(Saml2Exception.class) + .isThrownBy(() -> this.converter.read(RelyingPartyRegistration.Builder.class, response)) + .withMessage("Unsupported element of type saml2:Assertion"); + } + }