[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