[java-idp-plugin-metadatagen] branch dev/JMETAGEN-5 updated: JMETAGEN-5 - Replacement for SP metagen script

Scott Cantor cantor.2 at osu.edu
Mon Jul 10 19:18:24 UTC 2023


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch dev/JMETAGEN-5
in repository java-idp-plugin-metadatagen.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-plugin-metadatagen.git;a=commit;h=13d95c853def4d883e2aa6bfe260cfab57fd615f

The following commit(s) were added to refs/heads/dev/JMETAGEN-5 by this push:
     new 13d95c8  JMETAGEN-5 - Replacement for SP metagen script
13d95c8 is described below

commit 13d95c853def4d883e2aa6bfe260cfab57fd615f
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Jul 10 15:18:22 2023 -0400

    JMETAGEN-5 - Replacement for SP metagen script
    
    https://shibboleth.atlassian.net/browse/JMETAGEN-5
    
    Refactor generic code into OpenSAML.
---
 metadatagen-impl/pom.xml                           |  17 +
 .../impl/AssertionConsumerServiceConverter.java    |  55 +++
 .../plugin/metadatagen/impl/MetadataGenCLI.java    |  85 ++--
 .../impl/MetadataGenCommandLineArguments.java      | 520 +++++++--------------
 .../impl/SingleLogoutServiceConverter.java         |  61 +++
 .../AttributeAuthorityDescriptor.vm                |   3 -
 .../metadatagen-templates/EntityDescriptor.vm      |  13 -
 .../metadatagen-templates/IDPSSODescriptor.vm      |   4 -
 .../metadatagen-templates/KeyDescriptors.vm        |  30 --
 .../metadatagen-templates/SPSSODescriptor.vm       |  18 -
 .../metadatagen-templates/SingleLogoutServices.vm  |  12 -
 .../idp/plugin/metadatagen/conf/velocity.xml       |   3 +
 .../plugin/metadatagen/impl/MetadataGenTest.java   |  85 +---
 13 files changed, 345 insertions(+), 561 deletions(-)

diff --git a/metadatagen-impl/pom.xml b/metadatagen-impl/pom.xml
index 0fab6c1..dc0b8a8 100644
--- a/metadatagen-impl/pom.xml
+++ b/metadatagen-impl/pom.xml
@@ -47,11 +47,28 @@
             <scope>provided</scope>
         </dependency>
 
+        <dependency>
+            <groupId>${shib-shared.groupId}</groupId>
+            <artifactId>shib-profile-impl</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>${shib-shared.groupId}</groupId>
+            <artifactId>shib-metadata-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
         <dependency>
             <groupId>${opensaml.groupId}</groupId>
             <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/metadatagen-impl/src/main/java/net/shibboleth/idp/plugin/metadatagen/impl/AssertionConsumerServiceConverter.java b/metadatagen-impl/src/main/java/net/shibboleth/idp/plugin/metadatagen/impl/AssertionConsumerServiceConverter.java
new file mode 100644
index 0000000..087f059
--- /dev/null
+++ b/metadatagen-impl/src/main/java/net/shibboleth/idp/plugin/metadatagen/impl/AssertionConsumerServiceConverter.java
@@ -0,0 +1,55 @@
+/*
+ * 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.plugin.metadatagen.impl;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.saml.saml2.metadata.AssertionConsumerService;
+
+import net.shibboleth.idp.cas.config.AbstractProtocolConfiguration;
+import net.shibboleth.shared.annotation.constraint.Live;
+import net.shibboleth.shared.collection.Pair;
+
+/**
+ * JCommander support for parsing a binding/endpoint pair into a {@link AssertionConsumerService}.
+ */
+public class AssertionConsumerServiceConverter extends org.opensaml.saml.metadata.generator.impl.AssertionConsumerServiceConverter {
+    
+    /** {@inheritDoc} */
+    @Nonnull public AssertionConsumerService apply(@Nullable final String value,
+            @Nullable @Live final List<String> protocols) {
+        return getProcessedEndpoint(protocols, value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull protected Pair<String,String> getProtocolAndBinding(@Nonnull final String input) {
+        if (input.startsWith("CAS/")) {
+            return new Pair<>(AbstractProtocolConfiguration.PROTOCOL_URI,
+                    AbstractProtocolConfiguration.PROTOCOL_URI + "/login");
+        } else if (input.startsWith("CASProxy/")) {
+            return new Pair<>(AbstractProtocolConfiguration.PROTOCOL_URI,
+                    AbstractProtocolConfiguration.PROTOCOL_URI + "/proxy");
+        }
+        return super.getProtocolAndBinding(input);
+    }
+
+}
\ No newline at end of file
diff --git a/metadatagen-impl/src/main/java/net/shibboleth/idp/plugin/metadatagen/impl/MetadataGenCLI.java b/metadatagen-impl/src/main/java/net/shibboleth/idp/plugin/metadatagen/impl/MetadataGenCLI.java
index 08ed7e6..b4080a6 100644
--- a/metadatagen-impl/src/main/java/net/shibboleth/idp/plugin/metadatagen/impl/MetadataGenCLI.java
+++ b/metadatagen-impl/src/main/java/net/shibboleth/idp/plugin/metadatagen/impl/MetadataGenCLI.java
@@ -21,9 +21,9 @@ import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.io.PrintWriter;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
+import java.io.Writer;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -37,9 +37,7 @@ import java.util.stream.Stream;
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 
-import org.apache.velocity.VelocityContext;
 import org.apache.velocity.app.VelocityEngine;
-import org.apache.velocity.context.Context;
 import org.opensaml.core.xml.LangBearing;
 import org.opensaml.saml.common.xml.SAMLConstants;
 import org.opensaml.saml.ext.reqattr.RequestedAttributes;
@@ -47,6 +45,8 @@ 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.metadata.generator.impl.AbstractEndpointConverter;
+import org.opensaml.saml.metadata.generator.impl.VelocityMetadataGenerator;
 import org.opensaml.saml.saml2.core.Extensions;
 import org.opensaml.saml.saml2.metadata.ArtifactResolutionService;
 import org.opensaml.saml.saml2.metadata.AssertionConsumerService;
@@ -70,7 +70,6 @@ import org.springframework.core.env.Environment;
 import org.springframework.core.io.ClassPathResource;
 import org.springframework.core.io.Resource;
 
-import net.shibboleth.idp.cas.config.AbstractProtocolConfiguration;
 import net.shibboleth.idp.cli.AbstractIdPHomeAwareCommandLine;
 import net.shibboleth.idp.saml.xmlobject.ExtensionsConstants;
 import net.shibboleth.idp.saml.xmlobject.Scope;
@@ -97,15 +96,9 @@ public final class MetadataGenCLI extends AbstractIdPHomeAwareCommandLine<Metada
     /** Class logger. */
     @Nullable private Logger log;
 
-    /** Where we are outputting to? */
-    private PrintWriter output;
-
     /** The processed arguments. */
     private MetadataGenCommandLineArguments args;
     
-    /** Velocity engine for output generation. */
-    private VelocityEngine velocityEngine;
-    
     /** Constructor. */
     public MetadataGenCLI() {
         setCaseSensitiveOptions(false);
@@ -481,49 +474,23 @@ public final class MetadataGenCLI extends AbstractIdPHomeAwareCommandLine<Metada
     }
 
     /**
-     * Builds the Velocity template context.
-     * 
-     * @return the populated context
-     */
-    @Nonnull private VelocityContext getVelocityContext() {
-        final VelocityContext context = new VelocityContext();
-        
-        context.put("args", args);
-        
-        // Namespace handling.
-        if (!args.isOmitNamespaces()) {
-            final Map<String,String> prefixMap = new HashMap<>();
-            prefixMap.put(SAMLConstants.SAML20MD_PREFIX, SAMLConstants.SAML20MD_NS);
-            prefixMap.put(SAMLConstants.SAML20_PREFIX, SAMLConstants.SAML20_NS);
-            prefixMap.put(SignatureConstants.XMLSIG_PREFIX, SignatureConstants.XMLSIG_NS);
-            if (args.isOIDC()) {
-                prefixMap.put("oidcmd", "urn:mace:shibboleth:metadata:oidc:1.0");
-            }
-            context.put("namespaces", prefixMap);
-        }
-        
-        return context;
-    }
-
-    /**
-     * Set up {@link MetadataGenCLI#output}.
+     * Get the output sink.
      * 
-     * @return true iff this worked
+     * @return output sink
      */
-    private boolean setupWriter() {
+    @Nullable private Writer getWriter() {
         if (args.getOutputFile() == null) {
-            output = new PrintWriter(System.out);
+            return new PrintWriter(System.out);
         } else {
             final File out = new File(args.getOutputFile());
             try {
                 final FileOutputStream outStream = new FileOutputStream(out);
-                output = new PrintWriter(new BufferedOutputStream(outStream));
+                return new PrintWriter(new BufferedOutputStream(outStream));
             } catch (final IOException e) {
                 getLogger().error("Could not open {}", args.getOutputFile(), e);
-                return false;
+                return null;
             }
         }
-        return true;
     }
 
     /** {@inheritDoc} */
@@ -535,27 +502,39 @@ public final class MetadataGenCLI extends AbstractIdPHomeAwareCommandLine<Metada
         if (ret != RC_OK) {
             return ret;
         }
-        
-        if (!setupWriter()) {
-            return RC_IO;
-        }
-        
+
+        final VelocityEngine velocityEngine;
         try {
             velocityEngine = getApplicationContext().getBean(VELOCITY_ENGINE_BEAN_NAME, VelocityEngine.class);
         } catch (final BeansException e) {
             getLogger().error("Unable to acquire Velocity engine bean", e);
             return RC_INIT;
         }
+
+        final Writer sink = getWriter();
+        if (sink == null) {
+            return RC_IO;
+        }
         
         try {
-            Template.fromTemplateName(velocityEngine,
-                    "/metadatagen-templates/EntityDescriptor.vm").merge(getVelocityContext(), output);
+            final VelocityMetadataGenerator generator = new VelocityMetadataGenerator();
+            generator.setId("MetadataGeneratorCLI");
+            generator.setVelocityEngine(velocityEngine);
+            generator.initialize();
+
+            assert args != null;
+            generator.generate(args, sink);
+            sink.close();
         } catch (final Exception e) {
             getLogger().error("Error generating output", e);
+            try {
+                sink.close();
+            } catch (final IOException e1) {
+                
+            }
             return RC_IO;
         }
         
-        output.close();
         return RC_OK;
     }
 
@@ -569,6 +548,10 @@ public final class MetadataGenCLI extends AbstractIdPHomeAwareCommandLine<Metada
     * @param args arguments
     */
    public static int runMain(@Nonnull final String[] args) {
+       // Add Shibboleth-specific SAML 1.1 binding.
+       AbstractEndpointConverter.addBinding("Redirect1", SAMLConstants.SAML11P_NS,
+               net.shibboleth.idp.saml.xml.SAMLConstants.SAML1_REQUEST_BINDING);
+       
        final MetadataGenCLI cli = new MetadataGenCLI();
 
        return cli.run(args);
diff --git a/metadatagen-impl/src/main/java/net/shibboleth/idp/plugin/metadatagen/impl/MetadataGenCommandLineArguments.java b/metadatagen-impl/src/main/java/net/shibboleth/idp/plugin/metadatagen/impl/MetadataGenCommandLineArguments.java
index a1fc765..c9a216c 100644
--- a/metadatagen-impl/src/main/java/net/shibboleth/idp/plugin/metadatagen/impl/MetadataGenCommandLineArguments.java
+++ b/metadatagen-impl/src/main/java/net/shibboleth/idp/plugin/metadatagen/impl/MetadataGenCommandLineArguments.java
@@ -21,38 +21,52 @@ import java.io.PrintStream;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Collection;
 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.saml.common.xml.SAMLConstants;
-import org.opensaml.saml.saml2.metadata.ArtifactResolutionService;
-import org.opensaml.saml.saml2.metadata.AssertionConsumerService;
-import org.opensaml.saml.saml2.metadata.AttributeService;
-import org.opensaml.saml.saml2.metadata.SingleLogoutService;
-import org.opensaml.saml.saml2.metadata.SingleSignOnService;
+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.metadata.generator.impl.ArtifactResolutionServiceConverter;
+import org.opensaml.saml.metadata.generator.impl.AttributeServiceConverter;
+import org.opensaml.saml.metadata.generator.impl.SingleSignOnServiceConverter;
+import org.opensaml.saml.metadata.generator.impl.TemplateMetadataGeneratorParameters;
+import org.opensaml.saml.saml2.metadata.AttributeAuthorityDescriptor;
+import org.opensaml.saml.saml2.metadata.Endpoint;
+import org.opensaml.saml.saml2.metadata.IDPSSODescriptor;
+import org.opensaml.saml.saml2.metadata.IndexedEndpoint;
+import org.opensaml.saml.saml2.metadata.SPSSODescriptor;
 import org.opensaml.security.x509.X509Support;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.beust.jcommander.Parameter;
+import com.google.common.base.Predicates;
 
-import net.shibboleth.idp.cas.config.AbstractProtocolConfiguration;
 import net.shibboleth.idp.cli.AbstractIdPHomeAwareCommandLineArguments;
+import net.shibboleth.shared.annotation.constraint.Live;
 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.codec.Base64Support;
 import net.shibboleth.shared.codec.EncodingException;
 import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.primitive.StringSupport;
 
 /**
  * Command line arguments for Metadata Generation.
+ * 
+ * <p>Note that JCommander does not appear to handle data conversion when lists are involved,
+ * so the code is invoking a library of endpoint converters by hand.</p>
  */
-public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommandLineArguments {
+public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommandLineArguments
+        implements TemplateMetadataGeneratorParameters {
     
     /** Property name for the back channel certificate. */
     @Nonnull @NotEmpty public static final String BACKCHANNEL_PROPERTY = "idp.metadata.backchannel.cert";
@@ -84,18 +98,6 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
     /** The unique ID. */
     @Parameter(names = {"--entityID", "--client_id", "--id"}, required=true, description="Unique ID for entity")
     @Nullable private String entityID;
-    
-    /** Do we output SAML 2.0? */
-    @Parameter(names = {"--saml2"}, description="Include SAML 2.0 support?")
-    private boolean saml2;
-
-    /** Do we output CAS? */
-    @Parameter(names = {"--cas"}, description="Include CAS support?")
-    private boolean cas;
-
-    /** Do we output OIDC? */
-    @Parameter(names = {"--oidc"}, description="Include OpenID/OAuth support?")
-    private boolean oidc;
 
     /** Do we output an SP role? */
     @Parameter(names = {"--sp"}, description="Include Service Provider role?")
@@ -109,44 +111,20 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
     @Parameter(names = {"--aa"}, description="Include Attribute Authority role?")
     private boolean aa;
     
-    @Parameter(names = {"--sso-redirect"}, description="SingleSignOnService endpoint using HTTP-Redirect binding")
-    @Nullable private String ssoRedirect;
-
-    @Parameter(names = {"--sso-post"}, description="SingleSignOnService endpoint using HTTP-POST binding")
-    @Nullable private String ssoPost;
-
-    @Parameter(names = {"--sso-artifact"}, description="SingleSignOnService endpoint using HTTP-Artifact binding")
-    @Nullable private String ssoArtifact;
-
-    @Parameter(names = {"--sso-soap"}, description="SingleSignOnService endpoint using SOAP binding")
-    @Nullable private String ssoSoap;
+    @Parameter(names = {"--sso"}, description="SingleSignOnService endpoint(s)")
+    @Nonnull private List<String> ssoServices = new ArrayList<>();
 
-    @Parameter(names = {"--attr-soap"}, description="AttributeService endpoint using SOAP binding")
-    @Nullable private String attributeSoap;
+    @Parameter(names = {"--attribute-query", "--query"}, description="AttributeService endpoint(s)")
+    @Nonnull private List<String> attributeServices = new ArrayList<>();
 
-    @Parameter(names = {"--artifact-soap", "--artifact"}, description="ArtifactResolutionService endpoint(s) using SOAP binding")
-    @Nonnull private List<String> artifactSoap = new ArrayList<>();
+    @Parameter(names = {"--artifact"}, description="ArtifactResolutionService endpoint(s)")
+    @Nonnull private List<String> artifactServices = new ArrayList<>();
 
-    @Parameter(names = {"--logout-redirect", "-LR"}, description="SingleLogoutService endpoint(s) using HTTP-Redirect binding")
-    @Nonnull private List<String> logoutRedirect = new ArrayList<>();
+    @Parameter(names = {"--logout", "--slo"}, description="SingleLogoutService endpoint(s)")
+    @Nonnull private List<String> logoutServices = new ArrayList<>();
 
-    @Parameter(names = {"--logout-post", "-LP"}, description="SingleLogoutService endpoint(s) using HTTP-POST binding")
-    @Nonnull private List<String> logoutPost = new ArrayList<>();
-
-    @Parameter(names = {"--logout-artifact", "-LA"}, description="SingleLogoutService endpoint(s) using HTTP-Artifact binding")
-    @Nonnull private List<String> logoutArtifact = new ArrayList<>();
-
-    @Parameter(names = {"--logout-soap", "-LS"}, description="SingleLogoutService endpoint(s) using SOAP binding")
-    @Nonnull private List<String> logoutSoap = new ArrayList<>();
-
-    @Parameter(names = {"--acs", "--acs-post",  "-h"}, description="AssertionConsumerService endpoint(s) using HTTP-POST binding")
-    @Nonnull private List<String> acsPost = new ArrayList<>();
-
-    @Parameter(names = {"--acs-artifact"}, description="AssertionConsumerService endpoint(s) using HTTP-Artifact binding")
-    @Nonnull private List<String> acsArtifact = new ArrayList<>();
-
-    @Parameter(names = {"--acs-paos", "--ecp"}, description="AssertionConsumerService endpoint(s) using PAOS binding")
-    @Nonnull private List<String> acsPaos = new ArrayList<>();
+    @Parameter(names = {"--acs"}, description="AssertionConsumerService endpoint(s)")
+    @Nonnull private List<String> acServices = new ArrayList<>();
     
     /** Path(s) to dual-use certificate(s). */
     @Parameter(names = {"--certificate", "--cert", "-c"}, description="Path(s) to certificate(s) for signing and encryption")
@@ -177,274 +155,132 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
         return entityID;
     }
     
-    /**
-     * Do we output SAML 2.0 metadata?
-     * 
-     * @return argument value
-     */
-    public boolean isSAML2() {
-        return saml2;
-    }
-
-    /**
-     * Do we output CAS metadata?
-     * 
-     * @return argument value
-     */
-    public boolean isCAS() {
-        return cas;
-    }
-
-    /**
-     * Do we output OIDC/OAuth metadata?
-     * 
-     * @return argument value
-     */
-    public boolean isOIDC() {
-        return oidc;
-    }
-
-    /**
-     * Do we output SP role?
-     * 
-     * @return argument value
-     */
-    public boolean isSP() {
-        return sp;
-    }
-
-    /**
-     * Do we output IdP role?
-     * 
-     * @return argument value
-     */
-    public boolean isIDP() {
-        return idp;
-    }
-
-    /**
-     * Do we output AA role?
-     * 
-     * @return argument value
-     */
-    public boolean isAA() {
-        return aa;
-    }
-    
-    /**
-     * Get endpoint expression for {@link SingleSignOnService} endpoints using SAML 2.0 HTTP-Redirect binding.
-     * 
-     * <p>Endpoints may omit scheme but must specify port and full path.</p>
-     * 
-     * @return endpoint URL
-     */
-    @Nullable public String getSSORedirectEndpoint() {
-        return getProcessedEndpoint(ssoRedirect);
-    }
+    /** {@inheritDoc} */
+    @Nullable public SPSSODescriptor getSPSSODescriptor() {
+        if (!sp) {
+            return null;
+        }
+        
+        final XMLObjectBuilderFactory bf = XMLObjectProviderRegistrySupport.getBuilderFactory();
+        
+        final SAMLObjectBuilder<SPSSODescriptor> builder =
+                (SAMLObjectBuilder<SPSSODescriptor>) bf.<SPSSODescriptor>ensureBuilder(
+                        SPSSODescriptor.DEFAULT_ELEMENT_NAME);
+        final SPSSODescriptor role = builder.buildObject();
+        
+        final List<String> protocols = new ArrayList<>();
+        
+        role.getSingleLogoutServices().addAll(
+                convertEndpoints(new SingleLogoutServiceConverter(), protocols, logoutServices));
+        role.getArtifactResolutionServices().addAll(
+                convertEndpoints(new ArtifactResolutionServiceConverter(), protocols, artifactServices));
+        role.getAssertionConsumerServices().addAll(
+                convertEndpoints(new AssertionConsumerServiceConverter(), protocols, acServices));
+        
+        int index = 1;
+        for (final IndexedEndpoint e : role.getArtifactResolutionServices()) {
+            e.setIndex(index++);
+        }
 
-    /**
-     * Get endpoint expression for {@link SingleSignOnService} endpoint using SAML 2.0 HTTP-POST binding.
-     * 
-     * <p>Endpoints may omit scheme but must specify port and full path.</p>
-     * 
-     * @return endpoint URL
-     */
-    @Nullable public String getSSOPostEndpoint() {
-        return getProcessedEndpoint(ssoPost);
-    }
-    
-    /**
-     * Get endpoint expression for {@link SingleSignOnService} endpoint using SAML 2.0 HTTP-Artifact binding.
-     * 
-     * <p>Endpoints may omit scheme but must specify port and full path.</p>
-     * 
-     * @return endpoint URL
-     */
-    @Nullable public  String getSSOArtifactEndpoint() {
-        return getProcessedEndpoint(ssoArtifact);
-    }
+        index = 1;
+        for (final IndexedEndpoint e : role.getAssertionConsumerServices()) {
+            e.setIndex(index++);
+        }
+        
+        protocols.forEach(role::addSupportedProtocol);
 
-    /**
-     * Get endpoint expression for {@link SingleSignOnService} endpoint using SAML 2.0 SOAP binding.
-     * 
-     * <p>Endpoints may omit scheme but must specify port and full path.</p>
-     * 
-     * @return endpoint URL
-     */
-    @Nullable public String getSSOSoapEndpoint() {
-        return getProcessedEndpoint(ssoSoap);
+        return role;
     }
 
-    /**
-     * Get endpoint expression for {@link AttributeService} endpoint using SAML 2.0 SOAP binding.
-     * 
-     * <p>Endpoints may omit scheme but must specify port and full path.</p>
-     * 
-     * @return endpoint URL
-     */
-    @Nullable public String getAttributeSoapEndpoint() {
-        return getProcessedEndpoint(attributeSoap);
-    }
+    /** {@inheritDoc} */
+    @Nullable public IDPSSODescriptor getIDPSSODescriptor() {
+        if (!idp) {
+            return null;
+        }
+        
+        final XMLObjectBuilderFactory bf = XMLObjectProviderRegistrySupport.getBuilderFactory();
 
-    /**
-     * Get endpoint expression for {@link ArtifactResolutionService} endpoint(s) using SAML 2.0 SOAP binding.
-     * 
-     * <p>Endpoints may omit scheme but must specify port and full path.</p>
-     * 
-     * @return endpoint URLs
-     */
-    @Nullable @Unmodifiable @NotLive public List<String> getArtifactSoapEndpoints() {
-        return artifactSoap.stream()
-                .map(this::getProcessedEndpoint)
-                .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
-    }
+        final SAMLObjectBuilder<IDPSSODescriptor> builder =
+                (SAMLObjectBuilder<IDPSSODescriptor>) bf.<IDPSSODescriptor>ensureBuilder(
+                        IDPSSODescriptor.DEFAULT_ELEMENT_NAME);
+        final IDPSSODescriptor role = builder.buildObject();
 
-    /**
-     * Get endpoint expression(s) for {@link SingleLogoutService} endpoint(s) using SAML 2.0 HTTP-Redirect binding.
-     * 
-     * <p>Endpoints may omit scheme but must specify port and full path.</p>
-     * 
-     * @return endpoint URLs
-     */
-    @Nonnull @Unmodifiable @NotLive public List<String> getLogoutRedirectEndpoints() {
-        return logoutRedirect.stream()
-                .map(this::getProcessedEndpoint)
-                .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
-    }
+        final List<String> protocols = new ArrayList<>();
 
-    /**
-     * Get endpoint expression(s) for {@link SingleLogoutService} endpoint(s) using SAML 2.0 HTTP-POST binding.
-     * 
-     * <p>Endpoints may omit scheme but must specify port and full path.</p>
-     * 
-     * @return endpoint URLs
-     */
-    @Nonnull @Unmodifiable @NotLive public List<String> getLogoutPostEndpoints() {
-        return logoutPost.stream()
-                .map(this::getProcessedEndpoint)
-                .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
-    }
+        role.getSingleLogoutServices().addAll(
+                convertEndpoints(new SingleLogoutServiceConverter(), protocols, logoutServices));
+        role.getSingleSignOnServices().addAll(
+                convertEndpoints(new SingleSignOnServiceConverter(), protocols, ssoServices));
+        role.getArtifactResolutionServices().addAll(
+                convertEndpoints(new ArtifactResolutionServiceConverter(), protocols, artifactServices));
 
-    /**
-     * Get endpoint expression(s) for {@link SingleLogoutService} endpoint(s) using SAML 2.0 HTTP-Artifact binding.
-     * 
-     * <p>Endpoints may omit scheme but must specify port and full path.</p>
-     * 
-     * @return endpoint URLs
-     */
-    @Nonnull @Unmodifiable @NotLive public List<String> getLogoutArtifactEndpoints() {
-        return logoutArtifact.stream()
-                .map(this::getProcessedEndpoint)
-                .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
-    }
+        int index = 1;
+        for (final IndexedEndpoint e : role.getArtifactResolutionServices()) {
+            e.setIndex(index++);
+        }
+        
+        protocols.forEach(role::addSupportedProtocol);
 
-    /**
-     * Get endpoint expression(s) for {@link SingleLogoutService} endpoint(s) using SAML 2.0 SOAP binding.
-     * 
-     * <p>Endpoints may omit scheme but must specify port and full path.</p>
-     * 
-     * @return endpoint URLs
-     */
-    @Nonnull @Unmodifiable @NotLive public List<String> getLogoutSoapEndpoints() {
-        return logoutSoap.stream()
-                .map(this::getProcessedEndpoint)
-                .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
+        return role;
     }
 
-    /**
-     * Get endpoint expression(s) for {@link AssertionConsumerService} endpoint(s) using SAML 2.0 HTTP-POST binding.
-     * 
-     * <p>Endpoints may omit scheme but must specify port and full path.</p>
-     * 
-     * @return endpoint URLs
-     */
-    @Nonnull @Unmodifiable @NotLive public List<String> getACSPostEndpoints() {
-        return acsPost.stream()
-                .map(this::getProcessedEndpoint)
-                .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
-    }
+    /** {@inheritDoc} */
+    @Nullable public AttributeAuthorityDescriptor getAttributeAuthorityDescriptor() {
+        if (!aa) {
+            return null;
+        }
 
-    /**
-     * Get endpoint expression(s) for {@link AssertionConsumerService} endpoint(s) using SAML 2.0 HTTP-Artifact binding.
-     * 
-     * <p>Endpoints may omit scheme but must specify port and full path.</p>
-     * 
-     * @return endpoint URLs
-     */
-    @Nonnull @Unmodifiable @NotLive public List<String> getACSArtifactEndpoints() {
-        return acsArtifact.stream()
-                .map(this::getProcessedEndpoint)
-                .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
-    }
+        final XMLObjectBuilderFactory bf = XMLObjectProviderRegistrySupport.getBuilderFactory();
+        
+        final SAMLObjectBuilder<AttributeAuthorityDescriptor> builder =
+                (SAMLObjectBuilder<AttributeAuthorityDescriptor>) bf.<AttributeAuthorityDescriptor>ensureBuilder(
+                        AttributeAuthorityDescriptor.DEFAULT_ELEMENT_NAME);
+        final AttributeAuthorityDescriptor role = builder.buildObject();
+        
+        final List<String> protocols = new ArrayList<>();
 
-    /**
-     * Get endpoint expression(s) for {@link AssertionConsumerService} endpoint(s) using SAML 2.0 PAOS binding.
-     * 
-     * <p>Endpoints may omit scheme but must specify port and full path.</p>
-     * 
-     * @return endpoint URLs
-     */
-    @Nonnull @Unmodifiable @NotLive public List<String> getACSPaosEndpoints() {
-        return acsPaos.stream()
-                .map(this::getProcessedEndpoint)
-                .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
+        role.getAttributeServices().addAll(
+                convertEndpoints(new AttributeServiceConverter(), protocols, attributeServices));
+        
+        protocols.forEach(role::addSupportedProtocol);
+        
+        return role;
     }
-
+    
     /**
      * Dual-use certificates.
      * 
      * @return base64-encoded certificates
-     * 
-     * @throws CertificateException if unable to decode
-     * @throws EncodingException if unable to encode
      */
-    @Nullable @NotLive public List<String> getCertificates() throws CertificateException, EncodingException {
-        final List<String> encoded = new ArrayList<>();
-        
-        for (final String c : certificatePaths) {
-            assert c != null;
-            encoded.add(getEncodedCertificate(c));
-        }
-        
-        return encoded;
+    @Nonnull @Unmodifiable @NotLive public List<String> getCertificates() {
+        return certificatePaths.stream()
+                .map(this::getEncodedCertificate)
+                .filter(Predicates.notNull())
+                .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
     }
 
     /**
      * Signing-only certificate path(s).
      * 
      * @return base64-encoded certificates
-     * 
-     * @throws CertificateException if unable to decode
-     * @throws EncodingException if unable to encode
      */
-    @Nullable @NotLive public List<String> getSigningCertificates() throws CertificateException, EncodingException {
-        final List<String> encoded = new ArrayList<>();
-        
-        for (final String c : signingPaths) {
-            assert c != null;
-            encoded.add(getEncodedCertificate(c));
-        }
-        
-        return encoded;
+    @Nonnull @Unmodifiable @NotLive public List<String> getSigningCertificates() {
+        return signingPaths.stream()
+                .map(this::getEncodedCertificate)
+                .filter(Predicates.notNull())
+                .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
     }
 
     /**
      * Encryption-only certificate path(s).
      * 
      * @return base64-encoded certificates
-     * 
-     * @throws CertificateException if unable to decode
-     * @throws EncodingException if unable to encode
      */
-    @Nullable @NotLive public List<String> getEncryptionCertificates() throws CertificateException, EncodingException {
-        final List<String> encoded = new ArrayList<>();
-        
-        for (final String c : encryptionPaths) {
-            assert c != null;
-            encoded.add(getEncodedCertificate(c));
-        }
-        
-        return encoded;
+    @Nonnull @Unmodifiable @NotLive public List<String> getEncryptionCertificates() {
+        return encryptionPaths.stream()
+                .map(this::getEncodedCertificate)
+                .filter(Predicates.notNull())
+                .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
     }
 
     /**
@@ -454,10 +290,15 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
      * 
      * @return argument value
      */
-    public boolean isOmitNamespaces() {
+    public boolean isOmitNamespaceDeclarations() {
         return omitNamespaces;
     }
     
+    /** {@inheritDoc} */
+    @Nullable public Set<Namespace> getAdditionalNamespaces() {
+        return CollectionSupport.singleton(new Namespace("urn:mace:shibboleth:metadata:oidc:1.0", "oidcmd"));
+    }
+    
     /**
      * Output file path (stdout used otherwise).
      * 
@@ -467,27 +308,6 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
         return outputFile;
     }
     
-    // Computed values.
-    
-    /**
-     * Compute the appropriate protocol support string.
-     * 
-     * @return the protocolSupportEnumeration constant
-     */
-    @Nonnull public String getProtocolSupportEnumeration() {
-        final List<String> protocolSupportEnum = new ArrayList<>();
-        if (isSAML2()) {
-            protocolSupportEnum.add(SAMLConstants.SAML20P_NS);
-        }
-        if (isCAS()) {
-            protocolSupportEnum.add(AbstractProtocolConfiguration.PROTOCOL_URI);
-        }
-        if (isOIDC()) {
-            protocolSupportEnum.add("http://openid.net/specs/openid-connect-core-1_0.html");
-        }
-        return StringSupport.listToStringValue(protocolSupportEnum, " ");
-    }
-
     /** {@inheritDoc} */
     @Override
     @Nonnull public synchronized Logger getLog() {
@@ -501,10 +321,6 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
     /** {@inheritDoc} */
     @Override
     public void validate() throws IllegalArgumentException {
-        if (!saml2 && !cas && !oidc) {
-            saml2 = true;
-        }
-        
         if (!sp && !idp && !aa) {
             sp = true;
         }
@@ -514,32 +330,17 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
     @Override
     public void printHelp(@Nonnull final PrintStream out) {
         super.printHelp(out);
-        out.println(String.format("  %-20s %s", "--entityID, --id", "The entityID (or client_id, etc.)"));
-        
-        out.println(String.format("  %-20s %s", "--saml2", "Output SAML 2.0 metadata."));
-        out.println(String.format("  %-20s %s", "--cas", "Output CAS metadata."));
-        out.println(String.format("  %-20s %s", "--oidc", "Output OIDC metadata."));
+        out.println(String.format("  %-20s %s", "--entityID, --client_id, --id", "The entityID (or client_id, etc.)"));
 
         out.println(String.format("  %-20s %s", "--sp", "Output SP role."));
-        out.println(String.format("  %-20s %s", "--idp", "Ou            tput IdP role."));
+        out.println(String.format("  %-20s %s", "--idp", "Output IdP role."));
         out.println(String.format("  %-20s %s", "--aa", "Output Attribute Authority role."));
 
-        out.println(String.format("  %-20s %s", "--sso-redirect", "Endpoint for SAML 2.0 SSO HTTP-Redirect endpoint"));
-        out.println(String.format("  %-20s %s", "--sso-post", "Endpoint for SAML 2.0 SSO HTTP-POST endpoint"));
-        out.println(String.format("  %-20s %s", "--sso-artifact", "Endpoint for SAML 2.0 SSO HTTP-Artifact endpoint"));
-        out.println(String.format("  %-20s %s", "--sso-soap", "Endpoint for SAML 2.0 SSO SOAP endpoint"));
-
-        out.println(String.format("  %-20s %s", "--artifact-soap, --artifact", "Endpoint for SAML 2.0 Artifact Resolution SOAP endpoint"));
-        out.println(String.format("  %-20s %s", "--attr-soap", "Endpoint for SAML 2.0 Attribute Query SOAP endpoint"));
-
-        out.println(String.format("  %-20s %s", "--logout-redirect", "Endpoint for SAML 2.0 SLO HTTP-Redirect endpoint"));
-        out.println(String.format("  %-20s %s", "--logout-post", "Endpoint for SAML 2.0 SLO HTTP-POST endpoint"));
-        out.println(String.format("  %-20s %s", "--logout-artifact", "Endpoint for SAML 2.0 SLO HTTP-Artifact endpoint"));
-        out.println(String.format("  %-20s %s", "--logout-soap", "Endpoint for SAML 2.0 SLO SOAP endpoint"));
-
-        out.println(String.format("  %-20s %s", "--acs, --acs-post, -h", "Endpoint for SAML 2.0 ACS HTTP-POST endpoint"));
-        out.println(String.format("  %-20s %s", "--acs-artifact", "Endpoint for SAML 2.0 ACS HTTP-Artifact endpoint"));
-        out.println(String.format("  %-20s %s", "--ecp, --acs-paos", "Endpoint for SAML 2.0 ACS ECP/PAOS endpoint"));
+        out.println(String.format("  %-20s %s", "--sso", "Binding/Endpoint for SingleSignOnService"));
+        out.println(String.format("  %-20s %s", "--logout, --slo", "Binding/Endpoint for SingleLogoutService"));
+        out.println(String.format("  %-20s %s", "--artifact", "Binding/Endpoint for ArtifactResolutionService"));
+        out.println(String.format("  %-20s %s", "--attribute-query, --query", "Binding/Endpoint for AttributeService"));
+        out.println(String.format("  %-20s %s", "--acs", "Binding/Endpoint for AssertionConsumerService"));
 
         out.println(String.format("  %-20s %s", "--certificate,  --cert, -c", "Path to dual-use certificate."));
         out.println(String.format("  %-20s %s", "--signing, -x", "Path to signing certificate."));
@@ -556,34 +357,33 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
      * @param path certificate file path
      * 
      * @return encoded string
-     * 
-     * @throws CertificateException if unable to decode
-     * @throws EncodingException if unable to encode
      */
-    @Nonnull private String getEncodedCertificate(@Nonnull final String path)
-            throws CertificateException, EncodingException {
-        final X509Certificate cert = X509Support.decodeCertificate(new File(path));
-        return Base64Support.encode(cert.getEncoded(), true);
+    @Nullable private String getEncodedCertificate(@Nonnull final String path) {
+        try {
+            final X509Certificate cert = X509Support.decodeCertificate(new File(path));
+            return Base64Support.encode(cert.getEncoded(), true);
+        } catch (final CertificateException | EncodingException e) {
+            getLog().warn("Unable to decode and re-encode certificate at path {}", path, e);
+            return null;
+        }
     }
-
+    
     /**
-     * Process an endpoint expression into an absolute URL.
-     * 
-     * <p>For now, this merely detects the http schemes and if absent, adds the https scheme.</p>
+     * Convert the expressions into endpoints.
      * 
-     * @param endpoint input expression
+     * @param <T> endpoint type
+     * @param converter endpoint converter
+     * @param protocols accumulator for protocol support values
+     * @param input raw argument list
      * 
-     * @return an absolute URL or null if input was null
+     * @return converted endpoints
      */
-    @Nullable private String getProcessedEndpoint(@Nullable final String endpoint) {
-        if (endpoint == null) {
-            return null;
-        }
-        
-        if (endpoint.startsWith("https://") || endpoint.startsWith("http://")) {
-            return endpoint;
-        }
-        return "https://" + endpoint;
+    @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<String> input) {
+        return input.stream()
+                .map(s -> converter.apply(s, protocols))
+                .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
     }
     
 }
\ No newline at end of file
diff --git a/metadatagen-impl/src/main/java/net/shibboleth/idp/plugin/metadatagen/impl/SingleLogoutServiceConverter.java b/metadatagen-impl/src/main/java/net/shibboleth/idp/plugin/metadatagen/impl/SingleLogoutServiceConverter.java
new file mode 100644
index 0000000..b756974
--- /dev/null
+++ b/metadatagen-impl/src/main/java/net/shibboleth/idp/plugin/metadatagen/impl/SingleLogoutServiceConverter.java
@@ -0,0 +1,61 @@
+/*
+ * 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.plugin.metadatagen.impl;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.saml.saml2.metadata.SingleLogoutService;
+
+import net.shibboleth.idp.cas.config.AbstractProtocolConfiguration;
+import net.shibboleth.shared.annotation.constraint.Live;
+import net.shibboleth.shared.collection.Pair;
+
+/**
+ * JCommander support for parsing a binding/endpoint pair into a {@link SingleLogoutService}.
+ */
+public class SingleLogoutServiceConverter extends org.opensaml.saml.metadata.generator.impl.SingleLogoutServiceConverter {
+    
+    /** {@inheritDoc} */
+    @Nonnull public SingleLogoutService apply(@Nullable final String value,
+            @Nullable @Live final List<String> protocols) {
+        return getProcessedEndpoint(protocols, value);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull protected Pair<String,String> getProtocolAndBinding(@Nonnull final String input) {
+        if (input.startsWith("CAS/")) {
+            return new Pair<>(AbstractProtocolConfiguration.PROTOCOL_URI,
+                    AbstractProtocolConfiguration.PROTOCOL_URI + "/logout");
+        }
+        return super.getProtocolAndBinding(input);
+    }
+ 
+    /** {@inheritDoc} */
+    @Override
+    @Nonnull protected String getLocation(@Nonnull final String input) {
+        if (input.startsWith("CAS/")) {
+            return "urn:mace:shibboleth:profile:CAS:logout";
+        }
+        return super.getLocation(input);
+    }
+    
+}
\ No newline at end of file
diff --git a/metadatagen-impl/src/main/resources/metadatagen-templates/AttributeAuthorityDescriptor.vm b/metadatagen-impl/src/main/resources/metadatagen-templates/AttributeAuthorityDescriptor.vm
deleted file mode 100644
index 2a2d21b..0000000
--- a/metadatagen-impl/src/main/resources/metadatagen-templates/AttributeAuthorityDescriptor.vm
+++ /dev/null
@@ -1,3 +0,0 @@
-    <md:AttributeAuthorityDescriptor protocolSupportEnumeration="$args.protocolSupportEnumeration">
-#parse("/metadatagen-templates/KeyDescriptors.vm")
-    </md:AttributeAuthorityDescriptor>
diff --git a/metadatagen-impl/src/main/resources/metadatagen-templates/EntityDescriptor.vm b/metadatagen-impl/src/main/resources/metadatagen-templates/EntityDescriptor.vm
deleted file mode 100644
index 49239da..0000000
--- a/metadatagen-impl/src/main/resources/metadatagen-templates/EntityDescriptor.vm
+++ /dev/null
@@ -1,13 +0,0 @@
-<md:EntityDescriptor#if (!$args.omitNamespaces)#foreach ($ns in $namespaces.entrySet()) xmlns:$ns.key="$ns.value"#end#end entityID="$args.entityID">
-
-#if ($args.IDP)
-#parse("/metadatagen-templates/IDPSSODescriptor.vm")
-#end
-#if ($args.AA)
-#parse("/metadatagen-templates/AttributeAuthorityDescriptor.vm")
-#end
-#if ($args.SP)
-#parse("/metadatagen-templates/SPSSODescriptor.vm")
-#end
-
-</md:EntityDescriptor>
diff --git a/metadatagen-impl/src/main/resources/metadatagen-templates/IDPSSODescriptor.vm b/metadatagen-impl/src/main/resources/metadatagen-templates/IDPSSODescriptor.vm
deleted file mode 100644
index b972947..0000000
--- a/metadatagen-impl/src/main/resources/metadatagen-templates/IDPSSODescriptor.vm
+++ /dev/null
@@ -1,4 +0,0 @@
-    <md:IDPSSODescriptor protocolSupportEnumeration="$args.protocolSupportEnumeration">
-#parse("/metadatagen-templates/KeyDescriptors.vm")
-#parse("/metadatagen-templates/SingleLogoutServices.vm")
-    </md:IDPSSODescriptor>
diff --git a/metadatagen-impl/src/main/resources/metadatagen-templates/KeyDescriptors.vm b/metadatagen-impl/src/main/resources/metadatagen-templates/KeyDescriptors.vm
deleted file mode 100644
index 0907e7d..0000000
--- a/metadatagen-impl/src/main/resources/metadatagen-templates/KeyDescriptors.vm
+++ /dev/null
@@ -1,30 +0,0 @@
-#foreach ($cert in $args.certificates)        <md:KeyDescriptor>
-            <ds:KeyInfo>
-                <ds:X509Data>
-                    <ds:X509Certificate>
-$cert
-                    </ds:X509Certificate>
-                </ds:X509Data>
-            </ds:KeyInfo>
-        </md:KeyDescriptor>
-#end
-#foreach ($cert in $args.signingCertificates)        <md:KeyDescriptor use="signing">
-            <ds:KeyInfo>
-                <ds:X509Data>
-                    <ds:X509Certificate>
-$cert
-                    </ds:X509Certificate>
-                </ds:X509Data>
-            </ds:KeyInfo>
-        </md:KeyDescriptor>
-#end
-#foreach ($cert in $args.encryptionCertificates)        <md:KeyDescriptor use="encryption">
-            <ds:KeyInfo>
-                <ds:X509Data>
-                    <ds:X509Certificate>
-$cert
-                    </ds:X509Certificate>
-                </ds:X509Data>
-            </ds:KeyInfo>
-        </md:KeyDescriptor>
-#end
diff --git a/metadatagen-impl/src/main/resources/metadatagen-templates/SPSSODescriptor.vm b/metadatagen-impl/src/main/resources/metadatagen-templates/SPSSODescriptor.vm
deleted file mode 100644
index 4e5c076..0000000
--- a/metadatagen-impl/src/main/resources/metadatagen-templates/SPSSODescriptor.vm
+++ /dev/null
@@ -1,18 +0,0 @@
-    <md:SPSSODescriptor protocolSupportEnumeration="$args.protocolSupportEnumeration">
-#parse("/metadatagen-templates/KeyDescriptors.vm")
-#foreach ($loc in $args.artifactSoapEndpoints)
-        <md:ArtifactResolutionService index="$foreach.count" Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="$loc" />
-#end
-#parse("/metadatagen-templates/SingleLogoutServices.vm")
-#set ($index = 0)
-#foreach ($loc in $args.ACSPostEndpoints)#set ( $index = $index + 1 )
-        <md:AssertionConsumerService index="$index" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="$loc" />
-#end
-#set ($index = $args.ACSPostEndpoints.size())
-#foreach ($loc in $args.ACSArtifactEndpoints)#set ( $index = $index + 1 )
-        <md:AssertionConsumerService index="$index" Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="$loc" />
-#end
-#foreach ($loc in $args.ACSPaosEndpoints)#set ( $index = $index + 1 )
-        <md:AssertionConsumerService index="$index" Binding="urn:oasis:names:tc:SAML:2.0:bindings:PAOS" Location="$loc" />
-#end
-    </md:SPSSODescriptor>
diff --git a/metadatagen-impl/src/main/resources/metadatagen-templates/SingleLogoutServices.vm b/metadatagen-impl/src/main/resources/metadatagen-templates/SingleLogoutServices.vm
deleted file mode 100644
index ef854c3..0000000
--- a/metadatagen-impl/src/main/resources/metadatagen-templates/SingleLogoutServices.vm
+++ /dev/null
@@ -1,12 +0,0 @@
-#foreach ($loc in $args.logoutRedirectEndpoints)
-        <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="$loc" />
-#end
-#foreach ($loc in $args.logoutPostEndpoints)
-        <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="$loc" />
-#end
-#foreach ($loc in $args.logoutArtifactEndpoints)
-        <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Location="$loc" />
-#end
-#foreach ($loc in $args.logoutSoapEndpoints)
-        <md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="$loc" />
-#end
diff --git a/metadatagen-impl/src/main/resources/net/shibboleth/idp/plugin/metadatagen/conf/velocity.xml b/metadatagen-impl/src/main/resources/net/shibboleth/idp/plugin/metadatagen/conf/velocity.xml
index 449d116..b4471ec 100644
--- a/metadatagen-impl/src/main/resources/net/shibboleth/idp/plugin/metadatagen/conf/velocity.xml
+++ b/metadatagen-impl/src/main/resources/net/shibboleth/idp/plugin/metadatagen/conf/velocity.xml
@@ -37,6 +37,8 @@
     <bean id="shibboleth.IdentifiableBeanPostProcessor"
         class="net.shibboleth.shared.spring.config.IdentifiableBeanPostProcessor" />
 
+    <bean id="shibboleth.OpenSAMLConfig" class="net.shibboleth.profile.spring.impl.OpenSAMLConfigBean" />
+
     <util:map id="shibboleth.DefaultVelocityEngineProperties">
         <entry key="parser.space_gobbling" value="%{idp.velocity.space.gobbling:bc}" />
         <entry key="resource.loaders" value="file, classpath" />
@@ -48,6 +50,7 @@
     </util:map>
 
     <bean id="shibboleth.VelocityEngine" class="net.shibboleth.shared.spring.velocity.VelocityEngineFactoryBean"
+        depends-on="shibboleth.OpenSAMLConfig"
         p:velocityPropertiesMap="#{getObject('shibboleth.VelocityEngineProperties') ?: getObject('shibboleth.DefaultVelocityEngineProperties')}" />
 
  </beans>
diff --git a/metadatagen-impl/src/test/java/net/shibboleth/idp/plugin/metadatagen/impl/MetadataGenTest.java b/metadatagen-impl/src/test/java/net/shibboleth/idp/plugin/metadatagen/impl/MetadataGenTest.java
index 63e6542..a623f2e 100644
--- a/metadatagen-impl/src/test/java/net/shibboleth/idp/plugin/metadatagen/impl/MetadataGenTest.java
+++ b/metadatagen-impl/src/test/java/net/shibboleth/idp/plugin/metadatagen/impl/MetadataGenTest.java
@@ -19,15 +19,6 @@ package net.shibboleth.idp.plugin.metadatagen.impl;
 
 import static org.testng.Assert.assertEquals;
 
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-
-import org.springframework.core.io.ClassPathResource;
 import org.testng.annotations.Test;
 
 import net.shibboleth.shared.cli.AbstractCommandLine;
@@ -41,74 +32,28 @@ public class MetadataGenTest {
     //      src/test/resources/net/shibboleth/idp/plugin/metadatagen/impl/extra2
     private final static String IDP_HOME =  "/Users/scantor/Documents/shibboleth5/java-identity-provider/idp-conf-impl/src/main/resources/net/shibboleth/idp/module";
     private final boolean enabled = false;
-    
-    @Test(enabled = enabled) public void test() throws IOException {
-        assertEquals(MetadataGenCLI.runMain(
-                    new String[] {
-                            "--home", IDP_HOME,
-                            "--verbose",
-                            "--DNSName", "my.idp.example.org",
-                            "+saml1",
-                            "--backChannel", IDP_HOME + "/credentials/idp-backchannel.crt",
-                            "+attributeFetch","+artifact", "+logout",
-                            }),
-                AbstractCommandLine.RC_OK);
-    }
-    
-    @Test(enabled = enabled) public void testProps() throws IOException {
-        final File file1 = Files.createTempFile(getClass().getName(), ".props").toFile();
-        file1.deleteOnExit();
-        final ClassPathResource res1 = new ClassPathResource("/net/shibboleth/idp/plugin/metadatagen/impl/extra1");
-        try (final InputStream stream = res1.getInputStream(); final OutputStream out = new BufferedOutputStream(new FileOutputStream(file1))) {
-            stream.transferTo(out);
-        }
-        final File file2 = Files.createTempFile(getClass().getName(), ".props").toFile();
-        file2.deleteOnExit();
-        final ClassPathResource res2 = new ClassPathResource("/net/shibboleth/idp/plugin/metadatagen/impl/extra2");
-        try (final InputStream stream = res2.getInputStream(); final OutputStream out = new BufferedOutputStream(new FileOutputStream(file2))) {
-            stream.transferTo(out);
-        }
-        
-        assertEquals(MetadataGenCLI.runMain(
-                    new String[] {
-                            "--home", IDP_HOME,
-                            "--verbose",
-                            "+saml1",
-                            "--propertyFiles", file1.getPath() +","+ file2.getPath(),
-                            "+attributeFetch","+artifact", "+logout",
-                            }),
-                AbstractCommandLine.RC_OK);
-    }
-    
-    @Test(enabled = enabled) public void noBc() throws IOException {
-        assertEquals(MetadataGenCLI.runMain(
-                    new String[] {
-                            "--home", IDP_HOME,
-                            "--verbose", "+logout",
-                            "+saml1"}),
-                AbstractCommandLine.RC_OK);
-    }
-
-    @Test(enabled = enabled) public void help() throws IOException {
-        assertEquals(MetadataGenCLI.runMain(
-                    new String[] {
-                            "--home", IDP_HOME,
-                            "--help"}),
-                AbstractCommandLine.RC_OK);
-    }
 
-    @Test(enabled = true) public void testSimple() {
+    @Test(enabled = enabled) public void testSimple() {
         assertEquals(MetadataGenCLI.runMain(
                 new String[] {
                         "--home", IDP_HOME,
                         "--sp",
+                        "--idp",
+                        "--aa",
                         "--entityID", "https://sp.example.org",
                         "--cert", "/Users/scantor/Desktop/webauth2.crt",
-                        "--logout-redirect", "sp.example.org/Shibboleth.sso/SLO/Redirect",
-                        "--logout-redirect", "sp2.example.org/Shibboleth.ssoSLO/Redirect",
-                        "-h", "sp.example.org/Shibboleth.sso/SAML2/POST",
-                        "-h", "sp2.example.org/Shibboleth.sso/SAML2/POST",
-                        "--acs-paos", "sp.example.org/Shibboleth.sso/SAML2/ECP",
+                        "--sso", "Redirect1/idp.example.org/idp/profile/SAML/SSO/Redirect",
+                        "--sso", "Redirect/idp.example.org/idp/profile/SAML2/SSO/Redirect",
+                        "--sso", "POST/idp.example.org/idp/profile/SAML2/SSO/POST",
+                        "--sso", "SOAP/idp.example.org/idp/profile/SAML2/SSO/SOAP",
+                        "--query", "SOAP/idp.example.org:8443/idp/profile/SAML2/AttributeQuery/SOAP",
+                        "--query", "SOAP1/idp.example.org:8443/idp/profile/SAML/AttributeQuery/SOAP",
+                        "--logout", "Redirect/sp.example.org/Shibboleth.sso/SLO/Redirect",
+                        "--logout", "Artifact/sp.example.org/Shibboleth.sso/SLO/Artifact",
+                        "--acs", "POST1/sp.example.org/Shibboleth.sso/SAML/POST",
+                        "--acs", "POST/sp.example.org/Shibboleth.sso/SAML2/POST",
+                        "--acs", "POST/sp2.example.org/Shibboleth.sso/SAML2/POST",
+                        "--acs", "PAOS/sp.example.org/Shibboleth.sso/SAML2/ECP",
                         }),
             AbstractCommandLine.RC_OK);
     }

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list