Fix usage of java.text.MessageFormat.
[java-idp.git] / src / main / java / edu / internet2 / middleware / shibboleth / idp / profile / AbstractSAMLProfileHandler.java
index 06b263d..f0783fb 100644 (file)
@@ -36,15 +36,17 @@ import org.opensaml.saml2.metadata.RoleDescriptor;
 import org.opensaml.saml2.metadata.SSODescriptor;
 import org.opensaml.saml2.metadata.provider.MetadataProvider;
 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
+import org.opensaml.security.MetadataCredentialResolver;
+import org.opensaml.security.MetadataCredentialResolverFactory;
 import org.opensaml.ws.message.encoder.MessageEncodingException;
 import org.opensaml.ws.security.SecurityPolicyResolver;
 import org.opensaml.ws.transport.InTransport;
 import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
 import org.opensaml.xml.security.credential.Credential;
+import org.opensaml.xml.util.DatatypeHelper;
 import org.opensaml.xml.util.Pair;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.slf4j.helpers.MessageFormatter;
 
 import edu.internet2.middleware.shibboleth.common.attribute.BaseAttribute;
 import edu.internet2.middleware.shibboleth.common.attribute.encoding.AttributeEncoder;
@@ -177,6 +179,17 @@ public abstract class AbstractSAMLProfileHandler extends
     }
 
     /**
+     * A convenience method for obtaining a metadata credential resolver for the current metadata provider.
+     *
+     * @return the metadata credential resolver or null
+     */
+    public MetadataCredentialResolver getMetadataCredentialResolver() {
+        MetadataCredentialResolverFactory mcrFactory = MetadataCredentialResolverFactory.getFactory();
+        MetadataProvider metadataProvider = getMetadataProvider();
+        return mcrFactory.getInstance(metadataProvider);
+    }
+
+    /**
      * Gets the SAML message bindings that may be used by outbound messages.
      * 
      * @return SAML message bindings that may be used by outbound messages
@@ -422,53 +435,25 @@ public abstract class AbstractSAMLProfileHandler extends
      */
     protected <T extends SAMLNameIdentifierEncoder> Pair<BaseAttribute, T> selectNameIDAttributeAndEncoder(
             Class<T> nameIdEncoderType, BaseSAMLProfileRequestContext requestContext) throws ProfileException {
-        String requiredNameFormat = getRequiredNameIDFormat(requestContext);
-        String requiredNameFormatErr = MessageFormatter.format(
-                "No attribute of principal '{}' can be encoded in to a NameIdentifier of "
-                        + "required format '{}' for relying party '{}'", new Object[] {
-                        requestContext.getPrincipalName(), requiredNameFormat,
-                        requestContext.getInboundMessageIssuer(), });
 
-        Map<String, BaseAttribute> principalAttributes = requestContext.getAttributes();
-        if (principalAttributes == null || principalAttributes.isEmpty()) {
-            log.debug("No attributes for principal '{}', no name identifier will be created for relying party '{}'",
-                    requestContext.getPrincipalName(), requestContext.getInboundMessageIssuer());
-            if (requiredNameFormat == null) {
-                return null;
-            } else {
-                log.warn(requiredNameFormatErr);
-                throw new ProfileException(requiredNameFormatErr);
-            }
+        String requiredNameFormat = DatatypeHelper.safeTrimOrNullString(getRequiredNameIDFormat(requestContext));
+        if (requiredNameFormat != null) {
+            log.debug("Attempting to build name identifier for relying party'{}' that requires format '{}'",
+                    requestContext.getInboundMessageIssuer(), requiredNameFormat);
+            return selectNameIDAttributeAndEncoderByRequiredFormat(requiredNameFormat, nameIdEncoderType,
+                    requestContext);
         }
 
         List<String> supportedNameFormats = getNameFormats(requestContext);
-        if (!supportedNameFormats.isEmpty()) {
-            log.debug("Relying party '{}' supports the name formats: {}", requestContext.getInboundMessageIssuer(),
-                    supportedNameFormats);
-        } else {
-            log.debug("Relying party '{}' indicated no preferred name formats", requestContext
-                    .getInboundMessageIssuer());
-        }
-
-        Pair<BaseAttribute, T> nameIdAttributeAndEncoder = null;
-        if (requiredNameFormat == null) {
-            nameIdAttributeAndEncoder = selectNameIDAttributeAndEncoder(nameIdEncoderType, principalAttributes,
-                    supportedNameFormats);
-            if (nameIdAttributeAndEncoder == null) {
-                log.debug("No attributes for principal '{}' supports encoding into a supported NameIdentifier format for relying party '{}'",
-                                requestContext.getPrincipalName(), requestContext.getInboundMessageIssuer());
-                return null;
-            }
+        if (supportedNameFormats.isEmpty()) {
+            log.debug("Attempting to build name identifier for relying party '{}' that supports any format",
+                    requestContext.getInboundMessageIssuer());
         } else {
-            nameIdAttributeAndEncoder = selectNameIDAttributeAndEncoder(nameIdEncoderType, principalAttributes,
-                    requiredNameFormat);
-            if (nameIdAttributeAndEncoder == null) {
-                log.warn(requiredNameFormatErr);
-                throw new ProfileException(requiredNameFormatErr);
-            }
+            log.debug("Attempting to build name identifier for relying party '{}' that supports the formats: {}",
+                    requestContext.getInboundMessageIssuer(), supportedNameFormats);
         }
-
-        return nameIdAttributeAndEncoder;
+        return selectNameIDAttributeAndEncoderBySupportedFormats(supportedNameFormats, nameIdEncoderType,
+                requestContext);
     }
 
     /**
@@ -486,6 +471,43 @@ public abstract class AbstractSAMLProfileHandler extends
     }
 
     /**
+     * Selects the principal attribute that can be encoded in to the required name identifier format.
+     * 
+     * @param <T> type of name identifier encoder the attribute must support
+     * @param requiredNameFormat required name identifier format type
+     * @param nameIdEncoderType type of name identifier encoder the attribute must support
+     * @param requestContext the current request context
+     * 
+     * @return the select attribute, and its encoder, to be used to build the name identifier
+     * 
+     * @throws ProfileException thrown if a specific name identifier format was required but not supported
+     */
+    protected <T extends SAMLNameIdentifierEncoder> Pair<BaseAttribute, T> selectNameIDAttributeAndEncoderByRequiredFormat(
+            String requiredNameFormat, Class<T> nameIdEncoderType, BaseSAMLProfileRequestContext requestContext)
+            throws ProfileException {
+        String requiredNameFormatErr = "No attribute of principal '" + requestContext.getPrincipalName()
+                + "' can be encoded in to a NameIdentifier of " + "required format '" + requiredNameFormat
+                + "' for relying party '" + requestContext.getInboundMessageIssuer() + "'";
+
+        Map<String, BaseAttribute> principalAttributes = requestContext.getAttributes();
+        if (principalAttributes == null || principalAttributes.isEmpty()) {
+            log.debug("No attributes for principal '{}', no name identifier will be created for relying party '{}'",
+                    requestContext.getPrincipalName(), requestContext.getInboundMessageIssuer());
+            log.warn(requiredNameFormatErr);
+            throw new ProfileException(requiredNameFormatErr);
+        }
+
+        Pair<BaseAttribute, T> nameIdAttributeAndEncoder = selectNameIDAttributeAndEncoder(nameIdEncoderType,
+                principalAttributes, java.util.Collections.singletonList(requiredNameFormat));
+        if (nameIdAttributeAndEncoder == null) {
+            log.warn(requiredNameFormatErr);
+            throw new ProfileException(requiredNameFormatErr);
+        }
+
+        return nameIdAttributeAndEncoder;
+    }
+
+    /**
      * Gets the name identifier formats to use when creating identifiers for the relying party.
      * 
      * @param requestContext current request context
@@ -510,14 +532,6 @@ public abstract class AbstractSAMLProfileHandler extends
             nameFormats.clear();
         }
 
-        if (!nameFormats.isEmpty()) {
-            log.debug("Relying party '{}' supports the name formats: {}", requestContext.getInboundMessageIssuer(),
-                    nameFormats);
-        } else {
-            log.debug("Relying party '{}' indicated no preferred name formats", requestContext
-                    .getInboundMessageIssuer());
-        }
-
         return nameFormats;
     }
 
@@ -552,45 +566,39 @@ public abstract class AbstractSAMLProfileHandler extends
     }
 
     /**
-     * Selects an attribute, resolved previously and of the required format, to encode as a NameID.
+     * Selects the principal attribute that can be encoded in to one of the supported name identifier formats.
      * 
      * @param <T> type of name identifier encoder the attribute must support
+     * @param supportedNameFormats name identifier formats supported by the relaying part, or an empty list if all
+     *            formats are supported
      * @param nameIdEncoderType type of name identifier encoder the attribute must support
-     * @param principalAttributes resolved attributes
-     * @param requiredNameFormat required NameID format
+     * @param requestContext the current request context
      * 
-     * @return the attribute and its associated NameID encoder
+     * @return the select attribute, and its encoder, to be used to build the name identifier
      * 
-     * @throws ProfileException thrown if no attribute can be encoded in to a NameID of the required type
+     * @throws ProfileException thrown if there is a problem selecting the attribute
      */
-    protected <T extends SAMLNameIdentifierEncoder> Pair<BaseAttribute, T> selectNameIDAttributeAndEncoder(
-            Class<T> nameIdEncoderType, Map<String, BaseAttribute> principalAttributes, String requiredNameFormat)
+    protected <T extends SAMLNameIdentifierEncoder> Pair<BaseAttribute, T> selectNameIDAttributeAndEncoderBySupportedFormats(
+            List<String> supportedNameFormats, Class<T> nameIdEncoderType, BaseSAMLProfileRequestContext requestContext)
             throws ProfileException {
+        Map<String, BaseAttribute> principalAttributes = requestContext.getAttributes();
+        if (principalAttributes == null || principalAttributes.isEmpty()) {
+            log.debug("No attributes for principal '{}', no name identifier will be created for relying party '{}'",
+                    requestContext.getPrincipalName(), requestContext.getInboundMessageIssuer());
+            return null;
+        }
 
-        T nameIdEncoder = null;
-
-        if (principalAttributes != null) {
-            for (BaseAttribute<?> attribute : principalAttributes.values()) {
-                if (attribute == null) {
-                    continue;
-                }
-
-                for (AttributeEncoder encoder : attribute.getEncoders()) {
-                    if (encoder == null) {
-                        continue;
-                    }
-
-                    if (nameIdEncoderType.isInstance(encoder)) {
-                        nameIdEncoder = nameIdEncoderType.cast(encoder);
-                        if (nameIdEncoder.getNameFormat().equals(requiredNameFormat)) {
-                            return new Pair<BaseAttribute, T>(attribute, nameIdEncoder);
-                        }
-                    }
-                }
-            }
+        Pair<BaseAttribute, T> nameIdAttributeAndEncoder = null;
+        nameIdAttributeAndEncoder = selectNameIDAttributeAndEncoder(nameIdEncoderType, principalAttributes,
+                supportedNameFormats);
+        if (nameIdAttributeAndEncoder == null) {
+            log
+                    .debug(
+                            "No attributes for principal '{}' support encoding into a supported name identifier format for relying party '{}'",
+                            requestContext.getPrincipalName(), requestContext.getInboundMessageIssuer());
         }
 
-        return null;
+        return nameIdAttributeAndEncoder;
     }
 
     /**