Support Creating EntitiesDescriptor

Clsoes gh-12844
This commit is contained in:
Josh Cummings 2023-03-09 09:32:58 -07:00
parent 37b893a0f5
commit 7678523b73
3 changed files with 64 additions and 6 deletions

View File

@ -30,11 +30,13 @@ import org.opensaml.core.xml.XMLObjectBuilder;
import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
import org.opensaml.saml.common.xml.SAMLConstants;
import org.opensaml.saml.saml2.metadata.AssertionConsumerService;
import org.opensaml.saml.saml2.metadata.EntitiesDescriptor;
import org.opensaml.saml.saml2.metadata.EntityDescriptor;
import org.opensaml.saml.saml2.metadata.KeyDescriptor;
import org.opensaml.saml.saml2.metadata.NameIDFormat;
import org.opensaml.saml.saml2.metadata.SPSSODescriptor;
import org.opensaml.saml.saml2.metadata.SingleLogoutService;
import org.opensaml.saml.saml2.metadata.impl.EntitiesDescriptorMarshaller;
import org.opensaml.saml.saml2.metadata.impl.EntityDescriptorMarshaller;
import org.opensaml.security.credential.UsageType;
import org.opensaml.xmlsec.signature.KeyInfo;
@ -65,6 +67,8 @@ public final class OpenSamlMetadataResolver implements Saml2MetadataResolver {
private final EntityDescriptorMarshaller entityDescriptorMarshaller;
private final EntitiesDescriptorMarshaller entitiesDescriptorMarshaller;
private Consumer<EntityDescriptorParameters> entityDescriptorCustomizer = (parameters) -> {
};
@ -72,19 +76,40 @@ public final class OpenSamlMetadataResolver implements Saml2MetadataResolver {
this.entityDescriptorMarshaller = (EntityDescriptorMarshaller) XMLObjectProviderRegistrySupport
.getMarshallerFactory().getMarshaller(EntityDescriptor.DEFAULT_ELEMENT_NAME);
Assert.notNull(this.entityDescriptorMarshaller, "entityDescriptorMarshaller cannot be null");
this.entitiesDescriptorMarshaller = (EntitiesDescriptorMarshaller) XMLObjectProviderRegistrySupport
.getMarshallerFactory().getMarshaller(EntitiesDescriptor.DEFAULT_ELEMENT_NAME);
Assert.notNull(this.entitiesDescriptorMarshaller, "entitiesDescriptorMarshaller cannot be null");
}
@Override
public String resolve(RelyingPartyRegistration relyingPartyRegistration) {
EntityDescriptor entityDescriptor = build(EntityDescriptor.DEFAULT_ELEMENT_NAME);
entityDescriptor.setEntityID(relyingPartyRegistration.getEntityId());
SPSSODescriptor spSsoDescriptor = buildSpSsoDescriptor(relyingPartyRegistration);
entityDescriptor.getRoleDescriptors(SPSSODescriptor.DEFAULT_ELEMENT_NAME).add(spSsoDescriptor);
this.entityDescriptorCustomizer
.accept(new EntityDescriptorParameters(entityDescriptor, relyingPartyRegistration));
EntityDescriptor entityDescriptor = entityDescriptor(relyingPartyRegistration);
return serialize(entityDescriptor);
}
public String resolve(Iterable<RelyingPartyRegistration> relyingPartyRegistrations) {
Collection<EntityDescriptor> entityDescriptors = new ArrayList<>();
for (RelyingPartyRegistration registration : relyingPartyRegistrations) {
EntityDescriptor entityDescriptor = entityDescriptor(registration);
entityDescriptors.add(entityDescriptor);
}
if (entityDescriptors.size() == 1) {
return serialize(entityDescriptors.iterator().next());
}
EntitiesDescriptor entities = build(EntitiesDescriptor.DEFAULT_ELEMENT_NAME);
entities.getEntityDescriptors().addAll(entityDescriptors);
return serialize(entities);
}
private EntityDescriptor entityDescriptor(RelyingPartyRegistration registration) {
EntityDescriptor entityDescriptor = build(EntityDescriptor.DEFAULT_ELEMENT_NAME);
entityDescriptor.setEntityID(registration.getEntityId());
SPSSODescriptor spSsoDescriptor = buildSpSsoDescriptor(registration);
entityDescriptor.getRoleDescriptors(SPSSODescriptor.DEFAULT_ELEMENT_NAME).add(spSsoDescriptor);
this.entityDescriptorCustomizer.accept(new EntityDescriptorParameters(entityDescriptor, registration));
return entityDescriptor;
}
/**
* Set a {@link Consumer} for modifying the OpenSAML {@link EntityDescriptor}
* @param entityDescriptorCustomizer a consumer that accepts an
@ -184,6 +209,16 @@ public final class OpenSamlMetadataResolver implements Saml2MetadataResolver {
}
}
private String serialize(EntitiesDescriptor entities) {
try {
Element element = this.entitiesDescriptorMarshaller.marshall(entities);
return SerializeSupport.prettyPrintXML(element);
}
catch (Exception ex) {
throw new Saml2Exception(ex);
}
}
/**
* A tuple containing an OpenSAML {@link EntityDescriptor} and its associated
* {@link RelyingPartyRegistration}

View File

@ -35,4 +35,8 @@ public interface Saml2MetadataResolver {
*/
String resolve(RelyingPartyRegistration relyingPartyRegistration);
default String resolve(Iterable<RelyingPartyRegistration> relyingPartyRegistrations) {
return resolve(relyingPartyRegistrations.iterator().next());
}
}

View File

@ -16,6 +16,8 @@
package org.springframework.security.saml2.provider.service.metadata;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.security.saml2.core.TestSaml2X509Credentials;
@ -89,4 +91,21 @@ public class OpenSamlMetadataResolverTests {
assertThat(metadata).contains("<md:EntityDescriptor").contains("entityID=\"overriddenEntityId\"");
}
@Test
public void resolveIterableWhenRelyingPartiesThenMetadataMatches() {
RelyingPartyRegistration one = TestRelyingPartyRegistrations.full()
.assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT).build();
RelyingPartyRegistration two = TestRelyingPartyRegistrations.full().entityId("two")
.assertionConsumerServiceBinding(Saml2MessageBinding.REDIRECT).build();
OpenSamlMetadataResolver openSamlMetadataResolver = new OpenSamlMetadataResolver();
String metadata = openSamlMetadataResolver.resolve(List.of(one, two));
assertThat(metadata).contains("<md:EntitiesDescriptor").contains("<md:EntityDescriptor")
.contains("entityID=\"rp-entity-id\"").contains("two").contains("<md:KeyDescriptor use=\"signing\">")
.contains("<md:KeyDescriptor use=\"encryption\">")
.contains("<ds:X509Certificate>MIICgTCCAeoCCQCuVzyqFgMSyDANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBh")
.contains("Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"")
.contains("Location=\"https://rp.example.org/acs\" index=\"1\"")
.contains("ResponseLocation=\"https://rp.example.org/logout/saml2/response\"");
}
}