Allow for a NameIDPolicy without a specified format
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / profile / saml2 / AbstractSAML2ProfileHandler.java
index 95533e2..338b392 100644 (file)
@@ -30,8 +30,9 @@ import org.opensaml.common.SAMLObjectBuilder;
 import org.opensaml.common.SAMLVersion;
 import org.opensaml.common.impl.SAMLObjectContentReference;
 import org.opensaml.log.Level;
-import org.opensaml.saml2.core.Advice;
 import org.opensaml.saml2.core.Assertion;
+import org.opensaml.saml2.core.AttributeQuery;
+import org.opensaml.saml2.core.AttributeStatement;
 import org.opensaml.saml2.core.Audience;
 import org.opensaml.saml2.core.AudienceRestriction;
 import org.opensaml.saml2.core.AuthnRequest;
@@ -55,7 +56,6 @@ import org.opensaml.saml2.metadata.PDPDescriptor;
 import org.opensaml.saml2.metadata.RoleDescriptor;
 import org.opensaml.saml2.metadata.SPSSODescriptor;
 import org.opensaml.saml2.metadata.SSODescriptor;
-import org.opensaml.saml2.metadata.provider.MetadataProviderException;
 import org.opensaml.xml.XMLObjectBuilder;
 import org.opensaml.xml.security.credential.Credential;
 import org.opensaml.xml.signature.Signature;
@@ -66,24 +66,24 @@ import edu.internet2.middleware.shibboleth.common.attribute.AttributeRequestExce
 import edu.internet2.middleware.shibboleth.common.attribute.BaseAttribute;
 import edu.internet2.middleware.shibboleth.common.attribute.encoding.AttributeEncoder;
 import edu.internet2.middleware.shibboleth.common.attribute.encoding.AttributeEncodingException;
+import edu.internet2.middleware.shibboleth.common.attribute.encoding.SAML2NameIDAttributeEncoder;
+import edu.internet2.middleware.shibboleth.common.attribute.provider.SAML2AttributeAuthority;
+import edu.internet2.middleware.shibboleth.common.attribute.provider.ShibbolethSAMLAttributeRequestContext;
 import edu.internet2.middleware.shibboleth.common.log.AuditLogEntry;
 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
 import edu.internet2.middleware.shibboleth.common.profile.ProfileRequest;
 import edu.internet2.middleware.shibboleth.common.profile.ProfileResponse;
 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml2.AbstractSAML2ProfileConfiguration;
 import edu.internet2.middleware.shibboleth.idp.profile.AbstractSAMLProfileHandler;
+import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
+import edu.internet2.middleware.shibboleth.idp.session.Session;
 
-/**
- * Common implementation details for profile handlers.
- */
+/** Common implementation details for profile handlers. */
 public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHandler {
 
     /** SAML Version for this profile handler. */
     public static final SAMLVersion SAML_VERSION = SAMLVersion.VERSION_20;
 
-    /** URI for the SAML 2 protocol. */
-    public static final String SAML20_PROTOCOL_URI = "urn:oasis:names:tc:SAML:2.0:protocol";
-
     /** Class logger. */
     private Logger log = Logger.getLogger(AbstractSAML2ProfileHandler.class);
 
@@ -123,9 +123,6 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
     /** For building audience. */
     private SAMLObjectBuilder<Audience> audienceBuilder;
 
-    /** For building advice. */
-    private SAMLObjectBuilder<Advice> adviceBuilder;
-
     /** For building signature. */
     private XMLObjectBuilder<Signature> signatureBuilder;
 
@@ -153,134 +150,27 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
         proxyRestrictionBuilder = (SAMLObjectBuilder<ProxyRestriction>) getBuilderFactory().getBuilder(
                 ProxyRestriction.DEFAULT_ELEMENT_NAME);
         audienceBuilder = (SAMLObjectBuilder<Audience>) getBuilderFactory().getBuilder(Audience.DEFAULT_ELEMENT_NAME);
-        adviceBuilder = (SAMLObjectBuilder<Advice>) getBuilderFactory().getBuilder(Advice.DEFAULT_ELEMENT_NAME);
         signatureBuilder = (XMLObjectBuilder<Signature>) getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME);
     }
 
     /**
-     * Convenience method for getting the SAML 2 advice builder.
-     * 
-     * @return SAML 2 advice builder
-     */
-    public SAMLObjectBuilder<Advice> getAdviceBuilder() {
-        return adviceBuilder;
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 assertion builder.
-     * 
-     * @return SAML 2 assertion builder
-     */
-    public SAMLObjectBuilder<Assertion> getAssertionBuilder() {
-        return assertionBuilder;
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 audience builder.
-     * 
-     * @return SAML 2 audience builder
-     */
-    public SAMLObjectBuilder<Audience> getAudienceBuilder() {
-        return audienceBuilder;
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 audience restriction builder.
-     * 
-     * @return SAML 2 audience restriction builder
-     */
-    public SAMLObjectBuilder<AudienceRestriction> getAudienceRestrictionBuilder() {
-        return audienceRestrictionBuilder;
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 conditions builder.
-     * 
-     * @return SAML 2 conditions builder
-     */
-    public SAMLObjectBuilder<Conditions> getConditionsBuilder() {
-        return conditionsBuilder;
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 Issuer builder.
-     * 
-     * @return SAML 2 Issuer builder
-     */
-    public SAMLObjectBuilder<Issuer> getIssuerBuilder() {
-        return issuerBuilder;
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 proxy restriction builder.
-     * 
-     * @return SAML 2 proxy restriction builder
-     */
-    public SAMLObjectBuilder<ProxyRestriction> getProxyRestrictionBuilder() {
-        return proxyRestrictionBuilder;
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 response builder.
-     * 
-     * @return SAML 2 response builder
-     */
-    public SAMLObjectBuilder<Response> getResponseBuilder() {
-        return responseBuilder;
-    }
-
-    /**
-     * Convenience method for getting the Signature builder.
+     * Checks that the SAML major version for a request is 2.
      * 
-     * @return signature builder
-     */
-    public XMLObjectBuilder<Signature> getSignatureBuilder() {
-        return signatureBuilder;
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 status builder.
+     * @param requestContext current request context containing the SAML message
      * 
-     * @return SAML 2 status builder
+     * @throws ProfileException thrown if the major version of the SAML request is not 2
      */
-    public SAMLObjectBuilder<Status> getStatusBuilder() {
-        return statusBuilder;
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 status code builder.
-     * 
-     * @return SAML 2 status code builder
-     */
-    public SAMLObjectBuilder<StatusCode> getStatusCodeBuilder() {
-        return statusCodeBuilder;
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 status message builder.
-     * 
-     * @return SAML 2 status message builder
-     */
-    public SAMLObjectBuilder<StatusMessage> getStatusMessageBuilder() {
-        return statusMessageBuilder;
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 subject builder.
-     * 
-     * @return SAML 2 subject builder
-     */
-    public SAMLObjectBuilder<Subject> getSubjectBuilder() {
-        return subjectBuilder;
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 subject confirmation builder.
-     * 
-     * @return SAML 2 subject confirmation builder
-     */
-    public SAMLObjectBuilder<SubjectConfirmation> getSubjectConfirmationBuilder() {
-        return subjectConfirmationBuilder;
+    protected void checkSamlVersion(SAML2ProfileRequestContext requestContext) throws ProfileException {
+        SAMLVersion version = requestContext.getSamlRequest().getVersion();
+        if (version.getMajorVersion() < 2) {
+            requestContext.setFailureStatus(buildStatus(StatusCode.VERSION_MISMATCH_URI,
+                    StatusCode.REQUEST_VERSION_TOO_LOW_URI, null));
+            throw new ProfileException("SAML request version too low");
+        } else if (version.getMajorVersion() > 2) {
+            requestContext.setFailureStatus(buildStatus(StatusCode.VERSION_MISMATCH_URI,
+                    StatusCode.REQUEST_VERSION_TOO_HIGH_URI, null));
+            throw new ProfileException("SAML request version too high");
+        }
     }
 
     /**
@@ -293,10 +183,9 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      * @return the built response
      * 
      * @throws ProfileException thrown if there is a problem creating the SAML response
-     * @throws AttributeRequestException thrown if there is a problem resolving attributes
      */
     protected Response buildResponse(SAML2ProfileRequestContext requestContext, Subject assertionSubject,
-            List<Statement> statements) throws ProfileException, AttributeRequestException {
+            List<Statement> statements) throws ProfileException {
 
         DateTime issueInstant = new DateTime();
 
@@ -308,7 +197,7 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
         }
 
         // create the SAML response and add the assertion
-        Response samlResponse = getResponseBuilder().buildObject();
+        Response samlResponse = responseBuilder.buildObject();
         samlResponse.setIssueInstant(issueInstant);
         populateStatusResponse(requestContext, samlResponse);
 
@@ -332,7 +221,7 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      * @return the built assertion
      */
     protected Assertion buildAssertion(SAML2ProfileRequestContext requestContext, DateTime issueInstant) {
-        Assertion assertion = getAssertionBuilder().buildObject();
+        Assertion assertion = assertionBuilder.buildObject();
         assertion.setID(getIdGenerator().generateIdentifier());
         assertion.setIssueInstant(issueInstant);
         assertion.setVersion(SAMLVersion.VERSION_20);
@@ -352,9 +241,9 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      * @return the built issuer
      */
     protected Issuer buildEntityIssuer(SAML2ProfileRequestContext requestContext) {
-        Issuer issuer = getIssuerBuilder().buildObject();
+        Issuer issuer = issuerBuilder.buildObject();
         issuer.setFormat(Issuer.ENTITY);
-        issuer.setValue(requestContext.getRelyingPartyId());
+        issuer.setValue(requestContext.getAssertingPartyId());
 
         return issuer;
     }
@@ -371,7 +260,7 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
     protected Conditions buildConditions(SAML2ProfileRequestContext requestContext, DateTime issueInstant) {
         AbstractSAML2ProfileConfiguration profileConfig = requestContext.getProfileConfiguration();
 
-        Conditions conditions = getConditionsBuilder().buildObject();
+        Conditions conditions = conditionsBuilder.buildObject();
         conditions.setNotBefore(issueInstant);
         conditions.setNotOnOrAfter(issueInstant.plus(profileConfig.getAssertionLifetime()));
 
@@ -380,9 +269,9 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
         // add audience restrictions
         audiences = profileConfig.getAssertionAudiences();
         if (audiences != null && audiences.size() > 0) {
-            AudienceRestriction audienceRestriction = getAudienceRestrictionBuilder().buildObject();
+            AudienceRestriction audienceRestriction = audienceRestrictionBuilder.buildObject();
             for (String audienceUri : audiences) {
-                Audience audience = getAudienceBuilder().buildObject();
+                Audience audience = audienceBuilder.buildObject();
                 audience.setAudienceURI(audienceUri);
                 audienceRestriction.getAudiences().add(audience);
             }
@@ -392,10 +281,10 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
         // add proxy restrictions
         audiences = profileConfig.getProxyAudiences();
         if (audiences != null && audiences.size() > 0) {
-            ProxyRestriction proxyRestriction = getProxyRestrictionBuilder().buildObject();
+            ProxyRestriction proxyRestriction = proxyRestrictionBuilder.buildObject();
             Audience audience;
             for (String audienceUri : audiences) {
-                audience = getAudienceBuilder().buildObject();
+                audience = audienceBuilder.buildObject();
                 audience.setAudienceURI(audienceUri);
                 proxyRestriction.getAudiences().add(audience);
             }
@@ -423,6 +312,133 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
     }
 
     /**
+     * Executes a query for attributes and builds a SAML attribute statement from the results.
+     * 
+     * @param requestContext current request context
+     * 
+     * @return attribute statement resulting from the query
+     * 
+     * @throws ProfileException thrown if there is a problem making the query
+     */
+    protected AttributeStatement buildAttributeStatement(SAML2ProfileRequestContext requestContext)
+            throws ProfileException {
+
+        if (log.isDebugEnabled()) {
+            log.debug("Creating attribute statement in response to SAML request "
+                    + requestContext.getSamlRequest().getID() + " from relying party "
+                    + requestContext.getRelyingPartyId());
+        }
+
+        AbstractSAML2ProfileConfiguration profileConfiguration = requestContext.getProfileConfiguration();
+        SAML2AttributeAuthority attributeAuthority = profileConfiguration.getAttributeAuthority();
+
+        try {
+            if (log.isDebugEnabled()) {
+                log.debug("Resolving attributes for principal " + requestContext.getPrincipalName()
+                        + " of SAML request " + requestContext.getSamlRequest().getID() + " from relying party "
+                        + requestContext.getRelyingPartyId());
+            }
+            Map<String, BaseAttribute> principalAttributes = attributeAuthority
+                    .getAttributes(buildAttributeRequestContext(requestContext));
+
+            requestContext.setPrincipalAttributes(principalAttributes);
+
+            if (requestContext.getSamlRequest() instanceof AttributeQuery) {
+                return attributeAuthority.buildAttributeStatement((AttributeQuery) requestContext.getSamlRequest(),
+                        principalAttributes.values());
+            } else {
+                return attributeAuthority.buildAttributeStatement(null, principalAttributes.values());
+            }
+        } catch (AttributeRequestException e) {
+            log.error("Error resolving attributes for SAML request " + requestContext.getSamlRequest().getID()
+                    + " from relying party " + requestContext.getRelyingPartyId(), e);
+            requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null, "Error resolving attributes"));
+            throw new ProfileException("Error resolving attributes for SAML request "
+                    + requestContext.getSamlRequest().getID() + " from relying party "
+                    + requestContext.getRelyingPartyId(), e);
+        }
+    }
+
+    /**
+     * Resolves the principal name of the subject of the request.
+     * 
+     * @param requestContext current request context
+     * 
+     * @throws ProfileException thrown if the principal name can not be resolved
+     */
+    protected void resolvePrincipal(SAML2ProfileRequestContext requestContext) throws ProfileException {
+        AbstractSAML2ProfileConfiguration profileConfiguration = requestContext.getProfileConfiguration();
+        if (profileConfiguration == null) {
+            log.error("Unable to resolve principal, no SAML 2 profile configuration for relying party "
+                    + requestContext.getRelyingPartyId());
+            requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.REQUEST_DENIED_URI,
+                    "Error resolving principal"));
+            throw new ProfileException(
+                    "Unable to resolve principal, no SAML 2 profile configuration for relying party "
+                            + requestContext.getRelyingPartyId());
+        }
+        SAML2AttributeAuthority attributeAuthority = profileConfiguration.getAttributeAuthority();
+
+        if (log.isDebugEnabled()) {
+            log.debug("Resolving principal name for subject of SAML request " + requestContext.getSamlRequest().getID()
+                    + " from relying party " + requestContext.getRelyingPartyId());
+        }
+
+        try {
+            String principal = attributeAuthority.getPrincipal(buildAttributeRequestContext(requestContext));
+            requestContext.setPrincipalName(principal);
+        } catch (AttributeRequestException e) {
+            log.error("Error resolving attributes for SAML request " + requestContext.getSamlRequest().getID()
+                    + " from relying party " + requestContext.getRelyingPartyId(), e);
+            requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.UNKNOWN_PRINCIPAL_URI,
+                    "Error resolving principal"));
+            throw new ProfileException("Error resolving attributes for SAML request "
+                    + requestContext.getSamlRequest().getID() + " from relying party "
+                    + requestContext.getRelyingPartyId(), e);
+        }
+    }
+
+    /**
+     * Creates an attribute query context from the current profile request context.
+     * 
+     * @param requestContext current profile request
+     * 
+     * @return created query context
+     */
+    protected ShibbolethSAMLAttributeRequestContext<NameID, AttributeQuery> buildAttributeRequestContext(
+            SAML2ProfileRequestContext requestContext) {
+
+        ShibbolethSAMLAttributeRequestContext<NameID, AttributeQuery> queryContext;
+
+        if(requestContext.getSamlRequest() instanceof AttributeQuery){
+        queryContext = new ShibbolethSAMLAttributeRequestContext<NameID, AttributeQuery>(getMetadataProvider(),
+                requestContext.getRelyingPartyConfiguration(), (AttributeQuery) requestContext.getSamlRequest());
+        }else{
+            queryContext = new ShibbolethSAMLAttributeRequestContext<NameID, AttributeQuery>(getMetadataProvider(),
+                    requestContext.getRelyingPartyConfiguration(), null);
+        }
+        queryContext.setAttributeRequester(requestContext.getAssertingPartyId());
+        queryContext.setPrincipalName(requestContext.getPrincipalName());
+        queryContext.setProfileConfiguration(requestContext.getProfileConfiguration());
+        queryContext.setRequest(requestContext.getProfileRequest());
+
+        Session userSession = getSessionManager().getSession(getUserSessionId(requestContext.getProfileRequest()));
+        if (userSession != null) {
+            queryContext.setUserSession(userSession);
+            ServiceInformation serviceInfo = userSession.getServicesInformation().get(
+                    requestContext.getRelyingPartyId());
+            if (serviceInfo != null) {
+                String principalAuthenticationMethod = serviceInfo.getAuthenticationMethod().getAuthenticationMethod();
+
+                requestContext.setPrincipalAuthenticationMethod(principalAuthenticationMethod);
+                queryContext.setPrincipalAuthenticationMethod(principalAuthenticationMethod);
+            }
+        }
+
+        return queryContext;
+    }
+
+    /**
      * Signs the given assertion if either the current profile configuration or the relying party configuration contains
      * signing credentials.
      * 
@@ -441,18 +457,10 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
 
         boolean signAssertion = false;
 
-        RoleDescriptor relyingPartyRole;
-        try {
-            relyingPartyRole = getMetadataProvider().getRole(requestContext.getRelyingPartyId(),
-                    requestContext.getRelyingPartyRole(), SAML20_PROTOCOL_URI);
-        } catch (MetadataProviderException e) {
-            throw new ProfileException("Unable to lookup entity metadata for relying party "
-                    + requestContext.getRelyingPartyId());
-        }
         AbstractSAML2ProfileConfiguration profileConfig = requestContext.getProfileConfiguration();
 
-        if (relyingPartyRole instanceof SPSSODescriptor) {
-            SPSSODescriptor ssoDescriptor = (SPSSODescriptor) relyingPartyRole;
+        if (requestContext.getRelyingPartyRoleMetadata() instanceof SPSSODescriptor) {
+            SPSSODescriptor ssoDescriptor = (SPSSODescriptor) requestContext.getRelyingPartyRoleMetadata();
             if (ssoDescriptor.getWantAssertionsSigned() != null) {
                 signAssertion = ssoDescriptor.getWantAssertionsSigned().booleanValue();
                 if (log.isDebugEnabled()) {
@@ -503,26 +511,26 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      * @param topLevelCode The top-level status code. Should be from saml-core-2.0-os, sec. 3.2.2.2
      * @param secondLevelCode An optional second-level failure code. Should be from saml-core-2.0-is, sec 3.2.2.2. If
      *            null, no second-level Status element will be set.
-     * @param secondLevelFailureMessage An optional second-level failure message
+     * @param failureMessage An optional second-level failure message
      * 
      * @return a Status object.
      */
-    protected Status buildStatus(String topLevelCode, String secondLevelCode, String secondLevelFailureMessage) {
-        Status status = getStatusBuilder().buildObject();
+    protected Status buildStatus(String topLevelCode, String secondLevelCode, String failureMessage) {
+        Status status = statusBuilder.buildObject();
 
-        StatusCode statusCode = getStatusCodeBuilder().buildObject();
+        StatusCode statusCode = statusCodeBuilder.buildObject();
         statusCode.setValue(DatatypeHelper.safeTrimOrNullString(topLevelCode));
         status.setStatusCode(statusCode);
 
         if (secondLevelCode != null) {
-            StatusCode secondLevelStatusCode = getStatusCodeBuilder().buildObject();
+            StatusCode secondLevelStatusCode = statusCodeBuilder.buildObject();
             secondLevelStatusCode.setValue(DatatypeHelper.safeTrimOrNullString(secondLevelCode));
             statusCode.setStatusCode(secondLevelStatusCode);
         }
 
-        if (secondLevelFailureMessage != null) {
-            StatusMessage msg = getStatusMessageBuilder().buildObject();
-            msg.setMessage(secondLevelFailureMessage);
+        if (failureMessage != null) {
+            StatusMessage msg = statusMessageBuilder.buildObject();
+            msg.setMessage(failureMessage);
             status.setStatusMessage(msg);
         }
 
@@ -546,10 +554,10 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
         requestContext.setSubjectNameID(nameID);
         // TODO handle encryption
 
-        SubjectConfirmation subjectConfirmation = getSubjectConfirmationBuilder().buildObject();
+        SubjectConfirmation subjectConfirmation = subjectConfirmationBuilder.buildObject();
         subjectConfirmation.setMethod(confirmationMethod);
 
-        Subject subject = getSubjectBuilder().buildObject();
+        Subject subject = subjectBuilder.buildObject();
         subject.setNameID(nameID);
         subject.getSubjectConfirmations().add(subjectConfirmation);
 
@@ -560,7 +568,7 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      * Builds a NameID appropriate for this request. NameIDs are built by inspecting the SAML request and metadata,
      * picking a name format that was requested by the relying party or is mutually supported by both the relying party
      * and asserting party as described in their metadata entries. Once a set of supported name formats is determined
-     * the principals attributes are inspected for an attribtue supported an attribute encoder whose category is one of
+     * the principals attributes are inspected for an attribute supported an attribute encoder whose category is one of
      * the supported name formats.
      * 
      * @param requestContext current request context
@@ -576,34 +584,43 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
                     + "/" + requestContext.getRelyingPartyId());
         }
         Map<String, BaseAttribute> principalAttributes = requestContext.getPrincipalAttributes();
-        List<String> supportedNameFormats = getNameIDFormat(requestContext);
+        List<String> supportedNameFormats = getNameFormats(requestContext);
 
         if (log.isDebugEnabled()) {
             log.debug("Supported NameID formats: " + supportedNameFormats);
         }
 
-        if (principalAttributes != null && supportedNameFormats != null) {
-            try {
-                AttributeEncoder<NameID> nameIdEncoder = null;
-                for (BaseAttribute attribute : principalAttributes.values()) {
-                    for (String nameFormat : supportedNameFormats) {
-                        nameIdEncoder = attribute.getEncoderByCategory(nameFormat);
-                        if (nameIdEncoder != null) {
+        if (principalAttributes == null || supportedNameFormats == null) {
+            log.error("No attributes for principal " + requestContext.getPrincipalName() 
+                    + " support constructions of NameID");
+            requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.INVALID_NAMEID_POLICY_URI,
+                    "Unable to construct NameID"));
+            throw new ProfileException("No principal attributes support NameID construction");
+        }
+
+        try {
+            SAML2NameIDAttributeEncoder nameIdEncoder;
+            for (BaseAttribute<?> attribute : principalAttributes.values()) {
+                for (AttributeEncoder encoder : attribute.getEncoders()) {
+                    if (encoder instanceof SAML2NameIDAttributeEncoder) {
+                        nameIdEncoder = (SAML2NameIDAttributeEncoder) encoder;
+                        if (supportedNameFormats.contains(nameIdEncoder.getNameFormat())) {
                             if (log.isDebugEnabled()) {
                                 log.debug("Using attribute " + attribute.getId() + " suppoting NameID format "
-                                        + nameFormat + " to create the NameID for principal "
+                                        + nameIdEncoder.getNameFormat() + " to create the NameID for principal "
                                         + requestContext.getPrincipalName());
                             }
                             return nameIdEncoder.encode(attribute);
                         }
                     }
                 }
-            } catch (AttributeEncodingException e) {
-                throw new ProfileException("Unable to encode NameID attribute", e);
             }
+            requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null, "Unable to construct NameID"));
+            throw new ProfileException("No principal attribute supported encoding into the a supported name ID format.");
+        } catch (AttributeEncodingException e) {
+            requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null, "Unable to construct NameID"));
+            throw new ProfileException("Unable to encode NameID attribute", e);
         }
-
-        throw new ProfileException("No principal attributes support NameID construction");
     }
 
     /**
@@ -615,44 +632,39 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      * 
      * @throws ProfileException thrown if there is a problem determing the NameID format to use
      */
-    protected List<String> getNameIDFormat(SAML2ProfileRequestContext requestContext) throws ProfileException {
+    protected List<String> getNameFormats(SAML2ProfileRequestContext requestContext) throws ProfileException {
         ArrayList<String> nameFormats = new ArrayList<String>();
 
-        try {
-            RoleDescriptor assertingPartyRole = getMetadataProvider().getRole(requestContext.getAssertingPartyId(),
-                    requestContext.getAssertingPartyRole(), SAML20_PROTOCOL_URI);
-            List<String> assertingPartySupportedFormats = getEntitySupportedFormats(assertingPartyRole);
-
-            String nameFormat = null;
-            if (requestContext.getSamlRequest() instanceof AuthnRequest) {
-                AuthnRequest authnRequest = (AuthnRequest) requestContext.getSamlRequest();
-                if (authnRequest.getNameIDPolicy() != null) {
-                    nameFormat = authnRequest.getNameIDPolicy().getFormat();
-                    if (assertingPartySupportedFormats.contains(nameFormat)) {
-                        nameFormats.add(nameFormat);
-                    } else {
-                        throw new ProfileException("NameID format required by relying party is not supported");
-                    }
+        List<String> assertingPartySupportedFormats = getEntitySupportedFormats(requestContext
+                .getAssertingPartyRoleMetadata());
+
+        String nameFormat = null;
+        if (requestContext.getSamlRequest() instanceof AuthnRequest) {
+            AuthnRequest authnRequest = (AuthnRequest) requestContext.getSamlRequest();
+            if (authnRequest.getNameIDPolicy() != null) {
+                nameFormat = authnRequest.getNameIDPolicy().getFormat();
+                if (!DatatypeHelper.isEmpty(nameFormat) && assertingPartySupportedFormats.contains(nameFormat)) {
+                    nameFormats.add(nameFormat);
+                } else {
+                    requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI,
+                            StatusCode.INVALID_NAMEID_POLICY_URI, "Format not supported: " + nameFormat));
+                    throw new ProfileException("NameID format required by relying party is not supported");
                 }
             }
+        }
 
-            if (nameFormats.isEmpty()) {
-                RoleDescriptor relyingPartyRole = getMetadataProvider().getRole(requestContext.getRelyingPartyId(),
-                        requestContext.getRelyingPartyRole(), SAML20_PROTOCOL_URI);
-                List<String> relyingPartySupportedFormats = getEntitySupportedFormats(relyingPartyRole);
-
-                assertingPartySupportedFormats.retainAll(relyingPartySupportedFormats);
-                nameFormats.addAll(assertingPartySupportedFormats);
-            }
-            if (nameFormats.isEmpty()) {
-                nameFormats.add("urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified");
-            }
-
-            return nameFormats;
+        if (nameFormats.isEmpty()) {
+            List<String> relyingPartySupportedFormats = getEntitySupportedFormats(requestContext
+                    .getRelyingPartyRoleMetadata());
 
-        } catch (MetadataProviderException e) {
-            throw new ProfileException("Unable to determine lookup entity metadata", e);
+            assertingPartySupportedFormats.retainAll(relyingPartySupportedFormats);
+            nameFormats.addAll(assertingPartySupportedFormats);
         }
+        if (nameFormats.isEmpty()) {
+            nameFormats.add("urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified");
+        }
+
+        return nameFormats;
     }
 
     /**
@@ -689,21 +701,15 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      * Constructs an SAML response message carrying a request error.
      * 
      * @param requestContext current request context
-     * @param topLevelCode The top-level status code. Should be from saml-core-2.0-os, sec. 3.2.2.2
-     * @param secondLevelCode An optional second-level failure code. Should be from saml-core-2.0-is, sec 3.2.2.2. If
-     *            null, no second-level Status element will be set.
-     * @param secondLevelFailureMessage An optional second-level failure message
      * 
      * @return the constructed error response
      */
-    protected Response buildErrorResponse(SAML2ProfileRequestContext requestContext, String topLevelCode,
-            String secondLevelCode, String secondLevelFailureMessage) {
-        Response samlResponse = getResponseBuilder().buildObject();
+    protected Response buildErrorResponse(SAML2ProfileRequestContext requestContext) {
+        Response samlResponse = responseBuilder.buildObject();
         samlResponse.setIssueInstant(new DateTime());
         populateStatusResponse(requestContext, samlResponse);
 
-        Status status = buildStatus(topLevelCode, secondLevelCode, secondLevelFailureMessage);
-        samlResponse.setStatus(status);
+        samlResponse.setStatus(requestContext.getFailureStatus());
 
         return samlResponse;
     }
@@ -724,6 +730,9 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
         auditLogEntry.setRequestId(context.getSamlRequest().getID());
         auditLogEntry.setResponseBinding(context.getMessageEncoder().getBindingURI());
         auditLogEntry.setResponseId(context.getSamlResponse().getID());
+        if(context.getPrincipalAttributes() != null){
+            auditLogEntry.getReleasedAttributes().addAll(context.getPrincipalAttributes().keySet());
+        }
         getAduitLog().log(Level.CRITICAL, auditLogEntry);
     }
 
@@ -749,6 +758,9 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
         /** The NameID of the subject of this request. */
         private NameID subjectNameID;
 
+        /** The request failure status. */
+        private Status failureStatus;
+
         /**
          * Constructor.
          * 
@@ -831,5 +843,23 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
         public void setSamlResponse(ResponseType response) {
             samlResponse = response;
         }
+
+        /**
+         * Gets the status reflecting a request failure.
+         * 
+         * @return status reflecting a request failure
+         */
+        public Status getFailureStatus() {
+            return failureStatus;
+        }
+
+        /**
+         * Sets the status reflecting a request failure.
+         * 
+         * @param status status reflecting a request failure
+         */
+        public void setFailureStatus(Status status) {
+            failureStatus = status;
+        }
     }
 }
\ No newline at end of file