[java-idp-plugin-metadatagen] branch dev/JMETAGEN-5 updated: JMETAGEN-5 - Metadata generation

Scott Cantor cantor.2 at osu.edu
Wed Jul 12 20:29:38 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=f726af43603d9f2d92ac79cd9e0da76fb56ecba3

The following commit(s) were added to refs/heads/dev/JMETAGEN-5 by this push:
     new f726af4  JMETAGEN-5 - Metadata generation
f726af4 is described below

commit f726af43603d9f2d92ac79cd9e0da76fb56ecba3
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Jul 12 16:29:35 2023 -0400

    JMETAGEN-5 - Metadata generation
    
    https://shibboleth.atlassian.net/browse/JMETAGEN-5
    
    Add contact and UIInfo support.
---
 .../impl/MetadataGenCommandLineArguments.java      | 234 ++++++++++++++++++---
 .../plugin/metadatagen/impl/MetadataGenTest.java   |   5 +
 2 files changed, 214 insertions(+), 25 deletions(-)

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 0e87942..9f5e150 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
@@ -22,6 +22,7 @@ import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.function.BiFunction;
@@ -34,16 +35,23 @@ import org.opensaml.core.xml.Namespace;
 import org.opensaml.core.xml.XMLObjectBuilderFactory;
 import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
 import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.common.xml.SAMLConstants;
+import org.opensaml.saml.ext.saml2mdui.Logo;
 import org.opensaml.saml.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.ContactPerson;
+import org.opensaml.saml.saml2.metadata.ContactPersonTypeEnumeration;
+import org.opensaml.saml.saml2.metadata.EmailAddress;
 import org.opensaml.saml.saml2.metadata.Endpoint;
+import org.opensaml.saml.saml2.metadata.GivenName;
 import org.opensaml.saml.saml2.metadata.IDPSSODescriptor;
 import org.opensaml.saml.saml2.metadata.IndexedEndpoint;
 import org.opensaml.saml.saml2.metadata.NameIDFormat;
 import org.opensaml.saml.saml2.metadata.SPSSODescriptor;
+import org.opensaml.saml.saml2.metadata.SurName;
 import org.opensaml.security.x509.X509Support;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -80,6 +88,9 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
 
     /** Cached AA role. */
     @Nullable private AttributeAuthorityDescriptor aaRole;
+    
+    /** Cached logo. */
+    @Nullable private Logo logoObject;
 
     /** The unique ID. */
     @Parameter(names = {"--entityID", "--client_id", "--id"}, required=true, description="Unique ID for entity")
@@ -101,21 +112,27 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
     @Parameter(names = {"--want-assertions-signed", "-w"}, description="Require signed assertions?")
     private boolean wantAssertionsSigned;
 
+    /** NameID format(s). */
     @Parameter(names = {"--nameid-format", "-f"}, description="SP NameID format(s)")
     @Nonnull private List<String> nameIDFormats = new ArrayList<>();
 
+    /** SSO endpoint(s). */
     @Parameter(names = {"--sso"}, description="SingleSignOnService endpoint(s)")
     @Nonnull private List<String> ssoServices = new ArrayList<>();
 
+    /** Query endpoint(s). */
     @Parameter(names = {"--attribute-query", "--query"}, description="AttributeService endpoint(s)")
     @Nonnull private List<String> attributeServices = new ArrayList<>();
 
+    /** Artifact resolution endpoint(s). */
     @Parameter(names = {"--artifact"}, description="ArtifactResolutionService endpoint(s)")
     @Nonnull private List<String> artifactServices = new ArrayList<>();
 
+    /** Logout endpoint(s). */
     @Parameter(names = {"--logout", "--slo"}, description="SingleLogoutService endpoint(s)")
     @Nonnull private List<String> logoutServices = new ArrayList<>();
 
+    /** ACS endpoint(s). */
     @Parameter(names = {"--acs"}, description="AssertionConsumerService endpoint(s)")
     @Nonnull private List<String> acServices = new ArrayList<>();
     
@@ -131,11 +148,45 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
     @Parameter(names = {"--encryption", "-e"}, description="Path(s) to certificate(s) for encryption only")
     @Nonnull private List<String> encryptionPaths = new ArrayList<>();
 
+    /** Display name. */
+    @Parameter(names= {"--display-name", "-y"}, description="Display name")
+    @Nullable private String displayName;
+
+    /** Description. */
+    @Parameter(names= {"--description", "-d"}, description="Description")
+    @Nullable private String description;
+
+    /** Logo URL. */
+    @Parameter(names= {"--logo-url", "--logo"}, description="Logo URL")
+    @Nullable private String logoURL;
+
+    /** Logo width. */
+    @Parameter(names= {"--logo-width", "--width"}, description="Logo width")
+    @Nullable private Integer logoWidth;
+
+    /** Logo height. */
+    @Parameter(names= {"--logo-height", "--height"}, description="Logo height")
+    @Nullable private Integer logoHeight;
+
+    /** Organization name. */
     @Parameter(names = {"--org-name", "-o"}, description="Organization name")
     @Nullable private String orgName;
 
+    /** Organization URL. */
     @Parameter(names = {"--org-url", "-u"}, description="Organization URL")
     @Nullable private String orgURL;
+    
+    /** Admin contact(s). */
+    @Parameter(names = {"--contact-admin", "-a"}, description="Administrative contact(s) (first/last/email)")
+    @Nonnull private List<String> adminContacts = new ArrayList<>();
+
+    /** Support contact(s). */
+    @Parameter(names = {"--contact-support", "-s"}, description="Support contact(s) (first/last/email)")
+    @Nonnull private List<String> supportContacts = new ArrayList<>();
+
+    /** Technical contact(s). */
+    @Parameter(names = {"--contact-tech", "-t"}, description="Technical contact(s) (first/last/email)")
+    @Nonnull private List<String> techContacts = new ArrayList<>();
 
     /** Omit namespaces from root element? */
     @Parameter(names = {"--omit-namespaces"}, description="Omit namespace declarations (assumes surrounding document context)")
@@ -157,6 +208,50 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
         }
         return getLanguageRanges().get(0).getRange();
     }
+
+   /** {@inheritDoc} */
+   @Nullable public String getDisplayName() {
+        return displayName;
+    }
+
+   /** {@inheritDoc} */
+    @Nullable public String getDescription() {
+        return description;
+    }
+
+    /** {@inheritDoc} */
+    @Nullable public Logo getLogo() {
+        if (logoURL == null) {
+            return null;
+        }
+        
+        synchronized(this) {
+            if (logoObject != null) {
+                return logoObject;
+            }
+        }
+        
+        final XMLObjectBuilderFactory bf = XMLObjectProviderRegistrySupport.getBuilderFactory();
+        
+        final SAMLObjectBuilder<Logo> builder =
+                (SAMLObjectBuilder<Logo>) bf.<Logo>ensureBuilder(Logo.DEFAULT_ELEMENT_NAME);
+        final Logo logo = builder.buildObject();
+        logo.setURI(logoURL);
+        
+        if (logoWidth != null) {
+            logo.setWidth(logoWidth);
+        }
+
+        if (logoHeight != null) {
+            logo.setHeight(logoHeight);
+        }
+
+        synchronized(this) {
+            logoObject = logo;
+        }
+        
+        return logo;
+    }
     
     /** {@inheritDoc} */
     @Nullable public String getOrganizationName() {
@@ -307,11 +402,28 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
         return role;
     }
     
-    /**
-     * Dual-use certificates.
-     * 
-     * @return base64-encoded certificates
-     */
+    /** {@inheritDoc} */
+    @Nonnull @Unmodifiable @NotLive public List<ContactPerson> getContactPersons() {
+        if (adminContacts.isEmpty() && supportContacts.isEmpty() && techContacts.isEmpty()) {
+            return CollectionSupport.emptyList();
+        }
+        
+        final List<ContactPerson> contacts = new ArrayList<>();
+        
+        for (final String s : adminContacts) {
+            contacts.add(this.convertContactPerson(s, ContactPersonTypeEnumeration.ADMINISTRATIVE));
+        }
+        for (final String s : supportContacts) {
+            contacts.add(this.convertContactPerson(s, ContactPersonTypeEnumeration.SUPPORT));
+        }
+        for (final String s : techContacts) {
+            contacts.add(this.convertContactPerson(s, ContactPersonTypeEnumeration.TECHNICAL));
+        }
+        
+        return CollectionSupport.copyToList(contacts);
+    }
+    
+    /** {@inheritDoc} */
     @Nonnull @Unmodifiable @NotLive public List<String> getCertificates() {
         return certificatePaths.stream()
                 .map(this::getEncodedCertificate)
@@ -319,11 +431,7 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
                 .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
     }
 
-    /**
-     * Signing-only certificate path(s).
-     * 
-     * @return base64-encoded certificates
-     */
+    /** {@inheritDoc} */
     @Nonnull @Unmodifiable @NotLive public List<String> getSigningCertificates() {
         return signingPaths.stream()
                 .map(this::getEncodedCertificate)
@@ -331,11 +439,7 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
                 .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
     }
 
-    /**
-     * Encryption-only certificate path(s).
-     * 
-     * @return base64-encoded certificates
-     */
+    /** {@inheritDoc} */
     @Nonnull @Unmodifiable @NotLive public List<String> getEncryptionCertificates() {
         return encryptionPaths.stream()
                 .map(this::getEncodedCertificate)
@@ -343,24 +447,26 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
                 .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
     }
 
-    /**
-     * Gets whether to omit namespaces on root element.
-     * 
-     * <p>Used to optimize namespace declarations when generating batches.</p>
-     * 
-     * @return argument value
-     */
+    /** {@inheritDoc} */
     public boolean isOmitNamespaceDeclarations() {
         return omitNamespaces;
     }
     
     /** {@inheritDoc} */
     @Nullable public Set<Namespace> getAdditionalNamespaces() {
+        
+        final Set<Namespace> namespaces = new HashSet<>();
+        
+        if (displayName != null || description != null || logoURL != null) {
+            namespaces.add(new Namespace(SAMLConstants.SAML20MDUI_NS, SAMLConstants.SAML20MDUI_PREFIX));
+        }
+
         final SPSSODescriptor role = getSPSSODescriptor();
         if (role != null && role.isSupportedProtocol(AssertionConsumerServiceConverter.OPENID_PROTOCOL)) {
-            return CollectionSupport.singleton(new Namespace("urn:mace:shibboleth:metadata:oidc:1.0", "oidcmd"));
+            namespaces.add(new Namespace("urn:mace:shibboleth:metadata:oidc:1.0", "oidcmd"));
         }
-        return null;
+        
+        return namespaces;
     }
     
     /**
@@ -412,10 +518,20 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
         
         out.println(String.format("  %-20s %s", "--want-assertions-signed, -w", "SP requires signed assertions?"));
         out.println(String.format("  %-20s %s", "--nameid-format, -f", "SP NameID format(s)"));
-        
+
+        out.println(String.format("  %-20s %s", "--display-name, -y", "Display name"));
+        out.println(String.format("  %-20s %s", "--description, -d", "Description"));
+        out.println(String.format("  %-20s %s", "--logo-url, --logo", "Logo URL"));
+        out.println(String.format("  %-20s %s", "--logo-width, --width", "Logo width"));
+        out.println(String.format("  %-20s %s", "--logo-height, --height", "Logo height"));
+
         out.println(String.format("  %-20s %s", "--org-name, -o", "Organization name"));
         out.println(String.format("  %-20s %s", "--org-url, -u", "Organization URL"));
         
+        out.println(String.format("  %-20s %s", "--contact-admin, -a", "Administrative contact(s) (first/last/email)"));
+        out.println(String.format("  %-20s %s", "--contact-support, -s", "Support contact(s) (first/last/email)"));
+        out.println(String.format("  %-20s %s", "--contact-tech, -t", "Technical contact(s) (first/last/email)"));
+        
         out.println(String.format("  %-20s %s", "--omit-namespaces", "Omit namespaces on root element."));
         out.println(String.format("  %-20s %s", "--output, --out", "Output file path."));
         out.println();
@@ -456,4 +572,72 @@ public class MetadataGenCommandLineArguments extends AbstractIdPHomeAwareCommand
                 .collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableList())).get();
     }
     
+    /**
+     * Parse string into a {@link ContactPerson} object.
+     * 
+     * @param input input string
+     * @param type contact type
+     * 
+     * @return the built object
+     * 
+     * @throws IllegalArgumentException if the string is invalid
+     */
+    @Nonnull private ContactPerson convertContactPerson(@Nonnull final String input,
+            @Nonnull final ContactPersonTypeEnumeration type) {
+        
+        final String[] split = input.split("/", 3);
+        if (split == null || split.length != 3) {
+            throw new IllegalArgumentException("Contact string was invalid.");
+        }
+        
+        final String first = StringSupport.trimOrNull(split[0]);
+        final String second = StringSupport.trimOrNull(split[1]);
+        final String third = StringSupport.trimOrNull(split[2]);
+        
+        if (first == null && second == null && third == null) {
+            throw new IllegalArgumentException("Contact string contained no data.");
+        }
+
+        final XMLObjectBuilderFactory bf = XMLObjectProviderRegistrySupport.getBuilderFactory();
+        
+        final SAMLObjectBuilder<ContactPerson> contactBuilder =
+                (SAMLObjectBuilder<ContactPerson>) bf.<ContactPerson>ensureBuilder(
+                        ContactPerson.DEFAULT_ELEMENT_NAME);
+        final ContactPerson contact = contactBuilder.buildObject();
+        contact.setType(type);
+        
+        if (first != null) {
+            final SAMLObjectBuilder<GivenName> builder =
+                    (SAMLObjectBuilder<GivenName>) bf.<GivenName>ensureBuilder(
+                            GivenName.DEFAULT_ELEMENT_NAME);
+            final GivenName givenName = builder.buildObject();
+            givenName.setValue(first);
+            contact.setGivenName(givenName);
+        }
+ 
+        if (second != null) {
+            final SAMLObjectBuilder<SurName> builder =
+                    (SAMLObjectBuilder<SurName>) bf.<SurName>ensureBuilder(
+                            SurName.DEFAULT_ELEMENT_NAME);
+            final SurName surName = builder.buildObject();
+            surName.setValue(second);
+            contact.setSurName(surName);
+        }
+        
+        if (third != null) {
+            final SAMLObjectBuilder<EmailAddress> builder =
+                    (SAMLObjectBuilder<EmailAddress>) bf.<EmailAddress>ensureBuilder(
+                            EmailAddress.DEFAULT_ELEMENT_NAME);
+            final EmailAddress email = builder.buildObject();
+            if (third.startsWith("mailto:")) {
+                email.setURI(third);
+            } else {
+                email.setURI("mailto:" + third);
+            }
+            contact.getEmailAddresses().add(email);
+        }
+        
+        return contact;
+    }
+    
 }
\ No newline at end of file
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 621fc72..6d0b53b 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
@@ -39,8 +39,13 @@ public class MetadataGenTest {
                 new String[] {
                         "--home", IDP_HOME,
                         "--lang", "US-en",
+                        "--logo", "https://idp.example.org/logo.png",
+                        "--logo-width", "128",
                         "-o", "Example Org",
                         "-u", "https://example.org",
+                        "-a", "/Bono/",
+                        "-t", "John/Doe/mailto:doe at example.org",
+                        "--contact-support", "Help Desk//support at example.org",
                         "--sp", "-w",
                         "--nameid-format", NameIDType.EMAIL,
                         "--idp",

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


More information about the commits mailing list