Skip to content

Commit

Permalink
Init/Create SAML2 SP metadata at construction time (#1171)
Browse files Browse the repository at this point in the history
* init service provider metadata at construction time

* init service provider metadata at construction time
  • Loading branch information
mmoayyed committed Aug 14, 2018
1 parent 5dd3b70 commit b1eaca1
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 41 deletions.
@@ -1,11 +1,9 @@
package org.pac4j.saml.metadata;

import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.resolver.CriteriaSet;
import net.shibboleth.utilities.java.support.resolver.ResolverException;
import org.opensaml.core.criterion.EntityIdCriterion;
import org.opensaml.core.xml.XMLObject;
import org.opensaml.core.xml.io.MarshallingException;
import org.opensaml.saml.metadata.resolver.MetadataResolver;
import org.pac4j.core.exception.TechnicalException;
import org.pac4j.core.util.CommonHelper;
Expand All @@ -18,6 +16,7 @@

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
Expand Down Expand Up @@ -59,7 +58,11 @@ public SAML2ServiceProviderMetadataResolver(final SAML2ClientConfiguration confi
this.forceSpMetadataGeneration = configuration.isForceServiceProviderMetadataGeneration();
this.binding = configuration.getDestinationBindingType();

// If the spEntityId is blank, use the callback url
determineServiceProviderEntityId(callbackUrl);
prepareServiceProviderMetadata();
}

private void determineServiceProviderEntityId(final String callbackUrl) {
try {
if (CommonHelper.isBlank(this.spEntityId)) {
final URL url = new URL(callbackUrl);
Expand All @@ -75,9 +78,7 @@ public SAML2ServiceProviderMetadataResolver(final SAML2ClientConfiguration confi
}
}

@Override
public final MetadataResolver resolve() {

private MetadataResolver prepareServiceProviderMetadata() {
final boolean credentialProviderRequired = this.authnRequestSigned || this.wantsAssertionsSigned;
if (credentialProviderRequired && this.credentialProvider == null) {
throw new TechnicalException("Credentials Provider can not be null when authnRequestSigned or" +
Expand All @@ -100,46 +101,46 @@ public final MetadataResolver resolve() {
metadataGenerator.setAssertionConsumerServiceUrl(callbackUrl);
// for now same for logout url
metadataGenerator.setSingleLogoutServiceUrl(callbackUrl);
final MetadataResolver spMetadataProvider = metadataGenerator.buildMetadataResolver();

// Initialize metadata provider for our SP and get the XML as a String
this.spMetadata = metadataGenerator.getMetadata();
if (this.spMetadataResource != null) {
writeServiceProviderMetadataToResource();
return metadataGenerator.buildMetadataResolver();
} catch (final Exception e) {
throw new TechnicalException("Unable to generate metadata for service provider", e);
}
}

if (spMetadataResource.exists() && !this.forceSpMetadataGeneration) {
logger.info("Metadata file already exists at {}.", this.spMetadataResource.getFilename());
} else {
logger.info("Writing sp metadata to {}", this.spMetadataResource.getFilename());
final File parent = spMetadataResource.getFile().getParentFile();
if (parent != null) {
logger.info("Attempting to create directory structure for: {}", parent.getCanonicalPath());
if (!parent.exists() && !parent.mkdirs()) {
logger.warn("Could not construct the directory structure for SP metadata: {}",
parent.getCanonicalPath());
}
}
final Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
final StreamResult result = new StreamResult(new StringWriter());
final StreamSource source = new StreamSource(new StringReader(this.spMetadata));
transformer.transform(source, result);
try (final OutputStream spMetadataOutputStream = this.spMetadataResource.getOutputStream()) {
spMetadataOutputStream.write(result.getWriter().toString().getBytes(StandardCharsets.UTF_8));
private void writeServiceProviderMetadataToResource() throws IOException, TransformerException {
if (this.spMetadataResource != null) {
if (spMetadataResource.exists() && !this.forceSpMetadataGeneration) {
logger.info("Metadata file already exists at {}.", this.spMetadataResource.getFilename());
} else {
logger.info("Writing sp metadata to {}", this.spMetadataResource.getFilename());
final File parent = spMetadataResource.getFile().getParentFile();
if (parent != null) {
logger.info("Attempting to create directory structure for: {}", parent.getCanonicalPath());
if (!parent.exists() && !parent.mkdirs()) {
logger.warn("Could not construct the directory structure for SP metadata: {}",
parent.getCanonicalPath());
}
}
final Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
final StreamResult result = new StreamResult(new StringWriter());
final StreamSource source = new StreamSource(new StringReader(this.spMetadata));
transformer.transform(source, result);
try (final OutputStream spMetadataOutputStream = this.spMetadataResource.getOutputStream()) {
spMetadataOutputStream.write(result.getWriter().toString().getBytes(StandardCharsets.UTF_8));
}
}
return spMetadataProvider;
} catch (final ComponentInitializationException e) {
throw new TechnicalException("Error initializing spMetadataProvider", e);
} catch (final MarshallingException e) {
logger.warn("Unable to marshal SP metadata", e);
} catch (final IOException e) {
logger.warn("Unable to print SP metadata", e);
} catch (final Exception e) {
logger.warn("Unable to transform metadata", e);
}
return null;
}

@Override
public final MetadataResolver resolve() {
return prepareServiceProviderMetadata();
}

@Override
Expand Down
Expand Up @@ -4,22 +4,40 @@
import org.junit.Test;
import org.pac4j.core.exception.TechnicalException;
import org.pac4j.saml.client.SAML2ClientConfiguration;
import org.pac4j.saml.crypto.KeyStoreCredentialProvider;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;

import static org.junit.Assert.*;

public class SAML2ServiceProviderMetadataResolverTest {

private SAML2ServiceProviderMetadataResolver metadataResolver;

private SAML2ClientConfiguration configuration;

@Before
public void setUp() {
SAML2ClientConfiguration configuration = new SAML2ClientConfiguration();
configuration.setServiceProviderMetadataResource(new FileSystemResource("target"));
configuration = new SAML2ClientConfiguration();
configuration.setKeystorePath("target/keystore.jks");
configuration.setKeystorePassword("pac4j");
configuration.setPrivateKeyPassword("pac4j");
configuration.setServiceProviderMetadataResource(new FileSystemResource("target/out.xml"));
metadataResolver = new SAML2ServiceProviderMetadataResolver(configuration, "http://localhost", null);
configuration.setIdentityProviderMetadataResource(new ClassPathResource("idp-metadata.xml"));
configuration.init();
}

@Test(expected = TechnicalException.class)
public void resolveShouldThrowExceptionIfCredentialsProviderIsNullAndAuthnRequestSignedIsTrue() {
metadataResolver = new SAML2ServiceProviderMetadataResolver(configuration, "http://localhost", null);
metadataResolver.resolve();
}

@Test
public void resolveServiceProviderMetadata() {
metadataResolver = new SAML2ServiceProviderMetadataResolver(configuration, "http://localhost",
new KeyStoreCredentialProvider(configuration));
assertTrue(configuration.getServiceProviderMetadataResource().exists());
assertNotNull(metadataResolver.resolve());
}
}

0 comments on commit b1eaca1

Please sign in to comment.