[java-identity-provider] branch main updated: IDP-2142 Use OpenSAML metadata generation code
Rod Widdowson
rdw at steadingsoftware.com
Mon Jul 17 13:05:09 UTC 2023
This is an automated email from the git hooks/post-receive script.
rdw pushed a commit to branch main
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=7da2feb7aa7a24990f83234ec1abe29daa716a60
The following commit(s) were added to refs/heads/main by this push:
new 7da2feb7a IDP-2142 Use OpenSAML metadata generation code
7da2feb7a is described below
commit 7da2feb7aa7a24990f83234ec1abe29daa716a60
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Fri Jul 14 14:14:34 2023 +0100
IDP-2142 Use OpenSAML metadata generation code
https://shibboleth.atlassian.net/browse/IDP-2142
First cut. Just exports SAML2 SOAP Artifacts and
Redirect, SimpleSign and Post SSO
---
idp-installer/pom.xml | 5 +
.../impl/InstalledMetadataParameters.java | 340 ++++++++
.../shibboleth/idp/installer/impl/V5Install.java | 66 +-
.../installer/metadata/impl/MetadataGenerator.java | 917 ---------------------
.../metadata/impl/MetadataGeneratorParameters.java | 266 ------
.../idp/installer/metadata/impl/package-info.java | 24 -
.../idp/installer/metadata-generator-ant.xml | 19 -
.../idp/installer/metadata-generator.xml | 33 +-
8 files changed, 410 insertions(+), 1260 deletions(-)
diff --git a/idp-installer/pom.xml b/idp-installer/pom.xml
index c5f63eb21..e587c6afa 100644
--- a/idp-installer/pom.xml
+++ b/idp-installer/pom.xml
@@ -74,6 +74,11 @@
<artifactId>opensaml-saml-api</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>${opensaml.groupId}</groupId>
+ <artifactId>opensaml-saml-impl</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>${opensaml.groupId}</groupId>
<artifactId>opensaml-security-api</artifactId>
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/impl/InstalledMetadataParameters.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/impl/InstalledMetadataParameters.java
new file mode 100644
index 000000000..cc20e7988
--- /dev/null
+++ b/idp-installer/src/main/java/net/shibboleth/idp/installer/impl/InstalledMetadataParameters.java
@@ -0,0 +1,340 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.installer.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.Namespace;
+import org.opensaml.core.xml.XMLObjectBuilderFactory;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.common.xml.SAMLConstants;
+import org.opensaml.saml.ext.saml2mdui.Logo;
+import org.opensaml.saml.ext.saml2mdui.impl.LogoBuilder;
+import org.opensaml.saml.metadata.generator.impl.ArtifactResolutionServiceConverter;
+import org.opensaml.saml.metadata.generator.impl.SingleLogoutServiceConverter;
+import org.opensaml.saml.metadata.generator.impl.SingleSignOnServiceConverter;
+import org.opensaml.saml.metadata.generator.impl.TemplateMetadataGeneratorParameters;
+import org.opensaml.saml.saml2.metadata.Endpoint;
+import org.opensaml.saml.saml2.metadata.IDPSSODescriptor;
+import org.opensaml.saml.saml2.metadata.IndexedEndpoint;
+import org.opensaml.security.x509.X509Support;
+import org.slf4j.Logger;
+
+import net.shibboleth.idp.saml.xmlobject.ExtensionsConstants;
+import net.shibboleth.shared.annotation.constraint.Live;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+import net.shibboleth.shared.codec.Base64Support;
+import net.shibboleth.shared.codec.EncodingException;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.collection.Pair;
+import net.shibboleth.shared.component.AbstractInitializableComponent;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.resource.Resource;
+
+/**
+ * Parameters to metadata generation
+ */
+public class InstalledMetadataParameters extends AbstractInitializableComponent implements TemplateMetadataGeneratorParameters {
+
+ /** Logger. */
+ private final Logger log = LoggerFactory.getLogger(InstalledMetadataParameters.class);
+
+ /**
+ * The file with the certificate the IDP uses to encrypt.
+ */
+ @Nullable private File encryptionCert;
+
+ /**
+ * The file with the certificate that TLS uses to 'sign'.
+ */
+ @Nullable private File backChannelCert;
+
+ /**
+ * The file with the certificate the IDP uses to sign.
+ */
+ @Nullable private File signingCert;
+
+ /** The entityID. */
+ @NonnullAfterInit private String entityID;
+
+ /** The DNS name. */
+ @NonnullAfterInit private String dnsName;
+
+ /** The scope. */
+ @Nullable private String scope;
+
+ /*
+ * Static settings.
+ */
+ /** logout services. */
+ final @Nonnull List<Pair<String, String>> logoutServices = CollectionSupport.singletonList(
+ new Pair<>("SOAP/","/idp/profile/SAML2/SOAP/ArtifactResolution"));
+
+ /** sso services. */
+ final @Nonnull List<Pair<String, String>> ssoServices = CollectionSupport.listOf(
+ new Pair<>("SimpleSign/","/idp/profile/SAML2/POST-SimpleSign/SSO"),
+ new Pair<>("Redirect/","/idp/profile/SAML2/Redirect/SSO"),
+ new Pair<>("POST/","idp/profile/SAML2/POST/SSO"));
+
+ /** artifact services. */
+ final @Nonnull List<Pair<String, String>> artifactServices = CollectionSupport.emptyList();
+
+ /** {@inheritDoc} */
+ protected void doInitialize() throws ComponentInitializationException {
+ if (entityID == null || entityID.isEmpty()) {
+ throw new ComponentInitializationException("Entity ID not specified");
+ }
+ if (dnsName == null || dnsName.isEmpty()) {
+ throw new ComponentInitializationException("DNS name not specified");
+ }
+ }
+
+ /**
+ * Set the encryption Certificate file.
+ *
+ * @param resource what to set.
+ */
+ public void setEncryptionCertResource(@Nonnull final Resource resource) {
+
+ try {
+ encryptionCert = resource.getFile();
+ } catch (final IOException e) {
+ log.error("Could not open encryption cert", e);
+ encryptionCert = null;
+ }
+ }
+
+ /**
+ * Set the signing Certificate file.
+ *
+ * @param resource what to set.
+ */
+ public void setSigningCertResource(@Nonnull final Resource resource) {
+ try {
+ signingCert = resource.getFile();
+ } catch (final IOException e) {
+ log.error("Could not open signing cert", e);
+ signingCert = null;
+ }
+ }
+
+ /**
+ * Set the Backchannel Certificate.
+ *
+ * @param resource what to set.
+ */
+ public void setBackchannelCertResource(@Nonnull final Resource resource) {
+ try {
+ backChannelCert = resource.getFile();
+ } catch (final IOException e) {
+ log.error("Could not open back channel cert", e);
+ backChannelCert = null;
+ }
+ }
+
+ /**
+ * Load a certificate from a file in a standard format and produce a base64-encoded DER string.
+ *
+ * @param file certificate file
+ *
+ * @return encoded string
+ */
+ @Nullable private String getEncodedCertificate(@Nullable final File file) {
+ if (file == null) {
+ return null;
+ }
+ try {
+ final byte[] cert = X509Support.decodeCertificate(file).getEncoded();
+ assert cert != null;
+ return Base64Support.encode(cert, true);
+ } catch (final CertificateException | EncodingException e) {
+ log.warn("Unable to decode and re-encode certificate at path {}", file, e);
+ return null;
+ }
+ }
+
+ /**
+ * Sets the entityID.
+ *
+ * @param id what to set.
+ */
+ public void setEntityID(@Nonnull final String id) {
+ entityID = id;
+ }
+
+ /**
+ * Sets the dns name.
+ *
+ * @param name what to set.
+ */
+ public void setDnsName(@Nonnull final String name) {
+ dnsName = name;
+ }
+
+ /**
+ * Sets the scope.
+ *
+ * @param value what to set.
+ */
+ public void setScope(@Nullable final String value) {
+ scope = value;
+ }
+
+ // Methods for the interface
+
+ /** {@inheritDoc} */
+ @Nullable
+ public String getEntityID() {
+ return entityID;
+ }
+
+ /** {@inheritDoc} */
+ @Nullable public Set<Namespace> getAdditionalNamespaces() {
+ final Set<Namespace> namespaces = new HashSet<>();
+ namespaces.add(new Namespace(SAMLConstants.SAML20MDUI_NS, SAMLConstants.SAML20MDUI_PREFIX));
+ if (scope != null) {
+ namespaces.add(new Namespace(ExtensionsConstants.SHIB_MDEXT10_NS, ExtensionsConstants.SHIB_MDEXT10_PREFIX));
+ }
+ return namespaces;
+ }
+
+ /**
+ * Convert the expressions into endpoints.
+ *
+ * @param <T> endpoint type
+ * @param converter endpoint converter
+ * @param protocols accumulator for protocol support values
+ * @param input raw argument list
+ *
+ * @return converted endpoints
+ */
+ @Nonnull private <T extends Endpoint> Collection<T> convertEndpoints(
+ @Nonnull final BiFunction<String,List<String>,T> converter,
+ @Nonnull @Live final List<String> protocols,
+ @Nonnull final Collection<Pair<String,String>> input) {
+ return input
+ .stream()
+ .map(p -> converter.apply(new StringBuffer(p.getFirst()).append(dnsName).append(p.getSecond()).toString(),
+ protocols))
+ .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
+ }
+
+ /** {@inheritDoc} */
+ @Nullable
+ public IDPSSODescriptor getIDPSSODescriptor() {
+ final XMLObjectBuilderFactory bf = XMLObjectProviderRegistrySupport.getBuilderFactory();
+ final SAMLObjectBuilder<IDPSSODescriptor> idpSSOBuilder =
+ (SAMLObjectBuilder<IDPSSODescriptor>) bf.<IDPSSODescriptor>ensureBuilder(
+ IDPSSODescriptor.DEFAULT_ELEMENT_NAME);
+ final IDPSSODescriptor role = idpSSOBuilder.buildObject();
+
+ final List<String> protocols = new ArrayList<>();
+
+ role.getSingleLogoutServices().addAll(
+ convertEndpoints(new SingleLogoutServiceConverter(), protocols, logoutServices));
+ role.getSingleSignOnServices().addAll(
+ convertEndpoints(new SingleSignOnServiceConverter(), protocols, ssoServices));
+ role.getArtifactResolutionServices().addAll(
+ convertEndpoints(new ArtifactResolutionServiceConverter(), protocols, artifactServices));
+
+ int index = 1;
+ for (final IndexedEndpoint e : role.getArtifactResolutionServices()) {
+ e.setIndex(index++);
+ }
+ protocols.forEach(role::addSupportedProtocol);
+ return role;
+ }
+
+ /** {@inheritDoc} */
+ @Nonnull @Unmodifiable @NotLive
+ public List<String> getSigningCertificates() {
+ final List<String> result = new ArrayList<>(2);
+ String cert = getEncodedCertificate(backChannelCert);
+ if (cert != null) {
+ result.add(cert);
+ }
+ cert = getEncodedCertificate(signingCert);
+ if (cert != null) {
+ result.add(cert);
+ }
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Nonnull @Unmodifiable @NotLive
+ public List<String> getEncryptionCertificates() {
+ final String cert = getEncodedCertificate(encryptionCert);
+ if (cert == null) {
+ return CollectionSupport.emptyList();
+ }
+ return CollectionSupport.singletonList(cert);
+ }
+
+ /** {@inheritDoc} */
+ @Nullable
+ public String getLang() {
+ return "en";
+ }
+
+ /** {@inheritDoc} */
+ @Nullable
+ public String getDisplayName() {
+ return new StringBuffer("A name for the IdP at ").append(dnsName).toString();
+ }
+
+ /** {@inheritDoc} */
+ @Nullable
+ public String getDescription() {
+ return new StringBuffer("Enter a description for the IdP at ").append(dnsName).toString();
+ }
+
+ /** {@inheritDoc} */
+ @Nullable
+ public Logo getLogo() {
+ final Logo result = new LogoBuilder().buildObject();
+ result.setHeight(80);
+ result.setWidth(80);
+ result.setURI(new StringBuffer("https://").append(dnsName).append("/path/to/logo.png").toString());
+ return result;
+ }
+
+ /** {@inheritDoc} */
+ @Nonnull @Unmodifiable @NotLive public List<String> getScopes() {
+ final String s = scope;
+ if (s == null) {
+ return CollectionSupport.emptyList();
+ }
+ return CollectionSupport.singletonList(s);
+ }
+}
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/impl/V5Install.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/impl/V5Install.java
index b787b6440..cdec6b6cf 100644
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/impl/V5Install.java
+++ b/idp-installer/src/main/java/net/shibboleth/idp/installer/impl/V5Install.java
@@ -26,6 +26,7 @@ import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStream;
+import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
@@ -41,6 +42,10 @@ import javax.annotation.Nullable;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.tools.ant.BuildException;
+import org.apache.velocity.app.VelocityEngine;
+import org.opensaml.core.config.InitializationException;
+import org.opensaml.core.config.InitializationService;
+import org.opensaml.saml.metadata.generator.impl.VelocityMetadataGenerator;
import org.opensaml.security.httpclient.HttpClientSecurityParameters;
import org.slf4j.Logger;
import org.springframework.context.ApplicationContextInitializer;
@@ -52,8 +57,6 @@ import org.springframework.core.io.Resource;
import net.shibboleth.idp.Version;
import net.shibboleth.idp.installer.InstallerSupport;
import net.shibboleth.idp.installer.PropertiesWithComments;
-import net.shibboleth.idp.installer.metadata.impl.MetadataGenerator;
-import net.shibboleth.idp.installer.metadata.impl.MetadataGeneratorParameters;
import net.shibboleth.idp.installer.plugin.impl.PluginState;
import net.shibboleth.idp.module.IdPModule;
import net.shibboleth.idp.plugin.IdPPlugin;
@@ -87,9 +90,6 @@ public class V5Install {
/** Key Manager. */
@Nonnull private final KeyManagement keyManager;
- /** What will generate metadata? */
- @Nonnull private final MetadataGenerator metadataGenerator = new MetadataGenerator();
-
/** The HttpClient to use.*/
@Nonnull private final HttpClient httpClient;
@@ -101,7 +101,7 @@ public class V5Install {
* @param installState The current install.
* @param client {@link HttpClient} to use on any Plugin operations
* @param securityParams {@link HttpClientSecurityParameters} to use on any Plugin operations
- *
+ *
*/
public V5Install(@Nonnull final InstallerProperties props, @Nonnull final CurrentInstallState installState,
@Nonnull final HttpClient client, @Nullable final HttpClientSecurityParameters securityParams) {
@@ -130,8 +130,8 @@ public class V5Install {
generateMetadata();
reprotect();
}
-
- /** Check for any preconditions to the install.
+
+ /** Check for any preconditions to the install.
* @throws BuildException if one is broken.
*/
protected void checkPreConditions() throws BuildException {
@@ -156,7 +156,7 @@ public class V5Install {
}
}
- /** Report the to be installed and (if there is one) current versions.
+ /** Report the to be installed and (if there is one) current versions.
* Write to be installed version to the dist folder.
* @throws BuildException if the write fails
*/
@@ -285,7 +285,7 @@ public class V5Install {
final Path secrets = installerProps.getTargetDir().resolve("credentials").resolve("secrets.properties");
try (final FileWriter fileWriter = new FileWriter(secrets.toFile());
final BufferedWriter out = new BufferedWriter(fileWriter)) {
-
+
out.write("# This is a reserved spot for most properties containing passwords or other secrets.");
out.newLine();
out.write("# Created by install at " + Instant.now());
@@ -398,7 +398,7 @@ public class V5Install {
}
}
-
+
/** ReEnable modules which were already enabled.
* @throws BuildException if badness occurs
*/
@@ -466,26 +466,42 @@ public class V5Install {
log.debug("Metadata file {} exists", metadataFile.toString());
return;
}
+ try {
+ InitializationService.initialize();
+ } catch (InitializationException e) {
+ log.error("Could not intiailize opensaml", e);
+ throw new BuildException(e);
+ }
+
final Resource resource = new ClassPathResource("net/shibboleth/idp/installer/metadata-generator.xml");
final GenericApplicationContext context = new ApplicationContextBuilder()
- .setName(MetadataGenerator.class.getName())
+ .setName(V5Install.class.getName())
.setServiceConfigurations(CollectionSupport.singletonList(resource))
.setContextInitializer(new Initializer())
.build();
- final MetadataGeneratorParameters parameters = context.getBean("IdPConfiguration",
- MetadataGeneratorParameters.class);
-
log.info("Creating Metadata to {}", metadataFile);
+ final InstalledMetadataParameters parameters = context.getBean("IdPConfiguration", InstalledMetadataParameters.class);
+ parameters.setDnsName(installerProps.getHostName());
+ final VelocityEngine engine = context.getBean("VelocityEngine", VelocityEngine.class);
log.debug("Parameters {}", parameters);
- metadataGenerator.setOutput(metadataFile);
- metadataGenerator.setParameters(parameters);
- try {
- metadataGenerator.initialize();
- } catch (final ComponentInitializationException e) {
+ try (final Writer sink = new FileWriter(metadataFile)) {
+ sink.write("<!--\n This is example metadata only. Do *NOT* supply it as is without review,\n" +
+ "and do *NOT* provide it in real time to your partners.\n" +
+ "This metadata is not dynamic - it will not change as your configuration changes.\n" +
+ "On Demand Metadata Generation available from the metadatagen plugin.\n-->\n");
+ final VelocityMetadataGenerator generator = new VelocityMetadataGenerator();
+ generator.setId("Installer Metadata Generator");
+ generator.setVelocityEngine(engine);
+ generator.initialize();
+ generator.generate(parameters, sink);
+ } catch (ComponentInitializationException e) {
+ log.error("Metadata Generator initialization failed", e);
+ throw new BuildException(e);
+ } catch (IOException e) {
+ log.error("Metadata Generator failed to write to", metadataFile, e);
throw new BuildException(e);
}
- metadataGenerator.generate();
}
/** Set the protection on the files.
@@ -532,7 +548,7 @@ public class V5Install {
/** Did we create idp-backchannel.*?*/
private boolean createdBackchannel;
-
+
/** Did we create sealer.*?*/
private boolean createdSealer;
@@ -552,7 +568,7 @@ public class V5Install {
/**
* Helper method for {@link #execute()} to generate a crt and key file.
- *
+ *
* @param fileBase the partial file name
* @return true iff the file pair was created
* @throws BuildException if badness occurrs.
@@ -597,7 +613,7 @@ public class V5Install {
/**
* Helper method for {@link #execute()} to generate the backchannel keystore.
- *
+ *
* @throws BuildException if badness occurs.
*/
private void generateKeyStore() {
@@ -639,7 +655,7 @@ public class V5Install {
/**
* Helper method for {@link #execute()} to generate the Sealer.
- *
+ *
* @throws BuildException if badness occurs.
*/
private void generateSealer() {
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/metadata/impl/MetadataGenerator.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/metadata/impl/MetadataGenerator.java
deleted file mode 100644
index cbc6a1e04..000000000
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/metadata/impl/MetadataGenerator.java
+++ /dev/null
@@ -1,917 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You under the Apache
- * License, Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package net.shibboleth.idp.installer.metadata.impl;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.apache.tools.ant.BuildException;
-import org.opensaml.core.xml.LangBearing;
-import org.opensaml.saml.common.xml.SAMLConstants;
-import org.opensaml.saml.ext.reqattr.RequestedAttributes;
-import org.opensaml.saml.ext.saml2mdui.Description;
-import org.opensaml.saml.ext.saml2mdui.DisplayName;
-import org.opensaml.saml.ext.saml2mdui.Logo;
-import org.opensaml.saml.ext.saml2mdui.UIInfo;
-import org.opensaml.saml.saml2.core.Extensions;
-import org.opensaml.saml.saml2.metadata.ArtifactResolutionService;
-import org.opensaml.saml.saml2.metadata.AttributeAuthorityDescriptor;
-import org.opensaml.saml.saml2.metadata.AttributeService;
-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.SingleLogoutService;
-import org.opensaml.saml.saml2.metadata.SingleSignOnService;
-import org.opensaml.xmlsec.signature.KeyInfo;
-import org.opensaml.xmlsec.signature.X509Certificate;
-import org.opensaml.xmlsec.signature.X509Data;
-import org.opensaml.xmlsec.signature.support.SignatureConstants;
-
-import net.shibboleth.idp.saml.xmlobject.ExtensionsConstants;
-import net.shibboleth.idp.saml.xmlobject.Scope;
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.component.AbstractInitializableComponent;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.xml.DOMTypeSupport;
-import net.shibboleth.shared.xml.XMLConstants;
-
-/**
- * This class gathers information which it then uses to generate IdP Metadata. Loosely based on the SP metadata
- * generator, and the V2 metadata.
- */
-public class MetadataGenerator extends AbstractInitializableComponent {
-
- /**
- * The end points we understand.
- */
- enum Endpoints {
- /** IDPSSODescriptor. Artifact (SAML1). */
- SAML1Artifact,
- /** IDPSSODescriptor. Artifact (SAML2). */
- SAML2Artifact,
- /** IDPSSODescriptor. SLO (Redirect) */
- RedirectSLO,
- /** IDPSSODescriptor. SLO (Post) */
- POSTSLO,
- /** IDPSSODescriptor. SLO (Post Simple Sign) */
- POSTSimpleSignSLO,
- /** IDPSSODescriptor. SLO (Soap) */
- SOAPSLO,
- /** IDPSSODescriptor. SSO (Shibboleth protocol)*/
- ShibbolethSSO,
- /** IDPSSODescriptor. SSO (SAML2 Post)*/
- POSTSSO,
- /** IDPSSODescriptor. SSO (SAML2 Post Simple Sign)*/
- POSTSimpleSignSSO,
- /** IDPSSODescriptor. SSO (SAML2 Redirectr)*/
- RedirectSSO,
- /** AttributeAuthorityDescriptor. (SAML1=)*/
- SAML1Query,
- /** AttributeAuthorityDescriptor. (SAML2) */
- SAML2Query,
- }
-
- /**
- * Those endpoints which require a backchannel.
- */
- @Nonnull private static final Set<Endpoints> BACKCHANNEL_ENDPOINTS = CollectionSupport.setOf(
- Endpoints.SAML1Artifact, Endpoints.SAML2Artifact, Endpoints.SOAPSLO, Endpoints.SAML1Query,
- Endpoints.SAML2Query);
-
- /**
- * the Artifact endpoints.
- */
- @Nonnull private static final Set<Endpoints> ARTIFACT_ENDPOINTS = CollectionSupport.setOf(Endpoints.SAML1Artifact,
- Endpoints.SAML2Artifact);
-
- /**
- * the SSO endpoints.
- */
- @Nonnull private static final Set<Endpoints> SSO_ENDPOINTS = CollectionSupport.setOf(Endpoints.ShibbolethSSO,
- Endpoints.POSTSSO, Endpoints.POSTSimpleSignSSO, Endpoints.RedirectSSO);
-
- /**
- * the SLO endpoints.
- */
- @Nonnull private static final Set<Endpoints> SLO_ENDPOINTS = CollectionSupport.setOf(Endpoints.RedirectSLO,
- Endpoints.POSTSLO, Endpoints.POSTSimpleSignSLO, Endpoints.SOAPSLO);
-
- /**
- * AttributeAuthority endpoints.
- */
- @Nonnull private static final Set<Endpoints> AA_ENDPOINTS = CollectionSupport.setOf(Endpoints.SAML1Query,
- Endpoints.SAML2Query);
-
- /**
- * Which endpoints to generate.
- */
- @NonnullAfterInit private EnumSet<Endpoints> endpoints;
-
- /**
- * Whether to comment out the SAML2 AA endpoint.
- */
- private boolean saml2AttributeQueryCommented = true;
-
- /**
- * Whether to comment out the SAML2 SLO endpoints.
- */
- private boolean saml2LogoutCommented = true;
-
- /** Whether SAML1 is commented out. */
- private boolean saml1Commented = true;
-
- /** Comment depth. */
- private int commentDepth;
-
- /**
- * Where to write to - as {@link BufferedWriter}.
- */
- @NonnullAfterInit private BufferedWriter writer;
-
- /**
- * Where to write to - as {@link File}.
- */
- @Nullable private File output;
-
- /** The parameters. */
- private MetadataGeneratorParameters params;
-
- /** {@inheritDoc} */
- protected void doInitialize() throws ComponentInitializationException {
- try {
- final FileOutputStream outStream;
- outStream = new FileOutputStream(output);
- writer = new BufferedWriter(new OutputStreamWriter(outStream));
- } catch (final FileNotFoundException e) {
- throw new ComponentInitializationException(e);
- }
- endpoints = EnumSet.allOf(Endpoints.class);
- }
-
- /** Set where to write the metadata.
- * @param file what to set.
- */
- public void setOutput(@Nonnull final File file) {
- checkSetterPreconditions();
- output = Constraint.isNotNull(file, "provided file must be nonnull");
- }
-
- /** Set a description of the IdP.
- * @param what what to set. This component does not have to be initialized.
- */
- public void setParameters(@Nonnull final MetadataGeneratorParameters what) {
- checkSetterPreconditions();
- params = Constraint.isNotNull(what, "provided params must be nonnull");
- }
-
- /**
- * remove back channel endpoints.
- */
- public void removeBackChannel() {
- endpoints.removeAll(BACKCHANNEL_ENDPOINTS);
- }
-
- /**
- * Get the Endpoints.
- *
- * @return Returns the Endpoints
- */
- public EnumSet<Endpoints> getEndpoints() {
- return endpoints;
- }
-
- /**
- * Set the Endpoints.
- *
- * @param points what to set.
- */
- public void setEndpoints(@Nonnull final EnumSet<Endpoints> points) {
- endpoints = Constraint.isNotNull(points, "supplied endpoints should not be null");
- }
-
- /**
- * Returns whether to comment the SAML2 AA endpoint.
- *
- * @return whether to comment the SAML2 AA endpoint
- */
- public boolean isSAML2AttributeQueryCommented() {
- return saml2AttributeQueryCommented;
- }
-
- /**
- * Sets whether to comment the SAML2 AA endpoint.
- *
- * @param asComment whether to comment or not.
- */
- public void setSAML2AttributeQueryCommented(final boolean asComment) {
- saml2AttributeQueryCommented = asComment;
- }
-
- /**
- * Returns whether to comment SAML1 endpoints.
- *
- * @return whether to comment SAML1 endpoints
- */
- public boolean isSAML1Commented() {
- return saml1Commented;
- }
-
- /**
- * Sets whether to comment the comment SAML1 endpoints.
- *
- * @param asComment whether to comment or not.
- */
- public void setSAML1Commented(final boolean asComment) {
- saml1Commented= asComment;
- }
-
-
- /**
- * Returns whether to comment the SAML2 Logout endpoints.
- *
- * @return whether to comment the SAML2 Logout endpoints
- */
- public boolean isSAML2LogoutCommented() {
- return saml2LogoutCommented;
- }
-
- /**
- * Sets whether to comment the SAML2 Logout endpoints.
- *
- * @param asComment whether to comment or not
- */
- public void setSAML2LogoutCommented(final boolean asComment) {
- saml2LogoutCommented = asComment;
- }
-
- /** Generate the metadata given the parameters.
- * @throws BuildException if badness occurs.
- */
- public void generate() throws BuildException {
- checkComponentActive();
- try {
- writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
- writer.newLine();
- writeComments();
- writer.write("<");
- writer.write(EntityDescriptor.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(' ');
- writeNameSpace(null, SAMLConstants.SAML20MD_NS);
- writeNameSpace(SignatureConstants.XMLSIG_PREFIX, SignatureConstants.XMLSIG_NS);
- writeNameSpace(ExtensionsConstants.SHIB_MDEXT10_PREFIX, ExtensionsConstants.SHIB_MDEXT10_NS);
- writeNameSpace(XMLConstants.XML_PREFIX, XMLConstants.XML_NS);
- writeNameSpace(SAMLConstants.SAML20MDUI_PREFIX, SAMLConstants.SAML20MDUI_NS);
- writeNameSpace(SAMLConstants.SAML20PREQ_ATTRR_PREFIX, SAMLConstants.SAML20PREQ_ATTR_NS);
-
- writer.write(" validUntil=\"" + DOMTypeSupport.instantToString(Instant.now()) + "\"");
-
- writer.write(" entityID=\"");
- writer.write(params.getEntityID());
- writer.write("\">");
- writer.newLine();
- writer.newLine();
-
-
- writeIDPSSO();
- writer.newLine();
- writer.newLine();
- writeAttributeAuthorityDescriptor();
- writer.newLine();
- writer.write("</EntityDescriptor>");
- writer.newLine();
- writer.flush();
- writer.close();
- } catch (final IOException e) {
- try {
- writer.close();
- } catch (final IOException e1) {
- // Ignore
- }
- throw new BuildException(e);
- }
- }
-
- /**
- * Add appropriate comments to metadata header.
- *
- * @throws IOException if badness occurs in the writer
- */
- protected void writeComments() throws IOException {
- openComment();
- writer.newLine();
- writer.write(" This is example metadata only. Do *NOT* supply it as is without review,");
- writer.newLine();
- writer.write(" and do *NOT* provide it in real time to your partners.");
- writer.newLine();
- writer.newLine();
- writer.write(" This metadata is not dynamic - it will not change as your configuration changes.");
- writer.write(" On Demand Metadata Generation available from the metadatagen plugin.");
- writer.newLine();
- closeComment();
- writer.newLine();
- }
-
- /**
- * Writeout a prefix/namespace pair.
- *
- * @param prefix the prefix, or null
- * @param name the namespace
- * @throws IOException if badness happens
- */
- protected void writeNameSpace(@Nullable final String prefix, @Nonnull final String name) throws IOException {
- writer.write(" xmlns");
- if (null != prefix) {
- writer.write(':');
- writer.write(prefix);
- }
- writer.write("=\"");
- writer.write(name);
- writer.write("\"");
- }
-
- /**
- * Write the <IDPSSODescriptor>.
- *
- * @throws IOException if badness happens
- */
- protected void writeIDPSSO() throws IOException {
- final List<String> protocols;
- if (isSAML1Commented()) {
- protocols = CollectionSupport.singletonList(SAMLConstants.SAML20P_NS);
- } else {
- protocols = CollectionSupport.listOf(SAMLConstants.SAML20P_NS, SAMLConstants.SAML11P_NS, "urn:mace:shibboleth:1.0");
- }
-
- writeRoleDescriptor(IDPSSODescriptor.DEFAULT_ELEMENT_LOCAL_NAME, protocols);
- writer.newLine();
- openExtensions();
- writeScope();
- writeMDUI();
- closeExtensions();
- writer.newLine();
- writeKeyDescriptors();
- for (final Endpoints endpoint : ARTIFACT_ENDPOINTS) {
- if (getEndpoints().contains(endpoint)) {
- outputEndpoint(endpoint);
- }
- }
- writer.newLine();
- if (isSAML2LogoutCommented()) {
- openComment();
- writer.newLine();
- }
- for (final Endpoints endpoint : SLO_ENDPOINTS) {
- if (getEndpoints().contains(endpoint)) {
- outputEndpoint(endpoint);
- }
- }
- if (isSAML2LogoutCommented()) {
- closeComment();
- writer.newLine();
- }
-
- writer.newLine();
- for (final Endpoints endpoint : SSO_ENDPOINTS) {
- if (getEndpoints().contains(endpoint)) {
- outputEndpoint(endpoint);
- }
- }
- writer.newLine();
- writer.write(" </");
- writer.write(IDPSSODescriptor.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(">");
- writer.newLine();
- }
-
- /**
- * Write the <AttributeAuthorityDescriptor>.
- *
- * @throws IOException if badness happens
- */
- private void writeAttributeAuthorityDescriptor() throws IOException {
- if (isSAML2AttributeQueryCommented() && isSAML1Commented()) {
- openComment();
- }
- final List<String> protocols;
- if (isSAML2AttributeQueryCommented()) {
- protocols = CollectionSupport.singletonList(SAMLConstants.SAML11P_NS);
- } else {
- protocols = CollectionSupport.listOf(SAMLConstants.SAML20P_NS, SAMLConstants.SAML11P_NS);
- }
- writeRoleDescriptor(AttributeAuthorityDescriptor.DEFAULT_ELEMENT_LOCAL_NAME, protocols);
- writer.newLine();
- openExtensions();
- writeScope();
- closeExtensions();
- writer.newLine();
- writeKeyDescriptors();
- for (final Endpoints endpoint : AA_ENDPOINTS) {
- if (getEndpoints().contains(endpoint)) {
- outputEndpoint(endpoint);
- }
- }
- writer.newLine();
- writer.write(" </");
- writer.write(AttributeAuthorityDescriptor.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- if (isSAML2AttributeQueryCommented() && isSAML1Commented()) {
- closeComment();
- }
- writer.newLine();
- }
-
- /**
- * Write out an role descriptor.
- *
- * @param name the name
- * @param protocols the supported protocols
- * @throws IOException when badness happebns
- */
- protected void writeRoleDescriptor(final String name, final List<String> protocols) throws IOException {
- writer.write(" <");
- writer.write(name);
- writer.write(" protocolSupportEnumeration=\"");
- boolean first = true;
- for (final String protocol : protocols) {
- if (!first) {
- writer.write(" ");
- }
- writer.write(protocol);
- first = false;
- }
- writer.write("\">");
- writer.newLine();
- }
-
- /**
- * Write the open <Extensions> elements.
- *
- * @throws IOException if badness happens
- */
- protected void openExtensions() throws IOException {
-
- writer.write(" <");
- writer.write(Extensions.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- writer.newLine();
- }
-
- /**
- * Write out the close <\Extensions> Element.
- *
- * @throws IOException if badness happens
- */
- protected void closeExtensions() throws IOException {
-
- writer.write(" </");
- writer.write(Extensions.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- writer.newLine();
- }
-
- /**
- * Write out the <shibmd:Scope> element.
- *
- * @throws IOException if badness happens
- */
- protected void writeScope() throws IOException {
- final String scope = params.getScope();
- if (null == scope || scope.isEmpty()) {
- return;
- }
-
- writer.write(" <");
- writeNameSpaceQualified(ExtensionsConstants.SHIB_MDEXT10_PREFIX, Scope.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(" regexp=\"false\">");
- writer.write(params.getScope());
- writer.write("</");
- writeNameSpaceQualified(ExtensionsConstants.SHIB_MDEXT10_PREFIX, Scope.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- writer.newLine();
- }
-
- /**
- * Write out the <mdui:UIINFO> element and children.
- *
- * @throws IOException if badness happens
- */
- protected void writeMDUI() throws IOException {
- openComment();
- writer.newLine();
- writer.write(" Fill in the details for your IdP here ");
- writer.newLine();
- writer.newLine();
-
- writer.write(" <");
- writeNameSpaceQualified(SAMLConstants.SAML20MDUI_PREFIX, UIInfo.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- writer.newLine();
-
- // DisplayName
- writer.write(" <");
- writeNameSpaceQualified(SAMLConstants.SAML20MDUI_PREFIX, DisplayName.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(' ');
- writeLangAttribute("en");
- writer.write('>');
- writer.write("A Name for the IdP at ");
- writer.write(params.getDnsName());
- writer.write("</");
- writeNameSpaceQualified(SAMLConstants.SAML20MDUI_PREFIX, DisplayName.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- writer.newLine();
-
- // Description
- writer.write(" <");
- writeNameSpaceQualified(SAMLConstants.SAML20MDUI_PREFIX, Description.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(' ');
- writeLangAttribute("en");
- writer.write('>');
- writer.write("Enter a description of your IdP at ");
- writer.write(params.getDnsName());
- writer.write("</");
- writeNameSpaceQualified(SAMLConstants.SAML20MDUI_PREFIX, Description.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- writer.newLine();
-
- // Logo
- writer.write(" <");
- writeNameSpaceQualified(SAMLConstants.SAML20MDUI_PREFIX, Logo.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(" height=\"80\" width=\"80\">");
- writer.write("https://");
- writer.write(params.getDnsName());
- writer.write("/Path/To/Logo.png");
- writer.write("</");
- writeNameSpaceQualified(SAMLConstants.SAML20MDUI_PREFIX, Logo.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- writer.newLine();
-
- writer.write(" </");
- writeNameSpaceQualified(SAMLConstants.SAML20MDUI_PREFIX, UIInfo.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- writer.newLine();
-
- closeComment();
- writer.newLine();
- }
-
- /**
- * Write the language attribute.
- *
- * @param language which languages
- * @throws IOException if badness happens
- */
- protected void writeLangAttribute(final String language) throws IOException {
- writeNameSpaceQualified(XMLConstants.XML_PREFIX, LangBearing.XML_LANG_ATTR_LOCAL_NAME);
- writer.write("=\"");
- writer.write(language);
- writer.write('"');
- }
-
- /**
- * Write out any <KeyDescriptor>Elements.
- *
- * @throws IOException if badness happens
- */
- protected void writeKeyDescriptors() throws IOException {
- final List<List<String>> signing = new ArrayList<>(2);
- final List<String> backchannelCert = params.getBackchannelCert();
- if (backchannelCert != null && !backchannelCert.isEmpty()) {
- writer.write(" ");
- openComment();
- writer.write(" First signing certificate is BackChannel, the Second is FrontChannel");
- closeComment();
- writer.newLine();
- signing.add(backchannelCert);
- }
- final List<String> signingCert = params.getSigningCert();
- if (signingCert!= null && !signingCert.isEmpty()) {
- signing.add(signingCert);
- }
- writeKeyDescriptors(signing, "signing");
-
- final List<String> encryption = params.getEncryptionCert();
- if (encryption != null) {
- writeKeyDescriptors(CollectionSupport.singletonList(encryption), "encryption");
- }
- writer.newLine();
- }
-
- /**
- * Write out <KeyDescriptor>Elements. of a specific type
- *
- * @param certs the certificates
- * @param use the type - signing or encryption
- * @throws IOException if badness happens
- */
- protected void writeKeyDescriptors(@Nullable final List<List<String>> certs, @Nonnull @NotEmpty final String use)
- throws IOException {
-
- if (null == certs || certs.isEmpty()) {
- return;
- }
- for (final List<String> cert : certs) {
- writer.write(" <");
- writer.write(KeyDescriptor.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(" use=\"");
- writer.write(use);
- writer.write("\">");
- writer.newLine();
- writer.write(" <");
- writeNameSpaceQualified(SignatureConstants.XMLSIG_PREFIX, KeyInfo.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- writer.newLine();
- writer.write(" <");
- writeNameSpaceQualified(SignatureConstants.XMLSIG_PREFIX, X509Data.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- writer.newLine();
- writer.write(" <");
- writeNameSpaceQualified(SignatureConstants.XMLSIG_PREFIX, X509Certificate.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- writer.newLine();
- for (final String certLine : cert) {
- writer.write(certLine);
- writer.newLine();
- }
- writer.write(" </");
- writeNameSpaceQualified(SignatureConstants.XMLSIG_PREFIX, X509Certificate.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- writer.newLine();
- writer.write(" </");
- writeNameSpaceQualified(SignatureConstants.XMLSIG_PREFIX, X509Data.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- writer.newLine();
- writer.write(" </");
- writeNameSpaceQualified(SignatureConstants.XMLSIG_PREFIX, KeyInfo.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- writer.newLine();
- writer.newLine();
- writer.write(" </");
- writer.write(KeyDescriptor.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write('>');
- writer.newLine();
- }
- }
-
- /**
- * Output the SAML for a single endpoint.
- *
- * @param endpoint the type
- * @throws IOException if badness happens.
- */
- // Checkstyle: MethodLength|CyclomaticComplexity OFF
- protected void outputEndpoint(final Endpoints endpoint) throws IOException {
- switch (endpoint) {
- case SAML1Artifact:
- writer.write(" ");
- if (isSAML1Commented()) {
- openComment();
- }
- writer.write("<");
- writer.write(ArtifactResolutionService.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(" Binding=\"");
- writer.write(SAMLConstants.SAML1_SOAP11_BINDING_URI);
- writer.write("\" Location=\"https://");
- writer.write(params.getDnsName());
- writer.write(":8443/idp/profile/SAML1/SOAP/ArtifactResolution\"");
- writer.write(" index=\"1\"/>");
- writer.newLine();
- if (isSAML1Commented()) {
- closeComment();
- }
- break;
-
- case SAML2Artifact:
- writer.write(" ");
- writer.write("<");
- writer.write(ArtifactResolutionService.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(" Binding=\"");
- writer.write(SAMLConstants.SAML2_SOAP11_BINDING_URI);
- writer.write("\" Location=\"https://");
- writer.write(params.getDnsName());
- writer.write(":8443/idp/profile/SAML2/SOAP/ArtifactResolution\"");
- writer.write(" index=\"2\"/>");
- writer.newLine();
- break;
-
- case RedirectSLO:
- writer.write(" ");
- writer.write("<");
- writer.write(SingleLogoutService.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(" Binding=\"");
- writer.write(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
- writer.write("\" Location=\"https://");
- writer.write(params.getDnsName());
- writer.write("/idp/profile/SAML2/Redirect/SLO\"/>");
- writer.newLine();
- break;
-
- case POSTSLO:
- writer.write(" ");
- writer.write("<");
- writer.write(SingleLogoutService.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(" Binding=\"");
- writer.write(SAMLConstants.SAML2_POST_BINDING_URI);
- writer.write("\" Location=\"https://");
- writer.write(params.getDnsName());
- writer.write("/idp/profile/SAML2/POST/SLO\"/>");
- writer.newLine();
- break;
-
- case POSTSimpleSignSLO:
- writer.write(" ");
- writer.write("<");
- writer.write(SingleLogoutService.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(" Binding=\"");
- writer.write(SAMLConstants.SAML2_POST_SIMPLE_SIGN_BINDING_URI);
- writer.write("\" Location=\"https://");
- writer.write(params.getDnsName());
- writer.write("/idp/profile/SAML2/POST-SimpleSign/SLO\"/>");
- writer.newLine();
- break;
-
- case SOAPSLO:
- writer.write(" ");
- writer.write("<");
- writer.write(SingleLogoutService.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(" Binding=\"");
- writer.write(SAMLConstants.SAML2_SOAP11_BINDING_URI);
- writer.write("\" Location=\"https://");
- writer.write(params.getDnsName());
- writer.write(":8443/idp/profile/SAML2/SOAP/SLO\"/>");
- writer.newLine();
- break;
-
- case ShibbolethSSO:
- writer.write(" ");
- if (isSAML1Commented()) {
- openComment();
- }
- writer.write("<");
- writer.write(SingleSignOnService.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(" Binding=\"urn:mace:shibboleth:1.0:profiles:AuthnRequest\"");
- writer.write(" Location=\"https://");
- writer.write(params.getDnsName());
- writer.write("/idp/profile/Shibboleth/SSO\"/>");
- writer.newLine();
- if (isSAML1Commented()) {
- closeComment();
- }
- break;
-
- case POSTSSO:
- writer.write(" ");
- writer.write("<");
- writer.write(SingleSignOnService.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(" Binding=\"");
- writer.write(SAMLConstants.SAML2_POST_BINDING_URI);
- writer.write("\" ");
- writeNameSpaceQualified(SAMLConstants.SAML20PREQ_ATTRR_PREFIX,
- RequestedAttributes.SUPPORTS_REQUESTED_ATTRIBUTES_LOCAL_NAME);
- writer.write("=\"true\" Location=\"https://");
- writer.write(params.getDnsName());
- writer.write("/idp/profile/SAML2/POST/SSO\"/>");
- writer.newLine();
- break;
-
- case POSTSimpleSignSSO:
- writer.write(" ");
- writer.write("<");
- writer.write(SingleSignOnService.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(" Binding=\"");
- writer.write(SAMLConstants.SAML2_POST_SIMPLE_SIGN_BINDING_URI);
- writer.write("\" ");
- writeNameSpaceQualified(SAMLConstants.SAML20PREQ_ATTRR_PREFIX,
- RequestedAttributes.SUPPORTS_REQUESTED_ATTRIBUTES_LOCAL_NAME);
- writer.write("=\"true\" Location=\"https://");
- writer.write(params.getDnsName());
- writer.write("/idp/profile/SAML2/POST-SimpleSign/SSO\"/>");
- writer.newLine();
- break;
-
- case RedirectSSO:
- writer.write(" ");
- writer.write("<");
- writer.write(SingleSignOnService.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(" Binding=\"");
- writer.write(SAMLConstants.SAML2_REDIRECT_BINDING_URI);
- writer.write("\" ");
- writeNameSpaceQualified(SAMLConstants.SAML20PREQ_ATTRR_PREFIX,
- RequestedAttributes.SUPPORTS_REQUESTED_ATTRIBUTES_LOCAL_NAME);
- writer.write("=\"true\" Location=\"https://");
- writer.write(params.getDnsName());
- writer.write("/idp/profile/SAML2/Redirect/SSO\"/>");
- writer.newLine();
- break;
-
- case SAML1Query:
- writer.write(" ");
- if (isSAML1Commented()) {
- openComment();
- }
- writer.write("<");
- writer.write(AttributeService.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(" Binding=\"");
- writer.write(SAMLConstants.SAML1_SOAP11_BINDING_URI);
- writer.write("\" Location=\"https://");
- writer.write(params.getDnsName());
- writer.write(":8443/idp/profile/SAML1/SOAP/AttributeQuery\"/>");
- if (isSAML1Commented()) {
- closeComment();
- }
- writer.newLine();
- break;
-
- case SAML2Query:
- writer.write(" ");
- if (isSAML2AttributeQueryCommented()) {
- openComment();
- }
- writer.write("<");
- writer.write(AttributeService.DEFAULT_ELEMENT_LOCAL_NAME);
- writer.write(" Binding=\"");
- writer.write(SAMLConstants.SAML2_SOAP11_BINDING_URI);
- writer.write("\" Location=\"https://");
- writer.write(params.getDnsName());
- writer.write(":8443/idp/profile/SAML2/SOAP/AttributeQuery\"/>");
- if (isSAML2AttributeQueryCommented()) {
- closeComment();
- writer.newLine();
- writer.write(" ");
- openComment();
- writer.write(" If you uncomment the above you should add " + SAMLConstants.SAML20P_NS
- + " to the protocolSupportEnumeration above");
- closeComment();
- }
- writer.newLine();
- break;
-
- default:
- break;
- }
- }
-
- /**
- * Write a namespace:identifier pair.
- *
- * @param nameSpace the namespace
- * @param what the identifier
- * @throws IOException if badness happens
- */
- protected void writeNameSpaceQualified(@Nonnull final String nameSpace, final String what) throws IOException {
- writer.write(nameSpace);
- writer.write(':');
- writer.write(what);
- }
-
- /** Add an open comment. If we are nested closes the previous one.
- * @throws IOException if badness happens
- */
- private synchronized void openComment() throws IOException {
- if (commentDepth > 0) {
- writer.write("--> ");
- }
- writer.write("<!--");
- commentDepth++;
- }
-
- /** Add a close comment. If we are nested reopens the previous one.
- * @throws IOException if badness happens
- */
- private synchronized void closeComment() throws IOException {
- writer.write("--> ");
- commentDepth--;
- if (commentDepth > 0) {
- writer.write(" <!--");
- }
- }
-}
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/metadata/impl/MetadataGeneratorParameters.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/metadata/impl/MetadataGeneratorParameters.java
deleted file mode 100644
index 0f9dab109..000000000
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/metadata/impl/MetadataGeneratorParameters.java
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You under the Apache
- * License, Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package net.shibboleth.idp.installer.metadata.impl;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.annotation.constraint.NotLive;
-import net.shibboleth.shared.annotation.constraint.Unmodifiable;
-import net.shibboleth.shared.component.AbstractInitializableComponent;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.resource.Resource;
-
-/**
- * Parameters to metadata generation
- */
-public class MetadataGeneratorParameters extends AbstractInitializableComponent {
-
- /**
- * The file with the certificate the IDP uses to encrypt.
- */
- @Nullable private File encryptionCert;
-
- /**
- * The strings with the encryption cert in them (to allow for multiline output).
- */
- @Nullable private List<String> encryptionCerts;
-
- /**
- * The file with the certificate that TLS uses to 'sign'.
- */
- @Nullable private File backChannelCert;
-
- /**
- * The strings with the back channel cert in them (to allow for multiline output).
- */
- @Nullable private List<String> backChannelCerts;
-
- /**
- * The file with the certificate the IDP uses to sign.
- */
- @Nullable private File signingCert;
-
- /**
- * The strings with the signing certs in them (to allow for multiline output).
- */
- @Nullable private List<String> signingCerts;
-
- /** The entityID. */
- @NonnullAfterInit private String entityID;
-
- /** The DNS name. */
- @NonnullAfterInit private String dnsName;
-
- /** The scope. */
- @Nullable private String scope;
-
- /** {@inheritDoc} */
- protected void doInitialize() throws ComponentInitializationException {
- try {
- encryptionCerts = getCertificateContents(encryptionCert);
- signingCerts = getCertificateContents(signingCert);
- backChannelCerts = getCertificateContents(backChannelCert);
- } catch (final IOException e) {
- throw new ComponentInitializationException(e);
- }
- if (entityID == null || entityID.isEmpty()) {
- throw new ComponentInitializationException("Entity ID not specified");
- }
- if (dnsName == null || dnsName.isEmpty()) {
- throw new ComponentInitializationException("DNS name not specified");
- }
- }
-
- /**
- * Get the (mutli-line) string representations of the encryption certs.
- *
- * @return Returns the encryption cert or null if none available.
- */
- @Nullable public List<String> getEncryptionCert() {
- return encryptionCerts;
- }
-
- /**
- * Set the encryption Certificate file.
- *
- * @param resource what to set.
- */
- public void setEncryptionCertResource(@Nonnull final Resource resource) {
-
- try {
- encryptionCert = resource.getFile();
- } catch (final IOException e) {
- encryptionCert = null;
- }
- }
-
- /**
- * Get the (mutli-line) string representation of the signing cert.
- *
- * @return Returns the signing cert or null if none available.
- */
- @Nullable public List<String> getSigningCert() {
- return signingCerts;
- }
-
- /**
- * Set the signing Certificate file.
- *
- * @param resource what to set.
- */
- public void setSigningCertResource(@Nonnull final Resource resource) {
- try {
- signingCert = resource.getFile();
- } catch (final IOException e) {
- signingCert = null;
- }
- }
-
- /**
- * Get the (mutli-line)string representation of the back channel cert.
- *
- * @return Returns the back channel cert or null if non available.
- */
- @Nullable public List<String> getBackchannelCert() {
- return backChannelCerts;
- }
-
- /**
- * Set the Backchannel Certificate file.
- *
- * @param file what to set.
- */
- public void setBackchannelCert(@Nullable final File file) {
- backChannelCert = file;
- }
-
- /**
- * Set the Backchannel Certificate.
- *
- * @param resource what to set.
- */
- public void setBackchannelCertResource(@Nonnull final Resource resource) {
- try {
- backChannelCert = resource.getFile();
- } catch (final IOException e) {
- backChannelCert = null;
- }
- }
-
-
- /**
- * Open the file and return the contents and a list of lines.
- *
- * @param file the file
- * @return the contents
- *
- * @throws IOException if badness occurrs
- */
- @Nullable @Unmodifiable @NotLive private List<String> getCertificateContents(final File file) throws IOException {
- if (null == file || !file.exists()) {
- return null;
- }
-
- try (final FileReader fr = new FileReader(file);
- final BufferedReader reader = new BufferedReader(fr)) {
- final List<String> output = new ArrayList<>();
- String s = reader.readLine();
- while (s != null) {
- output.add(s);
- s = reader.readLine();
- }
- if ((output.size() > 0) && output.get(0).startsWith("----")) {
- output.remove(0);
- }
- final int last = output.size() - 1;
- if (last <= 0) {
- return null;
- }
- if (output.get(last).startsWith("----")) {
- output.remove(last);
- }
- return output;
- }
- }
-
- /**
- * Returns the entityID.
- *
- * @return the entityID.
- */
- @Nonnull @NotEmpty public String getEntityID() {
- assert entityID != null;
- return entityID;
- }
-
- /**
- * Sets the entityID.
- *
- * @param id what to set.
- */
- public void setEntityID(@Nonnull final String id) {
- entityID = id;
- }
-
- /**
- * Returns the dnsName (for use in endpoints).
- *
- * @return the dnsname.
- */
- @Nonnull @NotEmpty public String getDnsName() {
- assert dnsName != null;
- return dnsName;
- }
-
- /**
- * Sets the dns name.
- *
- * @param name what to set.
- */
- public void setDnsName(@Nonnull final String name) {
- dnsName = name;
- }
-
- /**
- * Returns the scope used.
- *
- * @return the scope.
- */
- @Nullable public String getScope() {
- return scope;
- }
-
- /**
- * Sets the scope.
- *
- * @param value what to set.
- */
- public void setScope(@Nullable final String value) {
- scope = value;
- }
-}
diff --git a/idp-installer/src/main/java/net/shibboleth/idp/installer/metadata/impl/package-info.java b/idp-installer/src/main/java/net/shibboleth/idp/installer/metadata/impl/package-info.java
deleted file mode 100644
index 9fb4c8fd4..000000000
--- a/idp-installer/src/main/java/net/shibboleth/idp/installer/metadata/impl/package-info.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the University Corporation for Advanced Internet Development,
- * Inc. (UCAID) under one or more contributor license agreements. See the
- * NOTICE file distributed with this work for additional information regarding
- * copyright ownership. The UCAID licenses this file to You under the Apache
- * License, Version 2.0 (the "License"); you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * Package to contain classes to do with the one off metadata generation.
- */
- at NonnullElements
-package net.shibboleth.idp.installer.metadata.impl;
-
-import net.shibboleth.shared.annotation.constraint.NonnullElements;
diff --git a/idp-installer/src/main/resources/net/shibboleth/idp/installer/metadata-generator-ant.xml b/idp-installer/src/main/resources/net/shibboleth/idp/installer/metadata-generator-ant.xml
deleted file mode 100644
index f9d393ce3..000000000
--- a/idp-installer/src/main/resources/net/shibboleth/idp/installer/metadata-generator-ant.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
- xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
- <bean
- class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
- p:placeholderPrefix="%{" p:placeholderSuffix="}" />
-
- <context:property-placeholder />
-
- <bean id="IdPConfiguration"
- class="net.shibboleth.idp.installer.metadata.impl.MetadataGeneratorParameters"
- p:encryptionCertResource="%{idp.encryption.cert}" p:signingCertResource="%{idp.signing.cert}"
- p:entityID="%{idp.entityID}" p:scope="%{idp.scope}" />
-</beans>
\ No newline at end of file
diff --git a/idp-installer/src/main/resources/net/shibboleth/idp/installer/metadata-generator.xml b/idp-installer/src/main/resources/net/shibboleth/idp/installer/metadata-generator.xml
index d9f814fdc..1ac927780 100644
--- a/idp-installer/src/main/resources/net/shibboleth/idp/installer/metadata-generator.xml
+++ b/idp-installer/src/main/resources/net/shibboleth/idp/installer/metadata-generator.xml
@@ -1,19 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
- xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p"
+ xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
default-init-method="initialize"
default-destroy-method="destroy">
- <bean id="IdPConfiguration"
- class="net.shibboleth.idp.installer.metadata.impl.MetadataGeneratorParameters"
- p:encryptionCertResource="%{idp.encryption.cert}"
+ <bean id="IdPConfiguration"
+ class="net.shibboleth.idp.installer.impl.InstalledMetadataParameters"
+ p:encryptionCertResource="%{idp.encryption.cert}"
p:signingCertResource="%{idp.signing.cert}"
p:backchannelCertResource="%{idp.backchannel.cert}"
p:dnsName="%{idp.dnsname}"
- p:entityID="%{idp.entityID}" p:scope="%{idp.scope}" />
-</beans>
\ No newline at end of file
+ p:entityID="%{idp.entityID}" p:scope="%{idp.scope}" />
+
+ <util:map id="VelocityEngineProperties">
+ <entry key="parser.space_gobbling" value="%{idp.velocity.space.gobbling:bc}" />
+ <entry key="resource.loaders" value="file, classpath, string" />
+ <entry key="resource.loader.classpath.class" value="org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader" />
+ <entry key="resource.loader.string.class" value="org.apache.velocity.runtime.resource.loader.StringResourceLoader" />
+ <entry key="resource.loader.file.class" value="org.apache.velocity.runtime.resource.loader.FileResourceLoader" />
+ <entry key="resource.loader.file.path" value="#{'%{idp.views:%{idp.home}/views}'.trim()}" />
+ <entry key="resource.loader.file.cache" value="false" />
+ <entry key="runtime.strict_mode.enable" value="%{idp.velocity.runtime.strictmode:false}" />
+ </util:map>
+
+ <bean id="VelocityEngine" class="net.shibboleth.shared.spring.velocity.VelocityEngineFactoryBean"
+ p:velocityPropertiesMap-ref="VelocityEngineProperties" />
+
+</beans>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list