[java-opensaml] branch main updated: JMETAGEN-5 - Add Velocity-based metadata generator to OpenSAML

Scott Cantor cantor.2 at osu.edu
Mon Jul 10 19:17:09 UTC 2023


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

scantor pushed a commit to branch main
in repository java-opensaml.

View the commit online:
http://git.shibboleth.net/view/?p=java-opensaml.git;a=commit;h=4ba3f15b243dde6747fda9bc859eb48f6f3fc98e

The following commit(s) were added to refs/heads/main by this push:
     new 4ba3f15b2 JMETAGEN-5 - Add Velocity-based metadata generator to OpenSAML
4ba3f15b2 is described below

commit 4ba3f15b243dde6747fda9bc859eb48f6f3fc98e
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Jul 10 15:17:05 2023 -0400

    JMETAGEN-5 - Add Velocity-based metadata generator to OpenSAML
    
    https://shibboleth.atlassian.net/browse/JMETAGEN-5
    
    Drafty implementation of template-driven generation.
    Converters that parse endpoint expressions to deduce bindings/protocols.
---
 .../generator/impl/AbstractEndpointConverter.java  | 178 +++++++++++++++++++++
 .../impl/ArtifactResolutionServiceConverter.java   |  53 ++++++
 .../impl/AssertionConsumerServiceConverter.java    |  53 ++++++
 .../generator/impl/AttributeServiceConverter.java  |  53 ++++++
 .../metadata/generator/impl/MetadataGenerator.java |  47 ++++++
 .../impl/MetadataGeneratorParameters.java          | 117 ++++++++++++++
 .../impl/SingleLogoutServiceConverter.java         |  53 ++++++
 .../impl/SingleSignOnServiceConverter.java         |  53 ++++++
 .../impl/TemplateMetadataGeneratorParameters.java  |  40 +++++
 .../generator/impl/VelocityMetadataGenerator.java  | 121 ++++++++++++++
 .../saml/metadata/generator/impl/package-info.java |  24 +++
 .../metadata/AttributeAuthorityDescriptor.vm       |   7 +
 .../templates/metadata/EntityDescriptor.vm         |  13 ++
 .../templates/metadata/IDPSSODescriptor.vm         |  13 ++
 .../resources/templates/metadata/KeyDescriptors.vm |  30 ++++
 .../templates/metadata/SPSSODescriptor.vm          |  13 ++
 16 files changed, 868 insertions(+)

diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/AbstractEndpointConverter.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/AbstractEndpointConverter.java
new file mode 100644
index 000000000..aecdea380
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/AbstractEndpointConverter.java
@@ -0,0 +1,178 @@
+/*
+ * 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 org.opensaml.saml.metadata.generator.impl;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.common.xml.SAMLConstants;
+import org.opensaml.saml.saml2.metadata.Endpoint;
+
+import net.shibboleth.shared.annotation.constraint.Live;
+import net.shibboleth.shared.collection.Pair;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * Support for parsing a binding/endpoint pair into an endpoint of a particular type.
+ * 
+ * <p>The input format is a binding token, forward slash, and an endpoint. The endpoint MAY omit the
+ * scheme, in which case 'https://' is prepended.</p>
+ * 
+ * <p>The SAML 2.0 bindings are represented by the tokens
+ * "Redirect", "POST", "SimpleSign", "Artifact", "SOAP", and "PAOS".</p>
+ * 
+ * <p>The SAML 1.1 bindings are represented by the tokens
+ * "Redirect1", "POST1", "Artifact1", and "SOAP1". The first token
+ * is actually the proprietary Shibboleth request protocol.</p>
+ * 
+ * <p>The second input parameter is mutated to maintain the list of protocols applicable to the surrounding role.</p>
+ * 
+ * @param <T> endpoint type
+ * 
+ * @since 5.0.0
+ */
+public abstract class AbstractEndpointConverter<T extends Endpoint> implements BiFunction<String,List<String>,T> {
+
+    /** Map of binding shortcuts to constants. */
+    @Nonnull private static Map<String,Pair<String,String>> bindingMap;
+
+    /** Object builder. */
+    @Nonnull private final SAMLObjectBuilder<T> builder;
+    
+    /**
+     * Constructor.
+     *
+     * @param theBuilder builder to use
+     */
+    public AbstractEndpointConverter(@Nonnull final SAMLObjectBuilder<T> theBuilder) {
+        builder = Constraint.isNotNull(theBuilder, "Builder cannot be 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>
+     * 
+     * @param protocols live list of protocol strings
+     * @param input the argument
+     * 
+     * @return the endpoint object
+     */
+    @Nonnull protected T getProcessedEndpoint(@Nullable @Live final List<String> protocols,
+            @Nullable final String input) {
+        
+        if (input == null) {
+            throw new IllegalArgumentException("Argument was null");
+        }
+        
+        final Pair<String,String> pair = getProtocolAndBinding(input);
+        final String loc = getLocation(input);
+        
+        final T endpoint = builder.buildObject();
+        
+        if (loc.startsWith("https://") || loc.startsWith("http://")) {
+            endpoint.setLocation(loc);
+        } else {
+            endpoint.setLocation("https://" + loc);
+        }
+        
+        endpoint.setBinding(pair.getSecond());
+        
+        if (protocols != null) {
+            protocols.add(pair.getFirst());
+        }
+        
+        return endpoint;
+    }
+    
+    /**
+     * Parse out the binding shortcut and map to a protocol and binding constant.
+     * 
+     * @param input the argument
+     * 
+     * @return the mapped constant
+     */
+    @Nonnull protected Pair<String,String> getProtocolAndBinding(@Nonnull final String input) {
+        final int sep = input.indexOf('/');
+        if (sep == -1) {
+            throw new IllegalArgumentException("No separator found in string.");
+        }
+        
+        final Pair<String,String> binding;
+        synchronized(bindingMap) {
+            binding = bindingMap.get(input.substring(0, sep));
+        }
+        if (binding == null) {
+            throw new IllegalArgumentException("Binding " + input.substring(0, sep) + " did not match a known value.");
+        }
+        
+        return binding;
+    }
+
+    /**
+     * Parse out the endpoint location.
+     * 
+     * @param input the argument
+     * 
+     * @return the endpoint location
+     */
+    @Nonnull protected String getLocation(@Nonnull final String input) {
+        final int sep = input.indexOf('/');
+        if (sep == -1 || input.length() == sep + 1) {
+            throw new IllegalArgumentException("No separator found in string.");
+        }
+        
+        return input.substring(sep + 1);
+    }
+    
+    /**
+     * Add a new mapping to the static set of protocol/binding mappings. 
+     * 
+     * @param token token used in strings converted into endpoints
+     * @param protocol protocol support string for binding
+     * @param binding binding constant
+     */
+    public static void addBinding(@Nonnull final String token, @Nonnull final String protocol,
+            @Nonnull final String binding) {
+        synchronized(bindingMap) {
+            bindingMap.put(token, new Pair<>(protocol, binding));
+        }
+    }
+    
+    static {
+        bindingMap = new HashMap<>();
+        bindingMap.put("Redirect", new Pair<>(SAMLConstants.SAML20P_NS, SAMLConstants.SAML2_REDIRECT_BINDING_URI));
+        bindingMap.put("POST", new Pair<>(SAMLConstants.SAML20P_NS, SAMLConstants.SAML2_POST_BINDING_URI));
+        bindingMap.put("SimpleSign",
+                new Pair<>(SAMLConstants.SAML20P_NS, SAMLConstants.SAML2_POST_SIMPLE_SIGN_BINDING_URI));
+        bindingMap.put("Artifact", new Pair<>(SAMLConstants.SAML20P_NS, SAMLConstants.SAML2_ARTIFACT_BINDING_URI));
+        bindingMap.put("SOAP", new Pair<>(SAMLConstants.SAML20P_NS, SAMLConstants.SAML2_SOAP11_BINDING_URI));
+        bindingMap.put("PAOS", new Pair<>(SAMLConstants.SAML20P_NS, SAMLConstants.SAML2_PAOS_BINDING_URI));
+        
+        bindingMap.put("POST1", new Pair<>(SAMLConstants.SAML11P_NS, SAMLConstants.SAML1_POST_BINDING_URI));
+        bindingMap.put("Artifact1", new Pair<>(SAMLConstants.SAML11P_NS, SAMLConstants.SAML1_ARTIFACT_BINDING_URI));
+        bindingMap.put("SOAP1", new Pair<>(SAMLConstants.SAML11P_NS, SAMLConstants.SAML1_SOAP11_BINDING_URI));
+    }
+
+}
\ No newline at end of file
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/ArtifactResolutionServiceConverter.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/ArtifactResolutionServiceConverter.java
new file mode 100644
index 000000000..edc8b5238
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/ArtifactResolutionServiceConverter.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.opensaml.saml.metadata.generator.impl;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.metadata.ArtifactResolutionService;
+
+import net.shibboleth.shared.annotation.constraint.Live;
+
+/**
+ * Support for parsing a binding/endpoint pair into a {@link ArtifactResolutionService}.
+ * 
+ * @since 5.0.0
+ */
+public class ArtifactResolutionServiceConverter extends AbstractEndpointConverter<ArtifactResolutionService> {
+    
+    /**
+     * Constructor.
+     */
+    public ArtifactResolutionServiceConverter() {
+        super((SAMLObjectBuilder<ArtifactResolutionService>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<ArtifactResolutionService>ensureBuilder(
+                        ArtifactResolutionService.DEFAULT_ELEMENT_NAME));
+    }
+    
+    /** {@inheritDoc} */
+    @Nonnull public ArtifactResolutionService apply(@Nullable final String value,
+            @Nullable @Live final List<String> protocols) {
+        return getProcessedEndpoint(protocols, value);
+    }
+
+}
\ No newline at end of file
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/AssertionConsumerServiceConverter.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/AssertionConsumerServiceConverter.java
new file mode 100644
index 000000000..7a852a231
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/AssertionConsumerServiceConverter.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.opensaml.saml.metadata.generator.impl;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.metadata.AssertionConsumerService;
+
+import net.shibboleth.shared.annotation.constraint.Live;
+
+/**
+ * Support for parsing a binding/endpoint pair into a {@link AssertionConsumerService}.
+ * 
+ * @since 5.0.0
+ */
+public class AssertionConsumerServiceConverter extends AbstractEndpointConverter<AssertionConsumerService> {
+    
+    /**
+     * Constructor.
+     */
+    public AssertionConsumerServiceConverter() {
+        super((SAMLObjectBuilder<AssertionConsumerService>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<AssertionConsumerService>ensureBuilder(
+                        AssertionConsumerService.DEFAULT_ELEMENT_NAME));
+    }
+    
+    /** {@inheritDoc} */
+    @Nonnull public AssertionConsumerService apply(@Nullable final String value,
+            @Nullable @Live final List<String> protocols) {
+        return getProcessedEndpoint(protocols, value);
+    }
+
+}
\ No newline at end of file
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/AttributeServiceConverter.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/AttributeServiceConverter.java
new file mode 100644
index 000000000..bb2beba22
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/AttributeServiceConverter.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.opensaml.saml.metadata.generator.impl;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.metadata.AttributeService;
+
+import net.shibboleth.shared.annotation.constraint.Live;
+
+/**
+ * Support for parsing a binding/endpoint pair into a {@link AttributeService}.
+ * 
+ * @since 5.0.0
+ */
+public class AttributeServiceConverter extends AbstractEndpointConverter<AttributeService> {
+    
+    /**
+     * Constructor.
+     */
+    public AttributeServiceConverter() {
+        super((SAMLObjectBuilder<AttributeService>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<AttributeService>ensureBuilder(
+                        AttributeService.DEFAULT_ELEMENT_NAME));
+    }
+    
+    /** {@inheritDoc} */
+    @Nonnull public AttributeService apply(@Nullable final String value,
+            @Nullable @Live final List<String> protocols) {
+        return getProcessedEndpoint(protocols, value);
+    }
+
+}
\ No newline at end of file
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/MetadataGenerator.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/MetadataGenerator.java
new file mode 100644
index 000000000..6c50f6355
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/MetadataGenerator.java
@@ -0,0 +1,47 @@
+/*
+ * 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 org.opensaml.saml.metadata.generator.impl;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Interface to a component that generates SAML metadata.
+ * 
+ * <p>TODO: this will eventually migrate into the API.</p>
+ * 
+ * @since 5.0.0
+ */
+public interface MetadataGenerator {
+
+    /**
+     * Generate metadata using the supplied parameters into the supplied destination.
+     * 
+     * <p>The writer must be open and will not be closed by this method.</p>
+     * 
+     * @param params input parameters
+     * @param sink destination for output
+     * 
+     * @throws IOException on error
+     */
+    public void generate(@Nonnull final MetadataGeneratorParameters params, @Nonnull final Writer sink)
+        throws IOException;
+
+}
\ No newline at end of file
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/MetadataGeneratorParameters.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/MetadataGeneratorParameters.java
new file mode 100644
index 000000000..374f65d30
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/MetadataGeneratorParameters.java
@@ -0,0 +1,117 @@
+/*
+ * 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 org.opensaml.saml.metadata.generator.impl;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.Namespace;
+import org.opensaml.saml.saml2.metadata.AttributeAuthorityDescriptor;
+import org.opensaml.saml.saml2.metadata.IDPSSODescriptor;
+import org.opensaml.saml.saml2.metadata.SPSSODescriptor;
+
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+
+/**
+ * Inputs to metadata generation.
+ * 
+ * <p>TODO: This will eventually migrate into the API.</p>
+ * 
+ * @since 5.0.0
+ */
+public interface MetadataGeneratorParameters {
+
+    /**
+     * Get the unique ID.
+     * 
+     * @return the unique ID
+     */
+    @Nullable String getEntityID();
+
+    /**
+     * Whether to omit the namespace declarations on the root element.
+     * 
+     * @return true iff namespace declarations should be omitted
+     */
+    boolean isOmitNamespaceDeclarations();
+    
+    /**
+     * Get a set of additional namespaces to declare on root element.
+     * 
+     * @return additional namespaces
+     */
+    @Nullable Set<Namespace> getAdditionalNamespaces();
+
+    /**
+     * Get the SP role to generate.
+     * 
+     * <p>Only the endpoints and any basic flags are extracted from the role.</p> 
+     * 
+     * @return SP role or null
+     */
+    @Nullable SPSSODescriptor getSPSSODescriptor();
+    
+    /**
+     * Get the IdP role to generate.
+     * 
+     * <p>Only the endpoints and any basic flags are extracted from the role.</p>
+     *  
+     * @return IdP role or null
+     */
+    @Nullable IDPSSODescriptor getIDPSSODescriptor();
+
+    /**
+     * Get the AA role to generate.
+     * 
+     * <p>Only the endpoints and any basic flags are extracted from the role.</p>
+     *  
+     * @return AA role or null
+     */
+    @Nullable AttributeAuthorityDescriptor getAttributeAuthorityDescriptor();
+
+    /**
+     * Dual-use certificates.
+     * 
+     * <p>Keys will be applied to all roles.</p>
+     * 
+     * @return base64-encoded certificates
+     */
+    @Nonnull @Unmodifiable @NotLive List<String> getCertificates();
+
+    /**
+     * Signing-only certificate path(s).
+     * 
+     * <p>Keys will be applied to all roles.</p>
+     * 
+     * @return base64-encoded certificates
+     */
+    @Nonnull @Unmodifiable @NotLive List<String> getSigningCertificates();
+
+    /**
+     * Encryption-only certificate path(s).
+     * 
+     * <p>Keys will be applied to all roles.</p>
+     * 
+     * @return base64-encoded certificates
+     */
+    @Nonnull @Unmodifiable @NotLive List<String> getEncryptionCertificates();
+        
+}
\ No newline at end of file
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/SingleLogoutServiceConverter.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/SingleLogoutServiceConverter.java
new file mode 100644
index 000000000..14032822e
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/SingleLogoutServiceConverter.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.opensaml.saml.metadata.generator.impl;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.metadata.SingleLogoutService;
+
+import net.shibboleth.shared.annotation.constraint.Live;
+
+/**
+ * Support for parsing a binding/endpoint pair into a {@link SingleLogoutService}.
+ * 
+ * @since 5.0.0
+ */
+public class SingleLogoutServiceConverter extends AbstractEndpointConverter<SingleLogoutService> {
+    
+    /**
+     * Constructor.
+     */
+    public SingleLogoutServiceConverter() {
+        super((SAMLObjectBuilder<SingleLogoutService>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<SingleLogoutService>ensureBuilder(
+                        SingleLogoutService.DEFAULT_ELEMENT_NAME));
+    }
+    
+    /** {@inheritDoc} */
+    @Nonnull public SingleLogoutService apply(@Nullable final String value,
+            @Nullable @Live final List<String> protocols) {
+        return getProcessedEndpoint(protocols, value);
+    }
+    
+}
\ No newline at end of file
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/SingleSignOnServiceConverter.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/SingleSignOnServiceConverter.java
new file mode 100644
index 000000000..4b8ff8cc0
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/SingleSignOnServiceConverter.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.opensaml.saml.metadata.generator.impl;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.metadata.SingleSignOnService;
+
+import net.shibboleth.shared.annotation.constraint.Live;
+
+/**
+ * Support for parsing a binding/endpoint pair into a {@link SingleSignOnService}.
+ * 
+ * @since 5.0.0
+ */
+public class SingleSignOnServiceConverter extends AbstractEndpointConverter<SingleSignOnService> {
+    
+    /**
+     * Constructor.
+     */
+    public SingleSignOnServiceConverter() {
+        super((SAMLObjectBuilder<SingleSignOnService>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<SingleSignOnService>ensureBuilder(
+                        SingleSignOnService.DEFAULT_ELEMENT_NAME));
+    }
+    
+    /** {@inheritDoc} */
+    @Nonnull public SingleSignOnService apply(@Nullable final String value,
+            @Nullable @Live final List<String> protocols) {
+        return getProcessedEndpoint(protocols, value);
+    }
+
+}
\ No newline at end of file
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/TemplateMetadataGeneratorParameters.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/TemplateMetadataGeneratorParameters.java
new file mode 100644
index 000000000..636810e5c
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/TemplateMetadataGeneratorParameters.java
@@ -0,0 +1,40 @@
+/*
+ * 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 org.opensaml.saml.metadata.generator.impl;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Extension interface with additional parameters specific to template-based
+ * implementations of metadata generation.
+ * 
+ * <p>TODO: This will eventually migrate into the API.</p>
+ * 
+ * @since 5.0.0
+ */
+public interface TemplateMetadataGeneratorParameters extends MetadataGeneratorParameters {
+
+    /**
+     * Get path to templates.
+     * 
+     * @return template path
+     */
+    @Nonnull default String getTemplatePath() {
+        return "/templates/metadata";
+    }
+
+}
\ No newline at end of file
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/VelocityMetadataGenerator.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/VelocityMetadataGenerator.java
new file mode 100644
index 000000000..60ede274b
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/VelocityMetadataGenerator.java
@@ -0,0 +1,121 @@
+/*
+ * 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 org.opensaml.saml.metadata.generator.impl;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.opensaml.core.xml.Namespace;
+import org.opensaml.saml.common.xml.SAMLConstants;
+import org.opensaml.xmlsec.signature.support.SignatureConstants;
+
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.velocity.Template;
+
+/**
+ * Implementation of SAML metadata generation using Velocity.
+ * 
+ * @since 5.0.0
+ */
+public class VelocityMetadataGenerator extends AbstractIdentifiableInitializableComponent implements MetadataGenerator {
+
+    /** Velocity engine. */
+    @NonnullAfterInit private VelocityEngine velocityEngine;
+    
+    /**
+     * Set the Velocity engine to use.
+     * 
+     * @param engine velocity engine
+     */
+    public void setVelocityEngine(@Nonnull final VelocityEngine engine) {
+        velocityEngine = Constraint.isNotNull(engine, "VelocityEngine cannot be null");
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected void doInitialize() throws ComponentInitializationException {
+        super.doInitialize();
+        
+        if (velocityEngine == null) {
+            throw new ComponentInitializationException("VelocityEngine cannot be null");
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void generate(@Nonnull final MetadataGeneratorParameters params, @Nonnull final Writer sink)
+            throws IOException {
+        try {
+            if (params instanceof TemplateMetadataGeneratorParameters downcast) {
+                Template.fromTemplateName(velocityEngine,
+                        downcast.getTemplatePath() + "/EntityDescriptor.vm").merge(
+                                getVelocityContext(downcast), sink);
+            } else {
+                throw new IllegalArgumentException("Parameters were not of the expected type");
+            }
+        } catch (final Exception e) {
+            if (e instanceof IOException io) {
+                throw io;
+            }
+            throw new IOException(e);
+        }
+    }
+
+    /**
+     * Builds the Velocity template context.
+     * 
+     * @param params the input parameters
+     * 
+     * @return the populated context
+     */
+    @Nonnull protected VelocityContext getVelocityContext(@Nonnull final TemplateMetadataGeneratorParameters params) {
+        final VelocityContext context = new VelocityContext();
+        
+        context.put("params", params);
+        
+        // Namespace handling.
+        if (!params.isOmitNamespaceDeclarations()) {
+            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);
+            
+            final Set<Namespace> additionalNamespaces = params.getAdditionalNamespaces();
+            if (additionalNamespaces != null) {
+                for (final Namespace ns : additionalNamespaces) {
+                    prefixMap.put(ns.getNamespacePrefix(), ns.getNamespaceURI());
+                }
+            }
+            
+            context.put("namespaces", prefixMap);
+        }
+        
+        return context;
+    }
+
+}
\ No newline at end of file
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/package-info.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/package-info.java
new file mode 100644
index 000000000..b8224509e
--- /dev/null
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/metadata/generator/impl/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+/**
+ * Implementation of Velocity-based metadata generation.
+ */
+ at NonnullElements
+package org.opensaml.saml.metadata.generator.impl;
+
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
diff --git a/opensaml-saml-impl/src/main/resources/templates/metadata/AttributeAuthorityDescriptor.vm b/opensaml-saml-impl/src/main/resources/templates/metadata/AttributeAuthorityDescriptor.vm
new file mode 100644
index 000000000..d4d8b206e
--- /dev/null
+++ b/opensaml-saml-impl/src/main/resources/templates/metadata/AttributeAuthorityDescriptor.vm
@@ -0,0 +1,7 @@
+#set ($role = $params.AttributeAuthorityDescriptor)
+    <md:AttributeAuthorityDescriptor protocolSupportEnumeration="#foreach($p in $role.supportedProtocols)$p#if(!$foreach.last) #end#end">
+#parse("$params.templatePath/KeyDescriptors.vm")
+#foreach ($endpoint in $role.attributeServices)
+        <md:AttributeService Binding="$endpoint.binding" Location="$endpoint.location" />
+#end
+    </md:AttributeAuthorityDescriptor>
diff --git a/opensaml-saml-impl/src/main/resources/templates/metadata/EntityDescriptor.vm b/opensaml-saml-impl/src/main/resources/templates/metadata/EntityDescriptor.vm
new file mode 100644
index 000000000..0991c32ef
--- /dev/null
+++ b/opensaml-saml-impl/src/main/resources/templates/metadata/EntityDescriptor.vm
@@ -0,0 +1,13 @@
+<md:EntityDescriptor#if (!$params.omitNamespaceDeclarations)#foreach ($ns in $namespaces.entrySet()) xmlns:$ns.key="$ns.value"#end#end entityID="$params.entityID">
+
+#if ($params.IDPSSODescriptor)
+#parse("$params.templatePath/IDPSSODescriptor.vm")
+#end
+#if ($params.AttributeAuthorityDescriptor)
+#parse("$params.templatePath/AttributeAuthorityDescriptor.vm")
+#end
+#if ($params.SPSSODescriptor)
+#parse("$params.templatePath/SPSSODescriptor.vm")
+#end
+
+</md:EntityDescriptor>
diff --git a/opensaml-saml-impl/src/main/resources/templates/metadata/IDPSSODescriptor.vm b/opensaml-saml-impl/src/main/resources/templates/metadata/IDPSSODescriptor.vm
new file mode 100644
index 000000000..bb639ea4b
--- /dev/null
+++ b/opensaml-saml-impl/src/main/resources/templates/metadata/IDPSSODescriptor.vm
@@ -0,0 +1,13 @@
+#set ($role = $params.IDPSSODescriptor)
+    <md:IDPSSODescriptor protocolSupportEnumeration="#foreach($p in $role.supportedProtocols)$p#if(!$foreach.last) #end#end">
+#parse("$params.templatePath/KeyDescriptors.vm")
+#foreach ($endpoint in $role.artifactResolutionServices)
+        <md:ArtifactResolutionService index="$endpoint.index" Binding="$endpoint.binding" Location="$endpoint.location" />
+#end
+#foreach ($endpoint in $role.singleLogoutServices)
+        <md:SingleLogoutService Binding="$endpoint.binding" Location="$endpoint.location" />
+#end
+#foreach ($endpoint in $role.singleSignOnServices)
+        <md:SingleSignOnService Binding="$endpoint.binding" Location="$endpoint.location" />
+#end
+    </md:IDPSSODescriptor>
diff --git a/opensaml-saml-impl/src/main/resources/templates/metadata/KeyDescriptors.vm b/opensaml-saml-impl/src/main/resources/templates/metadata/KeyDescriptors.vm
new file mode 100644
index 000000000..38096a4cf
--- /dev/null
+++ b/opensaml-saml-impl/src/main/resources/templates/metadata/KeyDescriptors.vm
@@ -0,0 +1,30 @@
+#foreach ($cert in $params.certificates)        <md:KeyDescriptor>
+            <ds:KeyInfo>
+                <ds:X509Data>
+                    <ds:X509Certificate>
+$cert
+                    </ds:X509Certificate>
+                </ds:X509Data>
+            </ds:KeyInfo>
+        </md:KeyDescriptor>
+#end
+#foreach ($cert in $params.signingCertificates)        <md:KeyDescriptor use="signing">
+            <ds:KeyInfo>
+                <ds:X509Data>
+                    <ds:X509Certificate>
+$cert
+                    </ds:X509Certificate>
+                </ds:X509Data>
+            </ds:KeyInfo>
+        </md:KeyDescriptor>
+#end
+#foreach ($cert in $params.encryptionCertificates)        <md:KeyDescriptor use="encryption">
+            <ds:KeyInfo>
+                <ds:X509Data>
+                    <ds:X509Certificate>
+$cert
+                    </ds:X509Certificate>
+                </ds:X509Data>
+            </ds:KeyInfo>
+        </md:KeyDescriptor>
+#end
diff --git a/opensaml-saml-impl/src/main/resources/templates/metadata/SPSSODescriptor.vm b/opensaml-saml-impl/src/main/resources/templates/metadata/SPSSODescriptor.vm
new file mode 100644
index 000000000..510a09cbf
--- /dev/null
+++ b/opensaml-saml-impl/src/main/resources/templates/metadata/SPSSODescriptor.vm
@@ -0,0 +1,13 @@
+#set ($role = $params.SPSSODescriptor)
+    <md:SPSSODescriptor protocolSupportEnumeration="#foreach($p in $role.supportedProtocols)$p#if(!$foreach.last) #end#end">
+#parse("$params.templatePath/KeyDescriptors.vm")
+#foreach ($endpoint in $role.artifactResolutionServices)
+        <md:ArtifactResolutionService index="$endpoint.index" Binding="$endpoint.binding" Location="$endpoint.location" />
+#end
+#foreach ($endpoint in $role.singleLogoutServices)
+        <md:SingleLogoutService Binding="$endpoint.binding" Location="$endpoint.location" />
+#end
+#foreach ($endpoint in $role.assertionConsumerServices)
+        <md:AssertionConsumerService index="$endpoint.index" Binding="$endpoint.binding" Location="$endpoint.location" />
+#end
+    </md:SPSSODescriptor>

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


More information about the commits mailing list