Now with SAML 1 attribute query goodness (though not yet tested)
authorlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Tue, 5 Jun 2007 19:21:07 +0000 (19:21 +0000)
committerlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Tue, 5 Jun 2007 19:21:07 +0000 (19:21 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@2233 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/edu/internet2/middleware/shibboleth/idp/profile/saml1/AbstractSAML1ProfileHandler.java
src/edu/internet2/middleware/shibboleth/idp/profile/saml1/AttributeQuery.java [deleted file]
src/edu/internet2/middleware/shibboleth/idp/profile/saml1/AttributeQueryProfileHandler.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AbstractAuthenticationRequest.java
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AbstractSAML2ProfileHandler.java
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AttributeQueryProfileHandler.java
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AuthenticationRequestException.java [deleted file]
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AuthenticationRequestProfileHandler.java [new file with mode: 0644]

index 68586c0..57fee58 100644 (file)
 
 package edu.internet2.middleware.shibboleth.idp.profile.saml1;
 
-import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
 
+import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
 
 import org.apache.log4j.Logger;
+import org.joda.time.DateTime;
+import org.opensaml.common.SAMLObject;
 import org.opensaml.common.SAMLObjectBuilder;
 import org.opensaml.common.SAMLVersion;
-import org.opensaml.common.binding.BindingException;
-import org.opensaml.common.binding.encoding.MessageEncoder;
 import org.opensaml.common.impl.SAMLObjectContentReference;
+import org.opensaml.common.xml.SAMLConstants;
 import org.opensaml.saml1.core.Assertion;
+import org.opensaml.saml1.core.Audience;
+import org.opensaml.saml1.core.AudienceRestrictionCondition;
+import org.opensaml.saml1.core.Conditions;
+import org.opensaml.saml1.core.ConfirmationMethod;
+import org.opensaml.saml1.core.NameIdentifier;
+import org.opensaml.saml1.core.RequestAbstractType;
 import org.opensaml.saml1.core.Response;
+import org.opensaml.saml1.core.ResponseAbstractType;
+import org.opensaml.saml1.core.Statement;
 import org.opensaml.saml1.core.Status;
 import org.opensaml.saml1.core.StatusCode;
 import org.opensaml.saml1.core.StatusMessage;
-import org.opensaml.saml2.metadata.Endpoint;
+import org.opensaml.saml1.core.Subject;
+import org.opensaml.saml1.core.SubjectConfirmation;
+import org.opensaml.saml2.metadata.AttributeAuthorityDescriptor;
+import org.opensaml.saml2.metadata.AuthnAuthorityDescriptor;
+import org.opensaml.saml2.metadata.NameIDFormat;
+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;
 import org.opensaml.xml.signature.Signer;
+import org.opensaml.xml.util.DatatypeHelper;
 
+import edu.internet2.middleware.shibboleth.common.attribute.AttributeRequestException;
+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.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.RelyingPartyConfiguration;
 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml1.AbstractSAML1ProfileConfiguration;
 import edu.internet2.middleware.shibboleth.idp.profile.AbstractSAMLProfileHandler;
 
-
 /**
  * Common implementation details for profile handlers.
  */
 public abstract class AbstractSAML1ProfileHandler extends AbstractSAMLProfileHandler {
-    
+
     /** SAML Version for this profile handler. */
     public static final SAMLVersion SAML_VERSION = SAMLVersion.VERSION_11;
-    
+
     /** Class logger. */
     private static Logger log = Logger.getLogger(AbstractSAML1ProfileHandler.class);
-    
+
+    /** Builder of Response objects. */
+    private SAMLObjectBuilder<Response> responseBuilder;
+
+    /** Builder of Assertion objects. */
+    private SAMLObjectBuilder<Assertion> assertionBuilder;
+
+    /** Builder of Conditions objects. */
+    private SAMLObjectBuilder<Conditions> conditionsBuilder;
+
+    /** Builder of AudienceRestrictionCondition objects. */
+    private SAMLObjectBuilder<AudienceRestrictionCondition> audienceRestrictionConditionBuilder;
+
+    /** Builder of AudienceRestrictionCondition objects. */
+    private SAMLObjectBuilder<Audience> audienceBuilder;
+
+    /** Builder of NameIdentifier objects. */
+    private SAMLObjectBuilder<NameIdentifier> nameIdBuilder;
+
+    /** Builder of SubjectConfirmation objects. */
+    private SAMLObjectBuilder<SubjectConfirmation> subjectConfirmationBuilder;
+
+    /** Builder of ConfirmationMethod objects. */
+    private SAMLObjectBuilder<ConfirmationMethod> confirmationMethodBuilder;
+
+    /** Builder of Subject objects. */
+    private SAMLObjectBuilder<Subject> subjectBuilder;
+
     /** Builder for Status objects. */
-    protected SAMLObjectBuilder<Status> statusBuilder;
-    
+    private SAMLObjectBuilder<Status> statusBuilder;
+
     /** Builder for StatusCode objects. */
-    protected SAMLObjectBuilder<StatusCode> statusCodeBuilder;
-    
+    private SAMLObjectBuilder<StatusCode> statusCodeBuilder;
+
     /** Builder for StatusMessage objects. */
-    protected SAMLObjectBuilder<StatusMessage> statusMessageBuilder;
-    
+    private SAMLObjectBuilder<StatusMessage> statusMessageBuilder;
+
     /** For building signature. */
     private XMLObjectBuilder<Signature> signatureBuilder;
-    
+
     /**
      * Default constructor.
      */
     @SuppressWarnings("unchecked")
-    public AbstractSAML1ProfileHandler(){
+    public AbstractSAML1ProfileHandler() {
         super();
-        statusBuilder        = (SAMLObjectBuilder<Status>) getBuilderFactory().getBuilder(Status.DEFAULT_ELEMENT_NAME);
-        statusCodeBuilder    = (SAMLObjectBuilder<StatusCode>) getBuilderFactory().getBuilder(StatusCode.DEFAULT_ELEMENT_NAME);
-        statusMessageBuilder = (SAMLObjectBuilder<StatusMessage>) getBuilderFactory().getBuilder(StatusMessage.DEFAULT_ELEMENT_NAME);
-        signatureBuilder     = (XMLObjectBuilder<Signature>) getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME);
-    }
-    
-    /**
-     * Build a SAML 1 Status element.
-     *
-     * @param statusCode The status code - see oasis-sstc-saml-core-1.1, section 3.4.3.1.
-     * @param statusMessage The status message, or <code>null</code> if none is to be set.
-     *
-     * @return The Status object, or <code>null</code> on error.
-     */
-    protected Status buildStatus(String statusCode, String statusMessage) {
-        
-        if (statusCode == null || statusCode.equals("")) {
-            return null;
-        }
-        
-        Status status = statusBuilder.buildObject();
-        StatusCode sc = statusCodeBuilder.buildObject();
-        sc.setValue(statusCode);
-        status.setStatusCode(sc);
-        
-        if (statusMessage != null || !(statusMessage.equals(""))) {
-            
-            StatusMessage sm = statusMessageBuilder.buildObject();
-            sm.setMessage(statusMessage);
-            status.setStatusMessage(sm);
-        }
-        
+        responseBuilder = (SAMLObjectBuilder<Response>) getBuilderFactory().getBuilder(Response.DEFAULT_ELEMENT_NAME);
+        assertionBuilder = (SAMLObjectBuilder<Assertion>) getBuilderFactory()
+                .getBuilder(Assertion.DEFAULT_ELEMENT_NAME);
+        conditionsBuilder = (SAMLObjectBuilder<Conditions>) getBuilderFactory().getBuilder(
+                Conditions.DEFAULT_ELEMENT_NAME);
+        audienceRestrictionConditionBuilder = (SAMLObjectBuilder<AudienceRestrictionCondition>) getBuilderFactory()
+                .getBuilder(AudienceRestrictionCondition.DEFAULT_ELEMENT_NAME);
+        audienceBuilder = (SAMLObjectBuilder<Audience>) getBuilderFactory().getBuilder(Audience.DEFAULT_ELEMENT_NAME);
+        nameIdBuilder = (SAMLObjectBuilder<NameIdentifier>) getBuilderFactory().getBuilder(
+                NameIdentifier.DEFAULT_ELEMENT_NAME);
+        subjectConfirmationBuilder = (SAMLObjectBuilder<SubjectConfirmation>) getBuilderFactory().getBuilder(
+                SubjectConfirmation.DEFAULT_ELEMENT_NAME);
+        confirmationMethodBuilder = (SAMLObjectBuilder<ConfirmationMethod>) getBuilderFactory().getBuilder(
+                ConfirmationMethod.DEFAULT_ELEMENT_NAME);
+        subjectBuilder = (SAMLObjectBuilder<Subject>) getBuilderFactory().getBuilder(Subject.DEFAULT_ELEMENT_NAME);
+        statusBuilder = (SAMLObjectBuilder<Status>) getBuilderFactory().getBuilder(Status.DEFAULT_ELEMENT_NAME);
+        statusCodeBuilder = (SAMLObjectBuilder<StatusCode>) getBuilderFactory().getBuilder(
+                StatusCode.DEFAULT_ELEMENT_NAME);
+        statusMessageBuilder = (SAMLObjectBuilder<StatusMessage>) getBuilderFactory().getBuilder(
+                StatusMessage.DEFAULT_ELEMENT_NAME);
+        signatureBuilder = (XMLObjectBuilder<Signature>) getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME);
+    }
+
+    /**
+     * Convenience method for getting the SAML 1 Response builder.
+     * 
+     * @return SAML 1 Response builder
+     */
+    public SAMLObjectBuilder<Response> getResponseBuilder() {
+        return responseBuilder;
+    }
+
+    /**
+     * Convenience method for getting the SAML 1 Assertion builder.
+     * 
+     * @return SAML 1 Assertion builder
+     */
+    public SAMLObjectBuilder<Assertion> getAssertionBuilder() {
+        return assertionBuilder;
+    }
+
+    /**
+     * Convenience method for getting the SAML 1 Conditions builder.
+     * 
+     * @return SAML 1 Conditions builder
+     */
+    public SAMLObjectBuilder<Conditions> getConditionsBuilder() {
+        return conditionsBuilder;
+    }
+
+    /**
+     * Convenience method for getting the SAML 1 AudienceRestrictionCondition builder.
+     * 
+     * @return SAML 1 AudienceRestrictionCondition builder
+     */
+    public SAMLObjectBuilder<AudienceRestrictionCondition> getAudienceRestrictionConditionBuilder() {
+        return audienceRestrictionConditionBuilder;
+    }
+
+    /**
+     * Convenience method for getting the SAML 1 Audience builder.
+     * 
+     * @return SAML 1 Audience builder
+     */
+    public SAMLObjectBuilder<Audience> getAudienceBuilder() {
+        return audienceBuilder;
+    }
+
+    /**
+     * Convenience method for getting the SAML 1 NameIdentifier builder.
+     * 
+     * @return SAML 1 NameIdentifier builder
+     */
+    public SAMLObjectBuilder<NameIdentifier> getNameIdentifierBuilder() {
+        return nameIdBuilder;
+    }
+
+    /**
+     * Convenience method for getting the SAML 1 SubjectConfirmation builder.
+     * 
+     * @return SAML 1 SubjectConfirmation builder
+     */
+    public SAMLObjectBuilder<SubjectConfirmation> getSubjectConfirmationBuilder() {
+        return subjectConfirmationBuilder;
+    }
+
+    /**
+     * Convenience method for getting the SAML 1 ConfirmationMethod builder.
+     * 
+     * @return SAML 1 ConfirmationMethod builder
+     */
+    public SAMLObjectBuilder<ConfirmationMethod> getConfirmationMethodBuilder() {
+        return confirmationMethodBuilder;
+    }
+
+    /**
+     * Convenience method for getting the SAML 1 Subject builder.
+     * 
+     * @return SAML 1 Subject builder
+     */
+    public SAMLObjectBuilder<Subject> getSubjectBuilder() {
+        return subjectBuilder;
+    }
+
+    /**
+     * Convenience method for getting the SAML 1 Status builder.
+     * 
+     * @return SAML 1 Status builder
+     */
+    public SAMLObjectBuilder<Status> getStatusBuilder() {
+        return statusBuilder;
+    }
+
+    /**
+     * Convenience method for getting the SAML 1 StatusCode builder.
+     * 
+     * @return SAML 2 StatusCode builder
+     */
+    public SAMLObjectBuilder<StatusCode> getStatusCodeBuilder() {
+        return statusCodeBuilder;
+    }
+
+    /**
+     * Convenience method for getting the SAML 1 StatusMessage builder.
+     * 
+     * @return SAML StatusMessage builder
+     */
+    public SAMLObjectBuilder<StatusMessage> getStatusMessageBuilder() {
+        return statusMessageBuilder;
+    }
+
+    /**
+     * Builds a response to the attribute query within the request context.
+     * 
+     * @param requestContext current request context
+     * @param statements the statements to include in the response
+     * 
+     * @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(SAML1ProfileRequestContext requestContext, List<Statement> statements)
+            throws ProfileException, AttributeRequestException {
+
+        DateTime issueInstant = new DateTime();
+
+        // create the assertion and add the attribute statement
+        Assertion assertion = buildAssertion(requestContext, issueInstant);
+        if (statements != null) {
+            assertion.getStatements().addAll(statements);
+        }
+
+        // create the SAML response and add the assertion
+        Response samlResponse = getResponseBuilder().buildObject();
+        samlResponse.setIssueInstant(issueInstant);
+        populateStatusResponse(requestContext, samlResponse);
+
+        samlResponse.getAssertions().add(assertion);
+
+        // sign the assertion if it should be signed
+        signAssertion(requestContext, assertion);
+
+        Status status = buildStatus(StatusCode.SUCCESS, null, null);
+        samlResponse.setStatus(status);
+
+        return samlResponse;
+    }
+
+    /**
+     * Builds a basic assertion with its id, issue instant, SAML version, issuer, subject, and conditions populated.
+     * 
+     * @param requestContext current request context
+     * @param issueInstant time to use as assertion issue instant
+     * 
+     * @return the built assertion
+     */
+    protected Assertion buildAssertion(SAML1ProfileRequestContext requestContext, DateTime issueInstant) {
+        Assertion assertion = getAssertionBuilder().buildObject();
+        assertion.setID(getIdGenerator().generateIdentifier());
+        assertion.setIssueInstant(issueInstant);
+        assertion.setVersion(SAMLVersion.VERSION_11);
+        assertion.setIssuer(requestContext.getAssertingPartyId());
+
+        Conditions conditions = buildConditions(requestContext, issueInstant);
+        assertion.setConditions(conditions);
+
+        return assertion;
+    }
+
+    /**
+     * Builds a SAML assertion condition set. The following fields are set; not before, not on or after, audience
+     * restrictions, and proxy restrictions.
+     * 
+     * @param requestContext current request context
+     * @param issueInstant timestamp the assertion was created
+     * 
+     * @return constructed conditions
+     */
+    protected Conditions buildConditions(SAML1ProfileRequestContext requestContext, DateTime issueInstant) {
+        AbstractSAML1ProfileConfiguration profileConfig = requestContext.getProfileConfiguration();
+
+        Conditions conditions = getConditionsBuilder().buildObject();
+        conditions.setNotBefore(issueInstant);
+        conditions.setNotOnOrAfter(issueInstant.plus(profileConfig.getAssertionLifetime()));
+
+        Collection<String> audiences;
+
+        // add audience restrictions
+        audiences = profileConfig.getAssertionAudiences();
+        if (audiences != null && audiences.size() > 0) {
+            AudienceRestrictionCondition audienceRestriction = getAudienceRestrictionConditionBuilder().buildObject();
+            for (String audienceUri : audiences) {
+                Audience audience = getAudienceBuilder().buildObject();
+                audience.setUri(audienceUri);
+                audienceRestriction.getAudiences().add(audience);
+            }
+            conditions.getAudienceRestrictionConditions().add(audienceRestriction);
+        }
+
+        return conditions;
+    }
+
+    /**
+     * Builds the SAML subject for the user for the service provider.
+     * 
+     * @param requestContext current request context
+     * @param confirmationMethod subject confirmation method used for the subject
+     * 
+     * @return SAML subject for the user for the service provider
+     * 
+     * @throws ProfileException thrown if a NameID can not be created either because there was a problem encoding the
+     *             name ID attribute or because there are no supported name formats
+     */
+    protected Subject buildSubject(SAML1ProfileRequestContext requestContext, String confirmationMethod)
+            throws ProfileException {
+        NameIdentifier nameID = buildNameId(requestContext);
+        requestContext.setSubjectNameID(nameID);
+
+        ConfirmationMethod method = getConfirmationMethodBuilder().buildObject();
+        method.setConfirmationMethod(confirmationMethod);
+
+        SubjectConfirmation subjectConfirmation = getSubjectConfirmationBuilder().buildObject();
+        subjectConfirmation.getConfirmationMethods().add(method);
+
+        Subject subject = getSubjectBuilder().buildObject();
+        subject.setNameIdentifier(nameID);
+        subject.setSubjectConfirmation(subjectConfirmation);
+
+        return subject;
+    }
+
+    /**
+     * Builds a NameIdentifier appropriate for this request. NameIdentifier 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 supported name formats.
+     * 
+     * @param requestContext current request context
+     * 
+     * @return the NameIdentifier appropriate for this request
+     * 
+     * @throws ProfileException thrown if a NameIdentifier can not be created either because there was a problem
+     *             encoding the name ID attribute or because there are no supported name formats
+     */
+    protected NameIdentifier buildNameId(SAML1ProfileRequestContext requestContext) throws ProfileException {
+        if (log.isDebugEnabled()) {
+            log.debug("Building assertion NameIdentifier to relying party " + requestContext.getRelyingPartyId()
+                    + " for principal " + requestContext.getPrincipalName());
+        }
+        Map<String, BaseAttribute> principalAttributes = requestContext.getPrincipalAttributes();
+        List<String> supportedNameFormats = getNameFormats(requestContext);
+
+        if (log.isDebugEnabled()) {
+            log.debug("Supported name formats: " + supportedNameFormats);
+        }
+
+        if (principalAttributes != null && supportedNameFormats != null) {
+            try {
+                AttributeEncoder<NameIdentifier> nameIdEncoder = null;
+                for (BaseAttribute attribute : principalAttributes.values()) {
+                    for (String nameFormat : supportedNameFormats) {
+                        nameIdEncoder = attribute.getEncoderByCategory(nameFormat);
+                        if (nameIdEncoder != null) {
+                            if (log.isDebugEnabled()) {
+                                log.debug("Using attribute " + attribute.getId() + " suppoting name format "
+                                        + nameFormat + " to create the NameIdentifier for principal "
+                                        + requestContext.getPrincipalName());
+                            }
+                            return nameIdEncoder.encode(attribute);
+                        }
+                    }
+                }
+            } catch (AttributeEncodingException e) {
+                throw new ProfileException("Unable to encode NameIdentifier attribute", e);
+            }
+        }
+
+        throw new ProfileException("No principal attributes support NameIdentifier construction");
+    }
+
+    /**
+     * Gets the NameIdentifier format to use when creating NameIdentifiers for the relying party.
+     * 
+     * @param requestContext current request context
+     * 
+     * @return list of formats that may be used with the relying party
+     * 
+     * @throws ProfileException thrown if there is a problem determing the NameIdentifier format to use
+     */
+    protected List<String> getNameFormats(SAML1ProfileRequestContext requestContext) throws ProfileException {
+        ArrayList<String> nameFormats = new ArrayList<String>();
+
+        try {
+            RoleDescriptor assertingPartyRole = getMetadataProvider().getRole(requestContext.getAssertingPartyId(),
+                    requestContext.getAssertingPartyRole(), SAMLConstants.SAML1P_NS);
+            List<String> assertingPartySupportedFormats = getEntitySupportedFormats(assertingPartyRole);
+
+            if (nameFormats.isEmpty()) {
+                RoleDescriptor relyingPartyRole = getMetadataProvider().getRole(requestContext.getRelyingPartyId(),
+                        requestContext.getRelyingPartyRole(), SAMLConstants.SAML1P_NS);
+                List<String> relyingPartySupportedFormats = getEntitySupportedFormats(relyingPartyRole);
+
+                assertingPartySupportedFormats.retainAll(relyingPartySupportedFormats);
+                nameFormats.addAll(assertingPartySupportedFormats);
+            }
+            if (nameFormats.isEmpty()) {
+                nameFormats.add("urn:oasis:names:tc:SAML:1.0:nameid-format:unspecified");
+            }
+
+            return nameFormats;
+
+        } catch (MetadataProviderException e) {
+            throw new ProfileException("Unable to determine lookup entity metadata", e);
+        }
+    }
+
+    /**
+     * Gets the list of NameIdentifier formats supported for a given role.
+     * 
+     * @param role the role to get the list of supported NameIdentifier formats
+     * 
+     * @return list of supported NameIdentifier formats
+     */
+    protected List<String> getEntitySupportedFormats(RoleDescriptor role) {
+        List<NameIDFormat> nameIDFormats = null;
+
+        if (role instanceof SSODescriptor) {
+            nameIDFormats = ((SSODescriptor) role).getNameIDFormats();
+        } else if (role instanceof AuthnAuthorityDescriptor) {
+            nameIDFormats = ((AuthnAuthorityDescriptor) role).getNameIDFormats();
+        } else if (role instanceof PDPDescriptor) {
+            nameIDFormats = ((PDPDescriptor) role).getNameIDFormats();
+        } else if (role instanceof AttributeAuthorityDescriptor) {
+            nameIDFormats = ((AttributeAuthorityDescriptor) role).getNameIDFormats();
+        }
+
+        ArrayList<String> supportedFormats = new ArrayList<String>();
+        if (nameIDFormats != null) {
+            for (NameIDFormat format : nameIDFormats) {
+                supportedFormats.add(format.getFormat());
+            }
+        }
+
+        return supportedFormats;
+    }
+
+    /**
+     * Constructs an SAML response message carrying a request error.
+     * 
+     * @param requestContext current request context
+     * @param topLevelCode top-level status code
+     * @param secondLevelCode second-level status code
+     * @param failureMessage An optional second-level failure message
+     * 
+     * @return the constructed error response
+     */
+    protected Response buildErrorResponse(SAML1ProfileRequestContext requestContext, String topLevelCode,
+            String secondLevelCode, String failureMessage) {
+        Response samlResponse = getResponseBuilder().buildObject();
+        samlResponse.setIssueInstant(new DateTime());
+        populateStatusResponse(requestContext, samlResponse);
+
+        Status status = buildStatus(topLevelCode, secondLevelCode, failureMessage);
+        samlResponse.setStatus(status);
+
+        return samlResponse;
+    }
+
+    /**
+     * Populates the response's id, in response to, issue instant, version, and issuer properties.
+     * 
+     * @param requestContext current request context
+     * @param response the response to populate
+     */
+    protected void populateStatusResponse(SAML1ProfileRequestContext requestContext, ResponseAbstractType response) {
+        response.setID(getIdGenerator().generateIdentifier());
+
+        SAMLObject samlMessage = requestContext.getSamlRequest();
+        if (samlMessage != null && samlMessage instanceof RequestAbstractType) {
+            response.setInResponseTo(((RequestAbstractType) samlMessage).getID());
+        }
+        response.setVersion(SAMLVersion.VERSION_11);
+    }
+
+    /**
+     * Build a status message, with an optional second-level failure message.
+     * 
+     * @param topLevelCode top-level status code
+     * @param secondLevelCode second-level status code
+     * @param failureMessage An optional second-level failure message
+     * 
+     * @return a Status object.
+     */
+    protected Status buildStatus(String topLevelCode, String secondLevelCode, String failureMessage) {
+        Status status = getStatusBuilder().buildObject();
+
+        StatusCode statusCode = getStatusCodeBuilder().buildObject();
+        statusCode.setValue(DatatypeHelper.safeTrimOrNullString(topLevelCode));
+        status.setStatusCode(statusCode);
+
+        if (secondLevelCode != null) {
+            StatusCode secondLevelStatusCode = getStatusCodeBuilder().buildObject();
+            secondLevelStatusCode.setValue(DatatypeHelper.safeTrimOrNullString(secondLevelCode));
+            statusCode.setStatusCode(secondLevelStatusCode);
+        }
+
+        if (failureMessage != null) {
+            StatusMessage msg = getStatusMessageBuilder().buildObject();
+            msg.setMessage(failureMessage);
+            status.setStatusMessage(msg);
+        }
+
         return status;
     }
-    
+
     /**
      * Signs the given assertion if either the current profile configuration or the relying party configuration contains
      * signing credentials.
-     *
+     * 
+     * @param requestContext current request context
      * @param assertion assertion to sign
-     * @param rpConfig relying party configuration
-     * @param profileConfig current profile configuration
+     * 
+     * @throws ProfileException thrown if the metadata can not be located for the relying party or, if signing is
+     *             required, if a signing credential is not configured
      */
-    protected void signAssertion(Assertion assertion, RelyingPartyConfiguration rpConfig,
-            AbstractSAML1ProfileConfiguration profileConfig) {
-        if (!profileConfig.getSignAssertions()) {
+    protected void signAssertion(SAML1ProfileRequestContext requestContext, Assertion assertion)
+            throws ProfileException {
+        if (log.isDebugEnabled()) {
+            log.debug("Determining if SAML assertion to relying party " + requestContext.getRelyingPartyId()
+                    + " should be signed");
+        }
+
+        boolean signAssertion = false;
+
+        RoleDescriptor relyingPartyRole;
+        try {
+            relyingPartyRole = getMetadataProvider().getRole(requestContext.getRelyingPartyId(),
+                    requestContext.getRelyingPartyRole(), SAMLConstants.SAML20P_NS);
+        } catch (MetadataProviderException e) {
+            throw new ProfileException("Unable to lookup entity metadata for relying party "
+                    + requestContext.getRelyingPartyId());
+        }
+        AbstractSAML1ProfileConfiguration profileConfig = requestContext.getProfileConfiguration();
+
+        if (relyingPartyRole instanceof SPSSODescriptor) {
+            SPSSODescriptor ssoDescriptor = (SPSSODescriptor) relyingPartyRole;
+            if (ssoDescriptor.getWantAssertionsSigned() != null) {
+                signAssertion = ssoDescriptor.getWantAssertionsSigned().booleanValue();
+                if (log.isDebugEnabled()) {
+                    log.debug("Entity metadata for relying party " + requestContext.getRelyingPartyId()
+                            + " indicates to sign assertions: " + signAssertion);
+                }
+            }
+        } else if (profileConfig.getSignAssertions()) {
+            signAssertion = true;
+            log.debug("IdP relying party configuration "
+                    + requestContext.getRelyingPartyConfiguration().getRelyingPartyId()
+                    + " indicates to sign assertions: " + signAssertion);
+        }
+
+        if (!signAssertion) {
             return;
         }
-        
+
+        if (log.isDebugEnabled()) {
+            log.debug("Determining signing credntial for assertion to relying party "
+                    + requestContext.getRelyingPartyId());
+        }
         Credential signatureCredential = profileConfig.getSigningCredential();
         if (signatureCredential == null) {
-            signatureCredential = rpConfig.getDefaultSigningCredential();
+            signatureCredential = requestContext.getRelyingPartyConfiguration().getDefaultSigningCredential();
         }
-        
+
         if (signatureCredential == null) {
-            return;
+            throw new ProfileException("No signing credential is specified for relying party configuration "
+                    + requestContext.getRelyingPartyConfiguration().getProviderId()
+                    + " or it's SAML2 attribute query profile configuration");
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("Signing assertion to relying party " + requestContext.getRelyingPartyId());
         }
-        
         SAMLObjectContentReference contentRef = new SAMLObjectContentReference(assertion);
         Signature signature = signatureBuilder.buildObject(Signature.DEFAULT_ELEMENT_NAME);
         signature.getContentReferences().add(contentRef);
         assertion.setSignature(signature);
-        
+
         Signer.signObject(signature);
     }
-    
-    /**
-     * Encode a SAML Response.
-     * 
-     * @param binding The SAML protocol binding to use.
-     * @param profileResponse The Raw output stream to send the message to.
-     * @param samlResponse The SAML Response to send.
-     * @param relyingParty The relying party to send the message to.
-     * @param roleDescriptor The role of the message sender.
-     * @param endpoint The endpoint to which the message should be send.
-     * 
-     * @throws ProfileException On error.
-     */
-    protected void encodeResponse(String binding,final ProfileResponse<ServletResponse> profileResponse,
-            final Response samlResponse, final RelyingPartyConfiguration relyingParty,
-            final RoleDescriptor roleDescriptor, final Endpoint endpoint) throws ProfileException {
-        
-        MessageEncoder<ServletResponse> encoder = getMessageEncoderFactory().getMessageEncoder(binding);
-        if (encoder == null) {
-            log.error("No MessageEncoder registered for " + binding);
-            throw new ProfileException("No MessageEncoder registered for " + binding);
-        }
-        
-        encoder.setResponse(profileResponse.getRawResponse());
-        encoder.setIssuer(relyingParty.getProviderId());
-        encoder.setMetadataProvider(getRelyingPartyConfigurationManager().getMetadataProvider());
-        encoder.setRelyingPartyRole(roleDescriptor);
-        encoder.setSigningCredential(relyingParty.getDefaultSigningCredential());
-        encoder.setSamlMessage(samlResponse);
-        encoder.setRelyingPartyEndpoint(endpoint);
-        
-        try {
-            encoder.encode();
-        } catch (BindingException ex) {
-            log.error("Unable to encode response the relying party: " + relyingParty.getRelyingPartyId(), ex);
-            throw new ProfileException("Unable to encode response the relying party: "
-                    + relyingParty.getRelyingPartyId(), ex);
+
+    /**
+     * Contextual object used to accumlate information as profile requests are being processed.
+     * 
+     * @param <RequestType> type of SAML 1 request
+     * @param <ResponseType> type of SAML 1 response
+     * @param <ProfileConfigurationType> configuration type for this profile
+     */
+    protected class SAML1ProfileRequestContext<RequestType extends SAMLObject, ResponseType extends ResponseAbstractType, ProfileConfigurationType extends AbstractSAML1ProfileConfiguration>
+            extends SAMLProfileRequestContext {
+
+        /** SAML request message. */
+        private RequestType samlRequest;
+
+        /** SAML response message. */
+        private ResponseType samlResponse;
+
+        /** Request profile configuration. */
+        private ProfileConfigurationType profileConfiguration;
+
+        /** The NameIdentifier of the subject of this request. */
+        private NameIdentifier subjectNameIdentifier;
+
+        /**
+         * Constructor.
+         * 
+         * @param request current profile request
+         * @param response current profile response
+         */
+        public SAML1ProfileRequestContext(ProfileRequest<ServletRequest> request,
+                ProfileResponse<ServletResponse> response) {
+            super(request, response);
+        }
+
+        /**
+         * Gets the NameIdentifier of the subject of this request.
+         * 
+         * @return NameIdentifier of the subject of this request
+         */
+        public NameIdentifier getSubjectNameID() {
+            return subjectNameIdentifier;
+        }
+
+        /**
+         * Sets the NameIdentifier of the subject of this request.
+         * 
+         * @param id NameIdentifier of the subject of this request
+         */
+        public void setSubjectNameID(NameIdentifier id) {
+            subjectNameIdentifier = id;
+        }
+
+        /**
+         * Gets the profile configuration for this request.
+         * 
+         * @return profile configuration for this request
+         */
+        public ProfileConfigurationType getProfileConfiguration() {
+            return profileConfiguration;
+        }
+
+        /**
+         * Sets the profile configuration for this request.
+         * 
+         * @param configuration profile configuration for this request
+         */
+        public void setProfileConfiguration(ProfileConfigurationType configuration) {
+            profileConfiguration = configuration;
+        }
+
+        /**
+         * Gets the SAML request message.
+         * 
+         * @return SAML request message
+         */
+        public RequestType getSamlRequest() {
+            return samlRequest;
+        }
+
+        /**
+         * Sets the SAML request message.
+         * 
+         * @param request SAML request message
+         */
+        public void setSamlRequest(RequestType request) {
+            samlRequest = request;
+        }
+
+        /**
+         * Gets the SAML response message.
+         * 
+         * @return SAML response message
+         */
+        public ResponseType getSamlResponse() {
+            return samlResponse;
+        }
+
+        /**
+         * Sets the SAML response message.
+         * 
+         * @param response SAML response message
+         */
+        public void setSamlResponse(ResponseType response) {
+            samlResponse = response;
         }
-        
     }
 }
\ No newline at end of file
diff --git a/src/edu/internet2/middleware/shibboleth/idp/profile/saml1/AttributeQuery.java b/src/edu/internet2/middleware/shibboleth/idp/profile/saml1/AttributeQuery.java
deleted file mode 100644 (file)
index 91dff4e..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
- *
- * Licensed 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 edu.internet2.middleware.shibboleth.idp.profile.saml1;
-
-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 javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-
-import org.apache.log4j.Logger;
-import org.joda.time.DateTime;
-import org.opensaml.common.SAMLObjectBuilder;
-import org.opensaml.common.binding.BindingException;
-import org.opensaml.saml1.core.Advice;
-import org.opensaml.saml1.core.Assertion;
-import org.opensaml.saml1.core.AttributeStatement;
-import org.opensaml.saml1.core.Conditions;
-import org.opensaml.saml1.core.Response;
-import org.opensaml.saml1.core.Status;
-import org.opensaml.saml1.core.StatusCode;
-import org.opensaml.saml1.core.Subject;
-import org.opensaml.saml1.core.SubjectStatement;
-import org.opensaml.xml.encryption.EncryptionException;
-
-import edu.internet2.middleware.shibboleth.common.attribute.filtering.AttributeFilteringException;
-import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolutionException;
-
-/**
- * SAML 1 Attribute Query profile handler.
- */
-public class AttributeQuery extends AbstractSAML1ProfileHandler {
-
-    /** {@inheritDoc} */
-    public String getProfileId() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    /** {@inheritDoc} */
-    public void processRequest(ProfileRequest<ServletRequest> request, ProfileResponse<ServletResponse> response) throws ProfileException {
-        // TODO Auto-generated method stub
-        
-    }
-//
-//    /** Class logger. */
-//    private static Logger log = Logger.getLogger(AttributeQuery.class);
-//
-//    /** {@inheritDoc} */
-//    public boolean processRequest(ProfileRequest request, ProfileResponse response) throws ServletException {
-//        if (log.isDebugEnabled()) {
-//            log.debug("begin processRequest");
-//        }
-//
-//        // get message from the decoder
-//        org.opensaml.saml1.core.AttributeQuery message = null;
-//        try {
-//            message = (org.opensaml.saml1.core.AttributeQuery) decodeMessage(request);
-//        } catch (BindingException e) {
-//            log.error("Error decoding attribute query message", e);
-//            throw new ServletException("Error decoding attribute query message");
-//        }
-//
-//        // get attribute statement from attribute authority
-//        AttributeAuthority aa = new AttributeAuthority();
-//        aa.setAttributeResolver(getAttributeResolver());
-//        aa.setFilteringEngine(getFilteringEngine());
-//        aa.setRelyingPartyConfiguration(getRelyingPartyConfiguration());
-//        aa.setSecurityPolicy(getDecoder().getSecurityPolicy());
-//        aa.setRequest(request);
-//        AttributeStatement statement = null;
-//        try {
-//            statement = aa.performAttributeQuery(message);
-//        } catch (AttributeResolutionException e) {
-//            log.error("Error resolving attributes", e);
-//            throw new ServletException("Error resolving attributes");
-//        } catch (FilteringException e) {
-//            log.error("Error filtering attributes", e);
-//            throw new ServletException("Error filtering attributes");
-//        }
-//
-//        // construct response
-//        Response samlResponse = null;
-//        try {
-//            samlResponse = buildResponse(message, request.getRemoteHost(), new DateTime(), statement);
-//        } catch (EncryptionException e) {
-//            log.error("Error encrypting SAML response", e);
-//            throw new ServletException("Error encrypting SAML response");
-//        }
-//        if (log.isDebugEnabled()) {
-//            log.debug("built saml1 response: " + samlResponse);
-//        }
-//
-//        // encode response
-//        try {
-//            encodeResponse(samlResponse);
-//        } catch (BindingException e) {
-//            log.error("Error encoding attribute query response", e);
-//            throw new ServletException("Error encoding attribute query response");
-//        }
-//
-//        return true;
-//    }
-//
-//    /**
-//     * This builds the response for this SAML request.
-//     * 
-//     * @param message <code>AttributeQuery</code>
-//     * @param dest <code>String</code>
-//     * @param issueInstant <code>DateTime</code>
-//     * @param statement <code>AttributeStatement</code>
-//     * @return <code>Response</code>
-//     * @throws EncryptionException if an error occurs attempting to encrypt data
-//     */
-//    private Response buildResponse(org.opensaml.saml1.core.AttributeQuery message, String dest, DateTime issueInstant,
-//            AttributeStatement statement) throws EncryptionException {
-//        SAMLObjectBuilder<Response> responseBuilder = (SAMLObjectBuilder<Response>) getBuilderFactory().getBuilder(
-//                Response.DEFAULT_ELEMENT_NAME);
-//        /*
-//         * required: samlp:Status, ID, Version, IssueInstant
-//         */
-//        Response response = responseBuilder.buildObject();
-//        response.setVersion(SAML_VERSION);
-//        response.setID(getIdGenerator().generateIdentifier());
-//        response.setInResponseTo(getDecoder().getSecurityPolicy().getIssuer().toString());
-//        response.setIssueInstant(issueInstant);
-//        response.setRecipient(dest);
-//
-//        response.setStatus(buildStatus());
-//        response.getAssertions().add(buildAssertion(message.getSubject(), issueInstant, statement));
-//        return response;
-//    }
-//
-//    /**
-//     * This builds the status response for this SAML request.
-//     * 
-//     * @return <code>Status</code>
-//     */
-//    private Status buildStatus() {
-//        // build status
-//        SAMLObjectBuilder<Status> statusBuilder = (SAMLObjectBuilder<Status>) getBuilderFactory().getBuilder(
-//                Status.DEFAULT_ELEMENT_NAME);
-//        Status status = statusBuilder.buildObject();
-//
-//        // build status code
-//        SAMLObjectBuilder<StatusCode> statusCodeBuilder = (SAMLObjectBuilder<StatusCode>) getBuilderFactory()
-//                .getBuilder(StatusCode.DEFAULT_ELEMENT_NAME);
-//        StatusCode statusCode = statusCodeBuilder.buildObject();
-//        statusCode.setValue("Success");
-//        status.setStatusCode(statusCode);
-//        return status;
-//    }
-//
-//    /**
-//     * This builds the assertion for this SAML request.
-//     * 
-//     * @param messageSubject <code>Subject</code>
-//     * @param issueInstant <code>DateTime</code>
-//     * @param statement <code>AttributeStatement</code> of attributes
-//     * @return <code>Assertion</code>
-//     * @throws EncryptionException if an error occurs attempting to encrypt data
-//     */
-//    private Assertion buildAssertion(Subject messageSubject, DateTime issueInstant, AttributeStatement statement)
-//            throws EncryptionException {
-//        // build assertion
-//        SAMLObjectBuilder<Assertion> assertionBuilder = (SAMLObjectBuilder<Assertion>) getBuilderFactory().getBuilder(
-//                Assertion.DEFAULT_ELEMENT_NAME);
-//        /*
-//         * required: saml:Issuer, ID, Version, IssueInstant
-//         */
-//        Assertion assertion = assertionBuilder.buildObject();
-//        assertion.setID(getIdGenerator().generateIdentifier());
-//        assertion.setIssueInstant(issueInstant);
-//        assertion.setVersion(SAML_VERSION);
-//        assertion.setIssuer(getRelyingPartyConfiguration().getProviderID());
-//
-//        // build subject
-//        assertion.getSubjectStatements().add(buildSubjectStatement(messageSubject));
-//        // build conditions
-//        assertion.setConditions(buildConditions(issueInstant));
-//        // build advice
-//        assertion.setAdvice(buildAdvice());
-//        // add attribute statement
-//        assertion.getAttributeStatements().add(statement);
-//        return assertion;
-//    }
-//
-//    /**
-//     * This builds the subject statement for this SAML request.
-//     * 
-//     * @param messageSubject <code>Subject</code>
-//     * @return <code>SubjectStatement</code>
-//     */
-//    private SubjectStatement buildSubjectStatement(Subject messageSubject) {
-//        // build subject
-//        SAMLObjectBuilder<SubjectStatement> subjectStatementBuilder = (SAMLObjectBuilder<SubjectStatement>) getBuilderFactory()
-//                .getBuilder(SubjectStatement.DEFAULT_ELEMENT_NAME);
-//        SubjectStatement subjectStatement = subjectStatementBuilder.buildObject();
-//        subjectStatement.setSubject(messageSubject);
-//        return subjectStatement;
-//    }
-//
-//    /**
-//     * This builds the conditions for this SAML request.
-//     * 
-//     * @param issueInstant <code>DateTime</code>
-//     * @return <code>Conditions</code>
-//     */
-//    private Conditions buildConditions(DateTime issueInstant) {
-//        SAMLObjectBuilder<Conditions> conditionsBuilder = (SAMLObjectBuilder<Conditions>) getBuilderFactory()
-//                .getBuilder(Conditions.DEFAULT_ELEMENT_NAME);
-//        Conditions conditions = conditionsBuilder.buildObject();
-//        conditions.setNotBefore(issueInstant);
-//        // TODO conditions.setNotOnOrAfter();
-//        // TODO add additional conditions : conditions.getConditions().add(Condition);
-//        // TODO what about AudienceRestriction, OneTimeUse, ProxyRestriction?
-//        return conditions;
-//    }
-//
-//    /**
-//     * This builds the advice for this SAML request.
-//     * 
-//     * @return <code>Advice</code>
-//     */
-//    private Advice buildAdvice() {
-//        SAMLObjectBuilder<Advice> adviceBuilder = (SAMLObjectBuilder<Advice>) getBuilderFactory().getBuilder(
-//                Advice.DEFAULT_ELEMENT_NAME);
-//        Advice advice = adviceBuilder.buildObject();
-//        // advice.getAssertionIDReferences().add();
-//        // advice.getAssertionURIReferences().add();
-//        // advice.getAssertions().add();
-//        // advice.getEncryptedAssertions().add();
-//        // advice.addNamespace(namespace);
-//        return advice;
-//    }
-}
\ No newline at end of file
diff --git a/src/edu/internet2/middleware/shibboleth/idp/profile/saml1/AttributeQueryProfileHandler.java b/src/edu/internet2/middleware/shibboleth/idp/profile/saml1/AttributeQueryProfileHandler.java
new file mode 100644 (file)
index 0000000..9626fe2
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed 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 edu.internet2.middleware.shibboleth.idp.profile.saml1;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.apache.log4j.Logger;
+import org.opensaml.common.binding.BindingException;
+import org.opensaml.common.binding.decoding.MessageDecoder;
+import org.opensaml.common.binding.encoding.MessageEncoder;
+import org.opensaml.common.binding.security.SAMLSecurityPolicy;
+import org.opensaml.saml1.binding.decoding.HTTPSOAP11Decoder;
+import org.opensaml.saml1.binding.encoding.HTTPSOAP11Encoder;
+import org.opensaml.saml1.core.AttributeQuery;
+import org.opensaml.saml1.core.AttributeStatement;
+import org.opensaml.saml1.core.NameIdentifier;
+import org.opensaml.saml1.core.Response;
+import org.opensaml.saml1.core.Statement;
+import org.opensaml.saml1.core.StatusCode;
+import org.opensaml.saml1.core.Subject;
+import org.opensaml.saml2.metadata.AttributeAuthorityDescriptor;
+import org.opensaml.saml2.metadata.SPSSODescriptor;
+import org.opensaml.ws.security.SecurityPolicyException;
+
+import edu.internet2.middleware.shibboleth.common.attribute.AttributeRequestException;
+import edu.internet2.middleware.shibboleth.common.attribute.BaseAttribute;
+import edu.internet2.middleware.shibboleth.common.attribute.provider.SAML1AttributeAuthority;
+import edu.internet2.middleware.shibboleth.common.attribute.provider.ShibbolethSAMLAttributeRequestContext;
+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.RelyingPartyConfiguration;
+import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml1.AttributeQueryConfiguration;
+import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
+import edu.internet2.middleware.shibboleth.idp.session.Session;
+
+/**
+ * SAML 1 Attribute Query profile handler.
+ */
+public class AttributeQueryProfileHandler extends AbstractSAML1ProfileHandler {
+
+    /** Class logger. */
+    private final Logger log = Logger.getLogger(AttributeQueryProfileHandler.class);
+
+    /** {@inheritDoc} */
+    public String getProfileId() {
+        return "urn:mace:shibboleth:2.0:idp:profiles:saml1:query:attribute";
+    }
+
+    /** {@inheritDoc} */
+    public void processRequest(ProfileRequest<ServletRequest> request, ProfileResponse<ServletResponse> response)
+            throws ProfileException {
+
+        AttributeQueryContext requestContext = new AttributeQueryContext(request, response);
+
+        Response samlResponse;
+        try {
+            decodeRequest(requestContext);
+
+            ArrayList<Statement> statements = new ArrayList<Statement>();
+            statements.add(buildAttributeStatement(requestContext));
+
+            samlResponse = buildResponse(requestContext, statements);
+        } catch (SecurityPolicyException e) {
+            samlResponse = buildErrorResponse(requestContext, StatusCode.REQUESTER, StatusCode.REQUEST_DENIED, e
+                    .getMessage());
+        } catch (AttributeRequestException e) {
+            samlResponse = buildErrorResponse(requestContext, StatusCode.RESPONDER, null, e.getMessage());
+        } catch (ProfileException e) {
+            samlResponse = buildErrorResponse(requestContext, StatusCode.RESPONDER, StatusCode.REQUEST_DENIED, e
+                    .getMessage());
+        }
+    }
+
+    /**
+     * Decodes the message in the request and adds it to the request context.
+     * 
+     * @param requestContext request context contianing the request to decode
+     * 
+     * @throws ProfileException throw if there is a problem decoding the request
+     * @throws SecurityPolicyException thrown if the message was decoded properly but did not meet the necessary
+     *             security policy requirements
+     */
+    protected void decodeRequest(AttributeQueryContext requestContext) throws ProfileException, SecurityPolicyException {
+        if (log.isDebugEnabled()) {
+            log.debug("Decoding incomming request");
+        }
+        MessageDecoder<ServletRequest> decoder = getMessageDecoderFactory().getMessageDecoder(
+                HTTPSOAP11Decoder.BINDING_URI);
+        if (decoder == null) {
+            throw new ProfileException("No request decoder was registered for binding type: "
+                    + HTTPSOAP11Decoder.BINDING_URI);
+        }
+        super.populateMessageDecoder(decoder);
+
+        decoder.setRequest(requestContext.getProfileRequest().getRawRequest());
+        requestContext.setMessageDecoder(decoder);
+
+        try {
+            decoder.decode();
+            if (log.isDebugEnabled()) {
+                log.debug("Decoded request");
+            }
+        } catch (BindingException e) {
+            log.error("Error decoding attribute query message", e);
+            throw new ProfileException("Error decoding attribute query message");
+        } finally {
+            // Set as much information as can be retrieved from the decoded message
+            SAMLSecurityPolicy securityPolicy = requestContext.getMessageDecoder().getSecurityPolicy();
+            requestContext.setRelyingPartyId(securityPolicy.getIssuer());
+
+            RelyingPartyConfiguration rpConfig = getRelyingPartyConfiguration(requestContext.getRelyingPartyId());
+            requestContext.setRelyingPartyConfiguration(rpConfig);
+
+            requestContext.setRelyingPartyRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
+
+            requestContext.setAssertingPartyId(requestContext.getRelyingPartyConfiguration().getProviderId());
+
+            requestContext.setAssertingPartyRole(AttributeAuthorityDescriptor.DEFAULT_ELEMENT_NAME);
+
+            requestContext.setProfileConfiguration((AttributeQueryConfiguration) rpConfig
+                    .getProfileConfiguration(AttributeQueryConfiguration.PROFILE_ID));
+
+            requestContext.setSamlRequest((AttributeQuery) requestContext.getMessageDecoder().getSAMLMessage());
+        }
+    }
+
+    /**
+     * 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
+     * @throws AttributeRequestException thrown if there is a problem resolving attributes
+     */
+    protected AttributeStatement buildAttributeStatement(AttributeQueryContext requestContext) throws ProfileException,
+            AttributeRequestException {
+
+        if (log.isDebugEnabled()) {
+            log.debug("Creating attribute statement in response to SAML request  from relying party "
+                    + requestContext.getRelyingPartyId());
+        }
+
+        try {
+            AttributeQueryConfiguration profileConfiguration = requestContext.getProfileConfiguration();
+            if (profileConfiguration == null) {
+                log.error("No SAML 1 attribute query profile configuration is defined for relying party: "
+                        + requestContext.getRelyingPartyId());
+                throw new AttributeRequestException("SAML 1 attribute query is not configured for this relying party");
+            }
+
+            SAML1AttributeAuthority attributeAuthority = profileConfiguration.getAttributeAuthority();
+
+            ShibbolethSAMLAttributeRequestContext<NameIdentifier, AttributeQuery> attributeRequestContext = buildAttributeRequestContext(requestContext);
+
+            if (log.isDebugEnabled()) {
+                log.debug("Resolving principal name for subject of SAML request from relying party "
+                        + requestContext.getRelyingPartyId());
+            }
+            String principal = attributeAuthority.getPrincipal(attributeRequestContext);
+            requestContext.setPrincipalName(principal);
+
+            if (log.isDebugEnabled()) {
+                log.debug("Resolving attributes for principal " + principal + " of SAML request from relying party "
+                        + requestContext.getRelyingPartyId());
+            }
+            Map<String, BaseAttribute> principalAttributes = attributeAuthority
+                    .getAttributes(buildAttributeRequestContext(requestContext));
+
+            requestContext.setPrincipalAttributes(principalAttributes);
+
+            AttributeStatement statment = attributeAuthority.buildAttributeStatement(requestContext.getSamlRequest(),
+                    principalAttributes.values());
+
+            Subject statementSubject = buildSubject(requestContext, "urn:oasis:names:tc:SAML:1.0:cm:sender-vouches");
+            statment.setSubject(statementSubject);
+
+            return statment;
+        } catch (AttributeRequestException e) {
+            log.error("Error resolving attributes for SAML request from relying party "
+                    + requestContext.getRelyingPartyId(), e);
+            throw e;
+        }
+    }
+
+    /**
+     * Creates an attribute query context from the current profile request context.
+     * 
+     * @param requestContext current profile request
+     * 
+     * @return created query context
+     */
+    protected ShibbolethSAMLAttributeRequestContext<NameIdentifier, AttributeQuery> buildAttributeRequestContext(
+            AttributeQueryContext requestContext) {
+
+        ShibbolethSAMLAttributeRequestContext<NameIdentifier, AttributeQuery> queryContext = new ShibbolethSAMLAttributeRequestContext<NameIdentifier, AttributeQuery>(
+                getMetadataProvider(), requestContext.getRelyingPartyConfiguration(), requestContext.getSamlRequest());
+
+        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.getServiceInformation(requestContext.getRelyingPartyId());
+            if (serviceInfo != null) {
+                String principalAuthenticationMethod = serviceInfo.getAuthenticationMethod().getAuthenticationMethod();
+
+                requestContext.setPrincipalAuthenticationMethod(principalAuthenticationMethod);
+                queryContext.setPrincipalAuthenticationMethod(principalAuthenticationMethod);
+            }
+        }
+
+        return queryContext;
+    }
+
+    /**
+     * Encodes the request's SAML response and writes it to the servlet response.
+     * 
+     * @param requestContext current request context
+     * 
+     * @throws ProfileException thrown if no message encoder is registered for this profiles binding
+     */
+    protected void encodeResponse(AttributeQueryContext requestContext) throws ProfileException {
+        if (log.isDebugEnabled()) {
+            log.debug("Encoding response to SAML request from relying party " + requestContext.getRelyingPartyId());
+        }
+        MessageEncoder<ServletResponse> encoder = getMessageEncoderFactory().getMessageEncoder(
+                HTTPSOAP11Encoder.BINDING_URI);
+        if (encoder == null) {
+            throw new ProfileException("No response encoder was registered for binding type: "
+                    + HTTPSOAP11Encoder.BINDING_URI);
+        }
+
+        super.populateMessageEncoder(encoder);
+        encoder.setResponse(requestContext.getProfileResponse().getRawResponse());
+        encoder.setSamlMessage(requestContext.getSamlResponse());
+        requestContext.setMessageEncoder(encoder);
+
+        try {
+            encoder.encode();
+        } catch (BindingException e) {
+            throw new ProfileException("Unable to encode response to relying party: "
+                    + requestContext.getRelyingPartyId(), e);
+        }
+    }
+
+    /** Basic data structure used to accumulate information as a request is being processed. */
+    protected class AttributeQueryContext extends
+            SAML1ProfileRequestContext<AttributeQuery, Response, AttributeQueryConfiguration> {
+
+        /**
+         * Constructor.
+         * 
+         * @param request current profile request
+         * @param response current profile response
+         */
+        public AttributeQueryContext(ProfileRequest<ServletRequest> request, ProfileResponse<ServletResponse> response) {
+            super(request, response);
+        }
+    }
+}
\ No newline at end of file
index de46cd2..031cfef 100644 (file)
@@ -32,7 +32,6 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
-
 import org.apache.log4j.Logger;
 import org.joda.time.DateTime;
 import org.opensaml.Configuration;
@@ -85,781 +84,732 @@ import edu.internet2.middleware.shibboleth.idp.authn.Saml2LoginContext;
  * Abstract SAML 2.0 Authentication Request profile handler.
  */
 public abstract class AbstractAuthenticationRequest extends AbstractSAML2ProfileHandler {
-    
-    
+
     /**
      * Represents the internal state of a SAML 2.0 Authentiation Request while it's being processed by the IdP.
      */
     protected class AuthenticationRequestContext {
-        
+
         /** The ProfileRequest. */
         protected ProfileRequest<ServletRequest> profileRequest;
-        
+
         /** The ProfileResponse. */
         protected ProfileResponse<ServletResponse> profileResponse;
-        
+
         /** The HttpServletRequest. */
         protected HttpServletRequest servletRequest;
-        
+
         /** The HttpServletResponse. */
         protected HttpServletResponse servletResponse;
-        
+
         /** The SAML 2.0 AuthnRequest. */
         protected AuthnRequest authnRequest;
-        
+
         /** The issuer. */
         protected String issuer;
-        
+
         /** The Subject. */
         protected Subject subject;
-        
+
         /** The Response. */
         protected Response response;
-        
+
         /** The IdP's LoginContext. */
         protected LoginContext loginContext;
-        
+
         /** The RelyingPartyConfiguration. */
         protected RelyingPartyConfiguration rpConfig;
-        
+
         /** The SSOConfiguration. */
         protected SSOConfiguration ssoConfig;
-        
+
         /** The SPSSOConfiguration. */
         protected SPSSODescriptor spDescriptor;
-        
+
         /** The AssertionConsumerService endpoint. */
         protected AssertionConsumerService assertionConsumerService;
-        
+
         public AuthenticationRequestContext() {
         }
-        
+
         public ProfileRequest<ServletRequest> getProfileRequest() {
             return profileRequest;
         }
-        
+
         public void setProfileRequest(ProfileRequest<ServletRequest> profileRequest) {
             this.profileRequest = profileRequest;
             this.servletRequest = (HttpServletRequest) profileRequest.getRawRequest();
         }
-        
+
         public ProfileResponse<ServletResponse> getProfileResponse() {
             return profileResponse;
         }
-        
+
         public void setProfileResponse(ProfileResponse<ServletResponse> profileResponse) {
             this.profileResponse = profileResponse;
             this.servletResponse = (HttpServletResponse) profileResponse.getRawResponse();
         }
-        
+
         public HttpServletRequest getServletRequest() {
             return servletRequest;
         }
-        
+
         public void setServletRequest(HttpServletRequest servletRequest) {
             this.servletRequest = servletRequest;
         }
-        
+
         public HttpServletResponse getServletResponse() {
             return servletResponse;
         }
-        
+
         public void setServletResponse(HttpServletResponse servletResponse) {
             this.servletResponse = servletResponse;
         }
-        
+
         public HttpSession getHttpSession() {
-            
+
             if (getServletRequest() != null) {
                 return getServletRequest().getSession();
             } else {
                 return null;
             }
         }
-        
+
         public AuthnRequest getAuthnRequest() {
             return authnRequest;
         }
-        
+
         public void setAuthnRequest(AuthnRequest authnRequest) {
             this.authnRequest = authnRequest;
         }
-        
+
         public String getIssuer() {
             return issuer;
         }
-        
+
         public void setIssuer(String issuer) {
             this.issuer = issuer;
         }
-        
+
         public Subject getSubject() {
             return subject;
         }
-        
+
         public void setSubject(Subject subject) {
             this.subject = subject;
         }
-        
+
         public LoginContext getLoginContext() {
             return loginContext;
         }
-        
+
         public void setLoginContext(LoginContext loginContext) {
             this.loginContext = loginContext;
         }
-        
+
         public RelyingPartyConfiguration getRpConfig() {
             return rpConfig;
         }
-        
+
         public void setRpConfig(RelyingPartyConfiguration rpConfig) {
             this.rpConfig = rpConfig;
         }
-        
+
         public SSOConfiguration getSsoConfig() {
             return ssoConfig;
         }
-        
+
         public void setSsoConfig(SSOConfiguration ssoConfig) {
             this.ssoConfig = ssoConfig;
         }
-        
+
         public SPSSODescriptor getSpDescriptor() {
             return spDescriptor;
         }
-        
+
         public void setSpDescriptor(SPSSODescriptor spDescriptor) {
             this.spDescriptor = spDescriptor;
         }
-        
+
         public Response getResponse() {
             return response;
         }
-        
+
         public void setResponse(Response response) {
             this.response = response;
         }
-        
+
         public AssertionConsumerService getAssertionConsumerService() {
             return assertionConsumerService;
         }
-        
+
         public void setAssertionConsumerService(AssertionConsumerService assertionConsumerService) {
             this.assertionConsumerService = assertionConsumerService;
         }
-        
+
         public boolean equals(Object obj) {
             if (obj == null) {
                 return false;
             }
-            
+
             if (getClass() != obj.getClass()) {
                 return false;
             }
-            
+
             final edu.internet2.middleware.shibboleth.idp.profile.saml2.AbstractAuthenticationRequest.AuthenticationRequestContext other = (edu.internet2.middleware.shibboleth.idp.profile.saml2.AbstractAuthenticationRequest.AuthenticationRequestContext) obj;
-            
-            if (this.profileRequest != other.profileRequest && (this.profileRequest == null || !this.profileRequest.equals(other.profileRequest))) {
+
+            if (this.profileRequest != other.profileRequest
+                    && (this.profileRequest == null || !this.profileRequest.equals(other.profileRequest))) {
                 return false;
             }
-            
-            if (this.profileResponse != other.profileResponse && (this.profileResponse == null || !this.profileResponse.equals(other.profileResponse))) {
+
+            if (this.profileResponse != other.profileResponse
+                    && (this.profileResponse == null || !this.profileResponse.equals(other.profileResponse))) {
                 return false;
             }
-            
-            if (this.servletRequest != other.servletRequest && (this.servletRequest == null || !this.servletRequest.equals(other.servletRequest))) {
+
+            if (this.servletRequest != other.servletRequest
+                    && (this.servletRequest == null || !this.servletRequest.equals(other.servletRequest))) {
                 return false;
             }
-            
-            if (this.servletResponse != other.servletResponse && (this.servletResponse == null || !this.servletResponse.equals(other.servletResponse))) {
+
+            if (this.servletResponse != other.servletResponse
+                    && (this.servletResponse == null || !this.servletResponse.equals(other.servletResponse))) {
                 return false;
             }
-            
-            if (this.authnRequest != other.authnRequest && (this.authnRequest == null || !this.authnRequest.equals(other.authnRequest))) {
+
+            if (this.authnRequest != other.authnRequest
+                    && (this.authnRequest == null || !this.authnRequest.equals(other.authnRequest))) {
                 return false;
             }
-            
+
             if (this.issuer != other.issuer && (this.issuer == null || !this.issuer.equals(other.issuer))) {
                 return false;
             }
-            
+
             if (this.subject != other.subject && (this.subject == null || !this.subject.equals(other.subject))) {
                 return false;
             }
-            
+
             if (this.response != other.response && (this.response == null || !this.response.equals(other.response))) {
                 return false;
             }
-            
-            if (this.loginContext != other.loginContext && (this.loginContext == null || !this.loginContext.equals(other.loginContext))) {
+
+            if (this.loginContext != other.loginContext
+                    && (this.loginContext == null || !this.loginContext.equals(other.loginContext))) {
                 return false;
             }
-            
+
             if (this.rpConfig != other.rpConfig && (this.rpConfig == null || !this.rpConfig.equals(other.rpConfig))) {
                 return false;
             }
-            
-            if (this.ssoConfig != other.ssoConfig && (this.ssoConfig == null || !this.ssoConfig.equals(other.ssoConfig))) {
+
+            if (this.ssoConfig != other.ssoConfig
+                    && (this.ssoConfig == null || !this.ssoConfig.equals(other.ssoConfig))) {
                 return false;
             }
-            
-            if (this.spDescriptor != other.spDescriptor && (this.spDescriptor == null || !this.spDescriptor.equals(other.spDescriptor))) {
+
+            if (this.spDescriptor != other.spDescriptor
+                    && (this.spDescriptor == null || !this.spDescriptor.equals(other.spDescriptor))) {
                 return false;
             }
-            
-            if (this.assertionConsumerService != other.assertionConsumerService && (this.assertionConsumerService == null || !this.assertionConsumerService.equals(other.assertionConsumerService))) {
+
+            if (this.assertionConsumerService != other.assertionConsumerService
+                    && (this.assertionConsumerService == null || !this.assertionConsumerService
+                            .equals(other.assertionConsumerService))) {
                 return false;
             }
-            
+
             return true;
         }
-        
+
         public int hashCode() {
             int hash = 7;
             return hash;
         }
-        
-        
+
     }
-    
-    
+
     /** Class logger. */
     private static final Logger log = Logger.getLogger(AbstractAuthenticationRequest.class);
-    
+
     /** HttpSession key for the AuthenticationRequestContext. */
     protected static final String REQUEST_CONTEXT_SESSION_KEY = "edu.internet2.middleware.shibboleth.idp.profile.saml2.AuthenticationRequestContext";
-    
+
     /** The path to the IdP's AuthenticationManager servlet */
     protected String authnMgrURL;
-    
+
     /** AuthenticationManager to be used */
     protected AuthenticationManager authnMgr;
-    
+
     /** A pool of XML parsers. */
     protected ParserPool parserPool;
-    
+
     /** Builder for AuthnStatements. */
     protected SAMLObjectBuilder<AuthnStatement> authnStatementBuilder;
-    
+
     /** Builder for AuthnContexts. */
     protected SAMLObjectBuilder<AuthnContext> authnContextBuilder;
-    
+
     /** Builder for AuthnContextDeclRef's */
     protected SAMLObjectBuilder<AuthnContextDeclRef> authnContextDeclRefBuilder;
-    
+
     /** Builder for AuthnContextClassRef's. */
     protected SAMLObjectBuilder<AuthnContextClassRef> authnContextClassRefBuilder;
-    
+
     /**
      * Constructor.
      */
     public AbstractAuthenticationRequest() {
-        
+
         parserPool = new BasicParserPool();
-        authnStatementBuilder = (SAMLObjectBuilder<AuthnStatement>) getBuilderFactory().getBuilder(AuthnStatement.DEFAULT_ELEMENT_NAME);
-        authnContextBuilder = (SAMLObjectBuilder<AuthnContext>) getBuilderFactory().getBuilder(AuthnContext.DEFAULT_ELEMENT_NAME);
-        authnContextDeclRefBuilder = (SAMLObjectBuilder<AuthnContextDeclRef>) getBuilderFactory().getBuilder(AuthnContextDeclRef.DEFAULT_ELEMENT_NAME);
-        authnContextClassRefBuilder = (SAMLObjectBuilder<AuthnContextClassRef>) getBuilderFactory().getBuilder(AuthnContextClassRef.DEFAULT_ELEMENT_NAME);
+        authnStatementBuilder = (SAMLObjectBuilder<AuthnStatement>) getBuilderFactory().getBuilder(
+                AuthnStatement.DEFAULT_ELEMENT_NAME);
+        authnContextBuilder = (SAMLObjectBuilder<AuthnContext>) getBuilderFactory().getBuilder(
+                AuthnContext.DEFAULT_ELEMENT_NAME);
+        authnContextDeclRefBuilder = (SAMLObjectBuilder<AuthnContextDeclRef>) getBuilderFactory().getBuilder(
+                AuthnContextDeclRef.DEFAULT_ELEMENT_NAME);
+        authnContextClassRefBuilder = (SAMLObjectBuilder<AuthnContextClassRef>) getBuilderFactory().getBuilder(
+                AuthnContextClassRef.DEFAULT_ELEMENT_NAME);
     }
-    
+
     /**
      * Set the Authentication Mananger.
-     *
-     * @param authnManager
-     *            The IdP's AuthenticationManager.
+     * 
+     * @param authnManager The IdP's AuthenticationManager.
      */
     public void setAuthenticationManager(AuthenticationManager authnManager) {
         this.authnMgr = authnMgr;
     }
-    
+
     /**
      * Evaluate a SAML 2 AuthenticationRequest message.
-     *
-     * @param authnRequest
-     *            A SAML 2 AuthenticationRequest
-     * @param issuer
-     *            The issuer of the authnRequest.
-     * @param session
-     *            The HttpSession of the request.
-     * @param relyingParty
-     *            The RelyingPartyConfiguration for the request.
-     * @param ssoConfig
-     *            The SSOConfiguration for the request.
-     * @param spDescriptor
-     *            The SPSSODescriptor for the request.
-     *
-     * @throws ProfileException
-     *             On Error.
+     * 
+     * @param authnRequest A SAML 2 AuthenticationRequest
+     * @param issuer The issuer of the authnRequest.
+     * @param session The HttpSession of the request.
+     * @param relyingParty The RelyingPartyConfiguration for the request.
+     * @param ssoConfig The SSOConfiguration for the request.
+     * @param spDescriptor The SPSSODescriptor for the request.
+     * 
+     * @throws ProfileException On Error.
      */
     protected void evaluateRequest(final AuthenticationRequestContext requestContext) throws ProfileException {
-        
+
         Response samlResponse;
-        
+
         final AuthnRequest authnRequest = requestContext.getAuthnRequest();
         String issuer = requestContext.getIssuer();
         final HttpSession session = requestContext.getHttpSession();
         final RelyingPartyConfiguration relyingParty = requestContext.getRpConfig();
         final SSOConfiguration ssoConfig = requestContext.getSsoConfig();
         final SPSSODescriptor spDescriptor = requestContext.getSpDescriptor();
-        
+
         LoginContext loginCtx = requestContext.getLoginContext();
         if (loginCtx.getAuthenticationOK()) {
-            
+
             // the user successfully authenticated.
             // build an authentication assertion.
-            samlResponse = buildResponse(authnRequest.getID(), new DateTime(),
-                    relyingParty.getProviderId(), buildStatus(StatusCode.SUCCESS_URI, null, null));
-            
+            samlResponse = buildResponse(authnRequest.getID(), new DateTime(), relyingParty.getProviderId(),
+                    buildStatus(StatusCode.SUCCESS_URI, null, null));
+
             DateTime now = new DateTime();
             Assertion assertion = buildAssertion(now, relyingParty, (AbstractSAML2ProfileConfiguration) ssoConfig);
             assertion.setSubject(requestContext.getSubject());
             setAuthenticationStatement(assertion, loginCtx, authnRequest);
             samlResponse.getAssertions().add(assertion);
-            
+
         } else {
-            
+
             // if authentication failed, encode the appropriate SAML error message.
             String failureMessage = loginCtx.getAuthenticationFailureMessage();
             Status failureStatus = buildStatus(StatusCode.RESPONDER_URI, StatusCode.AUTHN_FAILED_URI, failureMessage);
             samlResponse = buildResponse(authnRequest.getID(), new DateTime(), relyingParty.getProviderId(),
                     failureStatus);
         }
-        
+
         requestContext.setResponse(samlResponse);
     }
-    
+
     /**
      * Build a SAML 2 Response element with basic fields populated.
-     *
-     * Failure handlers can send the returned response element to the RP.
-     * Success handlers should add the assertions before sending it.
-     *
-     * @param inResponseTo
-     *            The ID of the request this is in response to.
-     * @param issueInstant
-     *            The timestamp of this response.
-     * @param issuer
-     *            The URI of the RP issuing the response.
-     * @param status
-     *            The response's status code.
-     *
+     * 
+     * Failure handlers can send the returned response element to the RP. Success handlers should add the assertions
+     * before sending it.
+     * 
+     * @param inResponseTo The ID of the request this is in response to.
+     * @param issueInstant The timestamp of this response.
+     * @param issuer The URI of the RP issuing the response.
+     * @param status The response's status code.
+     * 
      * @return The populated Response object.
      */
-    protected Response buildResponse(String inResponseTo,
-            final DateTime issueInstant, String issuer, final Status status) {
-        
+    protected Response buildResponse(String inResponseTo, final DateTime issueInstant, String issuer,
+            final Status status) {
+
         Response response = getResponseBuilder().buildObject();
-        
+
         Issuer i = getIssuerBuilder().buildObject();
         i.setValue(issuer);
-        
+
         response.setVersion(SAML_VERSION);
         response.setID(getIdGenerator().generateIdentifier());
         response.setInResponseTo(inResponseTo);
         response.setIssueInstant(issueInstant);
         response.setIssuer(i);
         response.setStatus(status);
-        
+
         return response;
     }
-    
+
     /**
      * Check if the user has already been authenticated.
-     *
-     * @param httpSession
-     *            the user's HttpSession.
-     *
-     * @return <code>true</code> if the user has been authenticated. otherwise
-     *         <code>false</code>
+     * 
+     * @param httpSession the user's HttpSession.
+     * 
+     * @return <code>true</code> if the user has been authenticated. otherwise <code>false</code>
      */
     protected boolean hasUserAuthenticated(final HttpSession httpSession) {
-        
+
         // if the user has authenticated, their session will have a LoginContext
-        
+
         Object o = httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
         return (o != null && o instanceof LoginContext);
     }
-    
+
     /**
-     * Check if the user has already been authenticated. If so, return the
-     * LoginContext. If not, redirect the user to the AuthenticationManager.
-     *
-     * @param authnRequest
-     *            The SAML 2 AuthnRequest.
-     * @param httpSession
-     *            The user's HttpSession.
-     * @param request
-     *            The user's HttpServletRequest.
-     * @param response
-     *            The user's HttpServletResponse.
-     *
-     * @throws ProfileException
-     *             on error.
+     * Check if the user has already been authenticated. If so, return the LoginContext. If not, redirect the user to
+     * the AuthenticationManager.
+     * 
+     * @param authnRequest The SAML 2 AuthnRequest.
+     * @param httpSession The user's HttpSession.
+     * @param request The user's HttpServletRequest.
+     * @param response The user's HttpServletResponse.
+     * 
+     * @throws ProfileException on error.
      */
     protected void authenticateUser(final AuthenticationRequestContext requestContext) throws ProfileException {
-        
+
         AuthnRequest authnRequest = requestContext.getAuthnRequest();
         HttpSession httpSession = requestContext.getHttpSession();
         HttpServletRequest servletRequest = requestContext.getServletRequest();
         HttpServletResponse servletResponse = requestContext.getServletResponse();
-        
+
         // Forward the request to the AuthenticationManager.
         // When the AuthenticationManager is done it will
         // forward the request back to this servlet.
-        
+
         // push the AuthenticationRequestContext into the session so we have it
         // for the return leg.
         httpSession.setAttribute(REQUEST_CONTEXT_SESSION_KEY, requestContext);
-        
+
         Saml2LoginContext loginCtx = new Saml2LoginContext(authnRequest);
         requestContext.setLoginContext(loginCtx);
         loginCtx.setProfileHandlerURL(servletRequest.getRequestURI());
-        
+
         // the AuthenticationManager expects the LoginContext to be in the HttpSession.
         httpSession.setAttribute(LoginContext.LOGIN_CONTEXT_KEY, loginCtx);
         try {
-            RequestDispatcher dispatcher = servletRequest
-                    .getRequestDispatcher(authnMgrURL);
-            dispatcher.forward(servletRequest,servletResponse);
+            RequestDispatcher dispatcher = servletRequest.getRequestDispatcher(authnMgrURL);
+            dispatcher.forward(servletRequest, servletResponse);
         } catch (IOException ex) {
-            log.error("Error forwarding SAML 2 AuthnRequest "
-                    + authnRequest.getID() + " to AuthenticationManager", ex);
-            throw new ProfileException("Error forwarding SAML 2 AuthnRequest "
-                    + authnRequest.getID() + " to AuthenticationManager", ex);
+            log.error("Error forwarding SAML 2 AuthnRequest " + authnRequest.getID() + " to AuthenticationManager", ex);
+            throw new ProfileException("Error forwarding SAML 2 AuthnRequest " + authnRequest.getID()
+                    + " to AuthenticationManager", ex);
         } catch (ServletException ex) {
-            log.error("Error forwarding SAML 2 AuthnRequest "
-                    + authnRequest.getID() + " to AuthenticationManager", ex);
-            throw new ProfileException("Error forwarding SAML 2 AuthnRequest "
-                    + authnRequest.getID() + " to AuthenticationManager", ex);
+            log.error("Error forwarding SAML 2 AuthnRequest " + authnRequest.getID() + " to AuthenticationManager", ex);
+            throw new ProfileException("Error forwarding SAML 2 AuthnRequest " + authnRequest.getID()
+                    + " to AuthenticationManager", ex);
         }
     }
-    
+
     /**
      * Build an AuthnStatement and add it to an Assertion.
-     *
+     * 
      * @param assertion An empty SAML 2 Assertion object.
      * @param loginContext The processed login context for the AuthnRequest.
      * @param authnRequest The AuthnRequest to which this is in response.
-     *
+     * 
      * @throws ProfileException On error.
      */
-    protected void setAuthenticationStatement(Assertion assertion,
-            final LoginContext loginContext,
+    protected void setAuthenticationStatement(Assertion assertion, final LoginContext loginContext,
             final AuthnRequest authnRequest) throws ProfileException {
-        
+
         // Build the AuthnCtx.
         // We need to determine if the user was authenticated
         // with an AuthnContextClassRef or a AuthnContextDeclRef
         AuthnContext authnCtx = buildAuthnCtx(authnRequest.getRequestedAuthnContext(), loginContext);
         if (authnCtx == null) {
-            log.error("Error respond to SAML 2 AuthnRequest "
-                    + authnRequest.getID()
+            log.error("Error respond to SAML 2 AuthnRequest " + authnRequest.getID()
                     + " : Unable to determine authentication method");
         }
-        
+
         AuthnStatement stmt = authnStatementBuilder.buildObject();
         stmt.setAuthnInstant(loginContext.getAuthenticationInstant());
         stmt.setAuthnContext(authnCtx);
-        
+
         // add the AuthnStatement to the Assertion
         List<AuthnStatement> authnStatements = assertion.getAuthnStatements();
         authnStatements.add(stmt);
     }
-    
+
     /**
      * Create the AuthnContex object.
-     *
-     * To do this, we have to walk the AuthnRequest's RequestedAuthnContext
-     * object and compare any values we find to what's set in the loginContext.
-     *
-     * @param requestedAuthnCtx
-     *            The RequestedAuthnContext from the Authentication Request.
-     * @param loginContext
-     *            The processed LoginContext (it must contain the authn method).
-     *
+     * 
+     * To do this, we have to walk the AuthnRequest's RequestedAuthnContext object and compare any values we find to
+     * what's set in the loginContext.
+     * 
+     * @param requestedAuthnCtx The RequestedAuthnContext from the Authentication Request.
+     * @param loginContext The processed LoginContext (it must contain the authn method).
+     * 
      * @return An AuthnCtx object on success or <code>null</code> on failure.
      */
-    protected AuthnContext buildAuthnCtx(
-            final RequestedAuthnContext requestedAuthnCtx,
-            final LoginContext loginContext) {
-        
+    protected AuthnContext buildAuthnCtx(final RequestedAuthnContext requestedAuthnCtx, final LoginContext loginContext) {
+
         // this method assumes that only one URI will match.
-        
+
         AuthnContext authnCtx = authnContextBuilder.buildObject();
         String authnMethod = loginContext.getAuthenticationMethod();
-        
-        List<AuthnContextClassRef> authnClasses = requestedAuthnCtx
-                .getAuthnContextClassRefs();
-        List<AuthnContextDeclRef> authnDeclRefs = requestedAuthnCtx
-                .getAuthnContextDeclRefs();
-        
+
+        List<AuthnContextClassRef> authnClasses = requestedAuthnCtx.getAuthnContextClassRefs();
+        List<AuthnContextDeclRef> authnDeclRefs = requestedAuthnCtx.getAuthnContextDeclRefs();
+
         if (authnClasses != null) {
             for (AuthnContextClassRef classRef : authnClasses) {
                 if (classRef != null) {
                     String s = classRef.getAuthnContextClassRef();
                     if (s != null && authnMethod.equals(s)) {
-                        AuthnContextClassRef ref = authnContextClassRefBuilder
-                                .buildObject();
+                        AuthnContextClassRef ref = authnContextClassRefBuilder.buildObject();
                         authnCtx.setAuthnContextClassRef(ref);
                         return authnCtx;
                     }
                 }
             }
         }
-        
+
         // if no AuthnContextClassRef's matched, try the DeclRefs
         if (authnDeclRefs != null) {
             for (AuthnContextDeclRef declRef : authnDeclRefs) {
                 if (declRef != null) {
                     String s = declRef.getAuthnContextDeclRef();
                     if (s != null && authnMethod.equals((s))) {
-                        AuthnContextDeclRef ref = authnContextDeclRefBuilder
-                                .buildObject();
+                        AuthnContextDeclRef ref = authnContextDeclRefBuilder.buildObject();
                         authnCtx.setAuthnContextDeclRef(ref);
                         return authnCtx;
                     }
                 }
             }
         }
-        
+
         // no matches were found.
         return null;
     }
-    
+
     /**
      * Verify the AuthnRequest is well-formed.
-     *
-     * @param authnRequest
-     *            The user's SAML 2 AuthnRequest.
-     * @param issuer
-     *            The Issuer of the AuthnRequest.
-     * @param relyingParty
-     *            The relying party configuration for the request's originator.
-     * @param session
-     *            The user's HttpSession.
-     *
-     * @throws AuthenticationRequestException
-     *             on error.
+     * 
+     * @param authnRequest The user's SAML 2 AuthnRequest.
+     * @param issuer The Issuer of the AuthnRequest.
+     * @param relyingParty The relying party configuration for the request's originator.
+     * @param session The user's HttpSession.
+     * 
+     * @throws AuthenticationRequestException on error.
      */
-    protected void verifyAuthnRequest(final AuthenticationRequestContext requestContext) throws AuthenticationRequestException {
-        
+    protected void verifyAuthnRequest(final AuthenticationRequestContext requestContext)
+            throws AuthenticationRequestException {
+
         final AuthnRequest authnRequest = requestContext.getAuthnRequest();
         String issuer = requestContext.getIssuer();
         final RelyingPartyConfiguration relyingParty = requestContext.getRpConfig();
         final HttpSession session = requestContext.getHttpSession();
-        
+
         Status failureStatus;
-        
+
         // Check if we are in scope to handle this AuthnRequest
         checkScope(authnRequest, issuer);
-        
+
         // verify that the AssertionConsumerService url is valid.
-        verifyAssertionConsumerService(requestContext,
-                getRelyingPartyConfigurationManager().getMetadataProvider());
-        
+        verifyAssertionConsumerService(requestContext, getRelyingPartyConfigurationManager().getMetadataProvider());
+
         // check for nameID constraints.
         verifySubject(requestContext);
     }
-    
+
     /**
      * Get and verify the Subject element.
-     *
+     * 
      * @param requestContext The context for the current request.
      * 
-     * @throws AuthenticationRequestException
-     *             on error.
+     * @throws AuthenticationRequestException on error.
      */
     protected void verifySubject(final AuthenticationRequestContext requestContext)
             throws AuthenticationRequestException {
-        
+
         final AuthnRequest authnRequest = requestContext.getAuthnRequest();
-        
+
         Status failureStatus;
-        
+
         Subject subject = authnRequest.getSubject();
-        
+
         if (subject == null) {
-            failureStatus = buildStatus(StatusCode.REQUESTER_URI, null,
-                    "SAML 2 AuthnRequest " + authnRequest.getID()
+            failureStatus = buildStatus(StatusCode.REQUESTER_URI, null, "SAML 2 AuthnRequest " + authnRequest.getID()
                     + " is malformed: It does not contain a Subject.");
-            throw new AuthenticationRequestException(
-                    "AuthnRequest lacks a Subject", failureStatus);
+            throw new AuthenticationRequestException("AuthnRequest lacks a Subject", failureStatus);
         }
-        
+
         // The Web Browser SSO profile disallows SubjectConfirmation
         // methods in the requested subject.
-        List<SubjectConfirmation> confMethods = subject
-                .getSubjectConfirmations();
+        List<SubjectConfirmation> confMethods = subject.getSubjectConfirmations();
         if (confMethods != null || confMethods.size() > 0) {
-            log
-                    .error("SAML 2 AuthnRequest "
-                    + authnRequest.getID()
+            log.error("SAML 2 AuthnRequest " + authnRequest.getID()
                     + " is malformed: It contains SubjectConfirmation elements.");
-            failureStatus = buildStatus(
-                    StatusCode.REQUESTER_URI,
-                    null,
-                    "SAML 2 AuthnRequest "
-                    + authnRequest.getID()
+            failureStatus = buildStatus(StatusCode.REQUESTER_URI, null, "SAML 2 AuthnRequest " + authnRequest.getID()
                     + " is malformed: It contains SubjectConfirmation elements.");
-            throw new AuthenticationRequestException(
-                    "AuthnRequest contains SubjectConfirmation elements",
+            throw new AuthenticationRequestException("AuthnRequest contains SubjectConfirmation elements",
                     failureStatus);
         }
-        
+
         requestContext.setSubject(subject);
-        
+
         return;
     }
-    
+
     /**
-     * Ensure that metadata can be found for the SP that issued
-     * the AuthnRequest.
-     *
+     * Ensure that metadata can be found for the SP that issued the AuthnRequest.
+     * 
      * If found, the request context is updated to reflect the appropriate entries.
-     *
+     * 
      * Before this method may be called, the request context must have an issuer set.
-     *
+     * 
      * @param requestContext The context for the current request.
-     *
+     * 
      * @throws AuthenticationRequestException On error.
      */
-    protected void validateRequestAgainstMetadata(final AuthenticationRequestContext requestContext) throws AuthenticationRequestException {
-        
+    protected void validateRequestAgainstMetadata(final AuthenticationRequestContext requestContext)
+            throws AuthenticationRequestException {
+
         RelyingPartyConfiguration relyingParty = null;
         SSOConfiguration ssoConfig = null;
         SPSSODescriptor spDescriptor = null;
-        
+
         // check that we have metadata for the RP
         relyingParty = getRelyingPartyConfigurationManager().getRelyingPartyConfiguration(requestContext.getIssuer());
-        
+
         ProfileConfiguration temp = relyingParty.getProfileConfigurations().get(SSOConfiguration.PROFILE_ID);
         if (temp == null) {
-            log.error("SAML 2 Authentication Request: No profile configuration registered for " + SSOConfiguration.PROFILE_ID);
-            throw new AuthenticationRequestException("No profile configuration registered for " + SSOConfiguration.PROFILE_ID);
+            log.error("SAML 2 Authentication Request: No profile configuration registered for "
+                    + SSOConfiguration.PROFILE_ID);
+            throw new AuthenticationRequestException("No profile configuration registered for "
+                    + SSOConfiguration.PROFILE_ID);
         }
-        
+
         ssoConfig = (SSOConfiguration) temp;
-        
+
         try {
-            spDescriptor = getMetadataProvider().getEntityDescriptor(
-                    relyingParty.getRelyingPartyId()).getSPSSODescriptor(
-                    SAML20_PROTOCOL_URI);
+            spDescriptor = getMetadataProvider().getEntityDescriptor(relyingParty.getRelyingPartyId())
+                    .getSPSSODescriptor(SAML20_PROTOCOL_URI);
         } catch (MetadataProviderException ex) {
-            log.error(
-                    "SAML 2 Authentication Request: Unable to locate SPSSODescriptor for SP "
+            log.error("SAML 2 Authentication Request: Unable to locate SPSSODescriptor for SP "
                     + requestContext.getIssuer() + " for protocol " + SAML20_PROTOCOL_URI, ex);
-            
-            Status failureStatus = buildStatus(StatusCode.REQUESTER_URI, null,
-                    "No metadata available for " + relyingParty.getRelyingPartyId());
-            
-            throw new AuthenticationRequestException("SAML 2 Authentication Request: Unable to locate SPSSODescriptor for SP "
-                    + requestContext.getIssuer() + " for protocol " + SAML20_PROTOCOL_URI, ex, failureStatus);
+
+            Status failureStatus = buildStatus(StatusCode.REQUESTER_URI, null, "No metadata available for "
+                    + relyingParty.getRelyingPartyId());
+
+            throw new AuthenticationRequestException(
+                    "SAML 2 Authentication Request: Unable to locate SPSSODescriptor for SP "
+                            + requestContext.getIssuer() + " for protocol " + SAML20_PROTOCOL_URI, ex, failureStatus);
         }
-        
+
         if (spDescriptor == null) {
             log.error("SAML 2 Authentication Request: Unable to locate SPSSODescriptor for SP "
                     + requestContext.getIssuer() + " for protocol " + SAML20_PROTOCOL_URI);
 
-            Status failureStatus = buildStatus(StatusCode.REQUESTER_URI, null,
-                    "No metadata available for " + relyingParty.getRelyingPartyId());
-            
-            throw new AuthenticationRequestException("SAML 2 Authentication Request: Unable to locate SPSSODescriptor for SP "
-                    + requestContext.getIssuer() + " for protocol " + SAML20_PROTOCOL_URI, failureStatus);
+            Status failureStatus = buildStatus(StatusCode.REQUESTER_URI, null, "No metadata available for "
+                    + relyingParty.getRelyingPartyId());
+
+            throw new AuthenticationRequestException(
+                    "SAML 2 Authentication Request: Unable to locate SPSSODescriptor for SP "
+                            + requestContext.getIssuer() + " for protocol " + SAML20_PROTOCOL_URI, failureStatus);
         }
-        
+
         // if all metadata was found, update the request context.
         requestContext.setRpConfig(relyingParty);
         requestContext.setSsoConfig(ssoConfig);
         requestContext.setSpDescriptor(spDescriptor);
     }
-    
+
     /**
      * Return the endpoint URL and protocol binding to use for the AuthnRequest.
-     *
+     * 
      * @param requestContext The context for the current request.
      * 
-     * @param metadata
-     *            The appropriate Metadata.
-     *
-     * @throws AuthenticationRequestException
-     *             On error.
+     * @param metadata The appropriate Metadata.
+     * 
+     * @throws AuthenticationRequestException On error.
      */
-    protected void verifyAssertionConsumerService(
-            final AuthenticationRequestContext requestContext,
-            
-            final MetadataProvider metadata)
-            throws AuthenticationRequestException {
-        
+    protected void verifyAssertionConsumerService(final AuthenticationRequestContext requestContext,
+
+    final MetadataProvider metadata) throws AuthenticationRequestException {
+
         Status failureStatus;
-        
+
         final AuthnRequest authnRequest = requestContext.getAuthnRequest();
         String providerId = requestContext.getRpConfig().getRelyingPartyId();
-        
+
         // Either the AssertionConsumerServiceIndex must be present
         // or AssertionConsumerServiceURL must be present.
-        
+
         Integer idx = authnRequest.getAssertionConsumerServiceIndex();
         String acsURL = authnRequest.getAssertionConsumerServiceURL();
-        
+
         if (idx != null && acsURL != null) {
             log
                     .error("SAML 2 AuthnRequest "
-                    + authnRequest.getID()
-                    + " is malformed: It contains both an AssertionConsumerServiceIndex and an AssertionConsumerServiceURL");
+                            + authnRequest.getID()
+                            + " is malformed: It contains both an AssertionConsumerServiceIndex and an AssertionConsumerServiceURL");
             failureStatus = buildStatus(
                     StatusCode.REQUESTER_URI,
                     null,
                     "SAML 2 AuthnRequest "
-                    + authnRequest.getID()
-                    + " is malformed: It contains both an AssertionConsumerServiceIndex and an AssertionConsumerServiceURL");
-            throw new AuthenticationRequestException("Malformed AuthnRequest",
-                    failureStatus);
+                            + authnRequest.getID()
+                            + " is malformed: It contains both an AssertionConsumerServiceIndex and an AssertionConsumerServiceURL");
+            throw new AuthenticationRequestException("Malformed AuthnRequest", failureStatus);
         }
-        
+
         SPSSODescriptor spDescriptor;
         try {
-            spDescriptor = metadata.getEntityDescriptor(providerId)
-                    .getSPSSODescriptor(SAML20_PROTOCOL_URI);
+            spDescriptor = metadata.getEntityDescriptor(providerId).getSPSSODescriptor(SAML20_PROTOCOL_URI);
         } catch (MetadataProviderException ex) {
-            log.error(
-                    "Unable retrieve SPSSODescriptor metadata for providerId "
-                    + providerId
-                    + " while processing SAML 2 AuthnRequest "
-                    + authnRequest.getID(), ex);
-            failureStatus = buildStatus(StatusCode.RESPONDER_URI, null,
-                    "Unable to locate metadata for " + providerId);
-            throw new AuthenticationRequestException(
-                    "Unable to locate metadata", ex, failureStatus);
+            log.error("Unable retrieve SPSSODescriptor metadata for providerId " + providerId
+                    + " while processing SAML 2 AuthnRequest " + authnRequest.getID(), ex);
+            failureStatus = buildStatus(StatusCode.RESPONDER_URI, null, "Unable to locate metadata for " + providerId);
+            throw new AuthenticationRequestException("Unable to locate metadata", ex, failureStatus);
         }
-        
-        List<AssertionConsumerService> acsList = spDescriptor
-                .getAssertionConsumerServices();
-        
+
+        List<AssertionConsumerService> acsList = spDescriptor.getAssertionConsumerServices();
+
         // if the ACS index is specified, retrieve it from the metadata
         if (idx != null) {
-            
+
             int i = idx.intValue();
-            
+
             // if the index is out of range, return an appropriate error.
             if (i > acsList.size()) {
-                log.error("Illegal AssertionConsumerIndex specicifed (" + i
-                        + ") in SAML 2 AuthnRequest " + authnRequest.getID());
-                
-                failureStatus = buildStatus(StatusCode.REQUESTER_URI, null,
-                        "Illegal AssertionConsumerIndex specicifed (" + i
-                        + ") in SAML 2 AuthnRequest "
+                log.error("Illegal AssertionConsumerIndex specicifed (" + i + ") in SAML 2 AuthnRequest "
                         + authnRequest.getID());
-                
-                throw new AuthenticationRequestException(
-                        "Illegal AssertionConsumerIndex in AuthnRequest",
+
+                failureStatus = buildStatus(StatusCode.REQUESTER_URI, null,
+                        "Illegal AssertionConsumerIndex specicifed (" + i + ") in SAML 2 AuthnRequest "
+                                + authnRequest.getID());
+
+                throw new AuthenticationRequestException("Illegal AssertionConsumerIndex in AuthnRequest",
                         failureStatus);
             }
-            
+
             requestContext.setAssertionConsumerService(acsList.get(i));
             return;
         }
-        
+
         // if the ACS endpoint is specified, validate it against the metadata
         String protocolBinding = authnRequest.getProtocolBinding();
         for (AssertionConsumerService acs : acsList) {
@@ -872,52 +822,45 @@ public abstract class AbstractAuthenticationRequest extends AbstractSAML2Profile
                 }
             }
         }
-        
-        log
-                .error("Error processing SAML 2 AuthnRequest message "
-                + authnRequest.getID()
-                + ": Unable to validate AssertionConsumerServiceURL against metadata: "
-                + acsURL + " for binding " + protocolBinding);
-        
+
+        log.error("Error processing SAML 2 AuthnRequest message " + authnRequest.getID()
+                + ": Unable to validate AssertionConsumerServiceURL against metadata: " + acsURL + " for binding "
+                + protocolBinding);
+
         failureStatus = buildStatus(StatusCode.REQUESTER_URI, null,
                 "Unable to validate AssertionConsumerService against metadata.");
-        
+
         throw new AuthenticationRequestException(
                 "SAML 2 AuthenticationRequest: Unable to validate AssertionConsumerService against Metadata",
                 failureStatus);
     }
-    
+
     /**
-     * Check if an {@link AuthnRequest} contains a {@link Scoping} element. If
-     * so, check if the specified IdP is in the {@link IDPList} element. If no
-     * Scoping element is present, this method returns <code>true</code>.
-     *
-     * @param authnRequest
-     *            The {@link AuthnRequest} element to check.
-     * @param providerId
-     *            The IdP's ProviderID.
-     *
-     * @throws AuthenticationRequestException
-     *             on error.
+     * Check if an {@link AuthnRequest} contains a {@link Scoping} element. If so, check if the specified IdP is in the
+     * {@link IDPList} element. If no Scoping element is present, this method returns <code>true</code>.
+     * 
+     * @param authnRequest The {@link AuthnRequest} element to check.
+     * @param providerId The IdP's ProviderID.
+     * 
+     * @throws AuthenticationRequestException on error.
      */
-    protected void checkScope(final AuthnRequest authnRequest, String providerId)
-            throws AuthenticationRequestException {
-        
+    protected void checkScope(final AuthnRequest authnRequest, String providerId) throws AuthenticationRequestException {
+
         Status failureStatus;
-        
+
         List<String> idpEntries = new LinkedList<String>();
-        
+
         Scoping scoping = authnRequest.getScoping();
         if (scoping == null) {
             return;
         }
-        
+
         // process all of the explicitly listed idp provider ids
         IDPList idpList = scoping.getIDPList();
         if (idpList == null) {
             return;
         }
-        
+
         List<IDPEntry> explicitIDPEntries = idpList.getIDPEntrys();
         if (explicitIDPEntries != null) {
             for (IDPEntry entry : explicitIDPEntries) {
@@ -927,7 +870,7 @@ public abstract class AbstractAuthenticationRequest extends AbstractSAML2Profile
                 }
             }
         }
-        
+
         // If the IDPList is incomplete, retrieve the complete list
         // and add the entries to idpEntries.
         GetComplete getComplete = idpList.getGetComplete();
@@ -943,7 +886,7 @@ public abstract class AbstractAuthenticationRequest extends AbstractSAML2Profile
                 }
             }
         }
-        
+
         // iterate over all the IDPEntries we've gathered,
         // and check if we're in scope.
         for (String requestProviderId : idpEntries) {
@@ -952,74 +895,60 @@ public abstract class AbstractAuthenticationRequest extends AbstractSAML2Profile
                 return;
             }
         }
-        
-        log.error("SAML 2 AuthnRequest " + authnRequest.getID()
-                + " contains a Scoping element which "
+
+        log.error("SAML 2 AuthnRequest " + authnRequest.getID() + " contains a Scoping element which "
                 + "does not contain a providerID registered with this IdP.");
-        
-        failureStatus = buildStatus(StatusCode.RESPONDER_URI,
-                StatusCode.NO_SUPPORTED_IDP_URI, null);
-        throw new AuthenticationRequestException(
-                "Unrecognized providerID in Scoping element", failureStatus);
+
+        failureStatus = buildStatus(StatusCode.RESPONDER_URI, StatusCode.NO_SUPPORTED_IDP_URI, null);
+        throw new AuthenticationRequestException("Unrecognized providerID in Scoping element", failureStatus);
     }
-    
+
     /**
      * Retrieve an incomplete IDPlist.
-     *
+     * 
      * This only handles URL-based <GetComplete/> references.
-     *
-     * @param getComplete
-     *            The (possibly <code>null</code>) &lt;GetComplete/&gt;
-     *            element
-     *
-     * @return an {@link IDPList} or <code>null</code> if the uri can't be
-     *         dereferenced.
+     * 
+     * @param getComplete The (possibly <code>null</code>) &lt;GetComplete/&gt; element
+     * 
+     * @return an {@link IDPList} or <code>null</code> if the uri can't be dereferenced.
      */
     protected IDPList getCompleteIDPList(final GetComplete getComplete) {
-        
+
         // XXX: enhance this method to cache the url and last-modified-header
-        
+
         if (getComplete == null) {
             return null;
         }
-        
+
         String uri = getComplete.getGetComplete();
         if (uri != null) {
             return null;
         }
-        
+
         IDPList idpList = null;
         InputStream istream = null;
-        
+
         try {
             URL url = new URL(uri);
             URLConnection conn = url.openConnection();
             istream = conn.getInputStream();
-            
+
             // convert the raw data into an XML object
             Document doc = parserPool.parse(istream);
             Element docElement = doc.getDocumentElement();
-            Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory()
-                    .getUnmarshaller(docElement);
+            Unmarshaller unmarshaller = Configuration.getUnmarshallerFactory().getUnmarshaller(docElement);
             idpList = (IDPList) unmarshaller.unmarshall(docElement);
-            
+
         } catch (MalformedURLException ex) {
-            log.error(
-                    "Unable to retrieve GetComplete IDPList. Unsupported URI: "
-                    + uri, ex);
+            log.error("Unable to retrieve GetComplete IDPList. Unsupported URI: " + uri, ex);
         } catch (IOException ex) {
-            log.error("IO Error while retreieving GetComplete IDPList from "
-                    + uri, ex);
+            log.error("IO Error while retreieving GetComplete IDPList from " + uri, ex);
         } catch (XMLParserException ex) {
-            log.error(
-                    "Internal OpenSAML error while parsing GetComplete IDPList from "
-                    + uri, ex);
+            log.error("Internal OpenSAML error while parsing GetComplete IDPList from " + uri, ex);
         } catch (UnmarshallingException ex) {
-            log.error(
-                    "Internal OpenSAML error while unmarshalling GetComplete IDPList from "
-                    + uri, ex);
+            log.error("Internal OpenSAML error while unmarshalling GetComplete IDPList from " + uri, ex);
         } finally {
-            
+
             if (istream != null) {
                 try {
                     istream.close();
@@ -1028,34 +957,33 @@ public abstract class AbstractAuthenticationRequest extends AbstractSAML2Profile
                 }
             }
         }
-        
+
         return idpList;
     }
-    
+
     /**
      * Encode a SAML Response.
-     *
+     * 
      * @param binding The SAML protocol binding to use.
      * @param requestContext The context for the request.
-     *
+     * 
      * @throws ProfileException On error.
      */
-    protected void encodeResponse(String binding,
-            final AuthenticationRequestContext requestContext) throws ProfileException {
-        
+    protected void encodeResponse(String binding, final AuthenticationRequestContext requestContext)
+            throws ProfileException {
+
         final ProfileResponse<ServletResponse> profileResponse = requestContext.getProfileResponse();
         final Response samlResponse = requestContext.getResponse();
         final RelyingPartyConfiguration relyingParty = requestContext.getRpConfig();
         final RoleDescriptor roleDescriptor = requestContext.getSpDescriptor();
         final Endpoint endpoint = requestContext.getAssertionConsumerService();
-        
-        
+
         MessageEncoder<ServletResponse> encoder = getMessageEncoderFactory().getMessageEncoder(binding);
         if (encoder == null) {
             log.error("No MessageEncoder registered for " + binding);
             throw new ProfileException("No MessageEncoder registered for " + binding);
         }
-        
+
         encoder.setResponse(profileResponse.getRawResponse());
         encoder.setIssuer(relyingParty.getProviderId());
         encoder.setMetadataProvider(getRelyingPartyConfigurationManager().getMetadataProvider());
@@ -1063,7 +991,7 @@ public abstract class AbstractAuthenticationRequest extends AbstractSAML2Profile
         encoder.setSigningCredential(relyingParty.getDefaultSigningCredential());
         encoder.setSamlMessage(samlResponse);
         encoder.setRelyingPartyEndpoint(endpoint);
-        
+
         try {
             encoder.encode();
         } catch (BindingException ex) {
@@ -1071,7 +999,7 @@ public abstract class AbstractAuthenticationRequest extends AbstractSAML2Profile
             throw new ProfileException("Unable to encode response the relying party: "
                     + relyingParty.getRelyingPartyId(), ex);
         }
-        
+
     }
-    
+
 }
index 95533e2..63b2dbb 100644 (file)
@@ -29,6 +29,7 @@ import org.joda.time.DateTime;
 import org.opensaml.common.SAMLObjectBuilder;
 import org.opensaml.common.SAMLVersion;
 import org.opensaml.common.impl.SAMLObjectContentReference;
+import org.opensaml.common.xml.SAMLConstants;
 import org.opensaml.log.Level;
 import org.opensaml.saml2.core.Advice;
 import org.opensaml.saml2.core.Assertion;
@@ -81,9 +82,6 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
     /** 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);
 
@@ -444,7 +442,7 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
         RoleDescriptor relyingPartyRole;
         try {
             relyingPartyRole = getMetadataProvider().getRole(requestContext.getRelyingPartyId(),
-                    requestContext.getRelyingPartyRole(), SAML20_PROTOCOL_URI);
+                    requestContext.getRelyingPartyRole(), SAMLConstants.SAML20P_NS);
         } catch (MetadataProviderException e) {
             throw new ProfileException("Unable to lookup entity metadata for relying party "
                     + requestContext.getRelyingPartyId());
@@ -576,7 +574,7 @@ 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);
@@ -615,12 +613,12 @@ 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);
+                    requestContext.getAssertingPartyRole(), SAMLConstants.SAML20P_NS);
             List<String> assertingPartySupportedFormats = getEntitySupportedFormats(assertingPartyRole);
 
             String nameFormat = null;
@@ -638,7 +636,7 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
 
             if (nameFormats.isEmpty()) {
                 RoleDescriptor relyingPartyRole = getMetadataProvider().getRole(requestContext.getRelyingPartyId(),
-                        requestContext.getRelyingPartyRole(), SAML20_PROTOCOL_URI);
+                        requestContext.getRelyingPartyRole(), SAMLConstants.SAML20P_NS);
                 List<String> relyingPartySupportedFormats = getEntitySupportedFormats(relyingPartyRole);
 
                 assertingPartySupportedFormats.retainAll(relyingPartySupportedFormats);
index fe0fbc6..11ec69d 100644 (file)
@@ -27,6 +27,7 @@ import org.opensaml.common.binding.BindingException;
 import org.opensaml.common.binding.decoding.MessageDecoder;
 import org.opensaml.common.binding.encoding.MessageEncoder;
 import org.opensaml.common.binding.security.SAMLSecurityPolicy;
+import org.opensaml.saml2.binding.decoding.HTTPSOAP11Decoder;
 import org.opensaml.saml2.core.AttributeQuery;
 import org.opensaml.saml2.core.AttributeStatement;
 import org.opensaml.saml2.core.NameID;
@@ -115,9 +116,11 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
         if (log.isDebugEnabled()) {
             log.debug("Decoding incomming request");
         }
-        MessageDecoder<ServletRequest> decoder = getMessageDecoderFactory().getMessageDecoder(BINDING);
+        MessageDecoder<ServletRequest> decoder = getMessageDecoderFactory().getMessageDecoder(
+                HTTPSOAP11Decoder.BINDING_URI);
         if (decoder == null) {
-            throw new ProfileException("No request decoder was registered for binding type: " + BINDING);
+            throw new ProfileException("No request decoder was registered for binding type: "
+                    + HTTPSOAP11Decoder.BINDING_URI);
         }
         super.populateMessageDecoder(decoder);
 
diff --git a/src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AuthenticationRequestException.java b/src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AuthenticationRequestException.java
deleted file mode 100644 (file)
index e7e717f..0000000
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
- *
- * Licensed 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 edu.internet2.middleware.shibboleth.idp.profile.saml2;
-
-import org.opensaml.saml2.core.Status;
-
-/**
- * Indicates an error while processing an {@link AuthenticationRequest}.
- */
-public class AuthenticationRequestException extends java.lang.Exception {
-
-       protected Status status = null;
-
-       /**
-        * Get the SAML 2 Status, if any, associated with this error.
-        * 
-        * @return A SAML 2 Status object, or <code>null</code> if none was set.
-        */
-       public Status getStatus() {
-               return status;
-       }
-
-       /**
-        * Creates a new instance of <code>AuthenticationRequestException</code>
-        * without detail message.
-        */
-       public AuthenticationRequestException() {
-       }
-
-       /**
-        * Constructs an instance of <code>AuthenticationRequestException</code>
-        * with the specified detail message.
-        * 
-        * @param message
-        *            The detail message.
-        */
-       public AuthenticationRequestException(final String message) {
-               super(message);
-       }
-
-       /**
-        * Constructs an instance of <code>AuthenticationRequestException</code>
-        * with the specified detail message.
-        * 
-        * @param message
-        *            The detail message.
-        * @param status
-        *            A SAML 2 Status indicated which error should be returned to
-        *            the requestor.
-        */
-       public AuthenticationRequestException(final String message,
-                       final Status status) {
-               super(message);
-               this.status = status;
-       }
-
-       /**
-        * Constructs an instance of <code>AuthenticationRequestException</code>
-        * with the specified cause and a detail message of
-        * <code>(cause==null ? null : cause.toString())</code> (which typically
-        * contains the class and detail message of cause). This constructor is
-        * useful for exceptions that are little more than wrappers for other
-        * throwables (for example, {@link PrivilegedActionException}).
-        * 
-        * @param cause
-        *            The cause (which is saved for later retrieval by the
-        *            {@link Throwable#getCause()} method). (A <code>null</code>
-        *            is permitted, and indicates that the cause is nonexistent or
-        *            unknown.)
-        */
-       public AuthenticationRequestException(final Throwable cause) {
-               super(cause);
-       }
-
-       /**
-        * Constructs an instance of <code>AuthenticationRequestException</code>
-        * with the specified cause and a detail message of
-        * <code>(cause==null ? null : cause.toString())</code> (which typically
-        * contains the class and detail message of cause). This constructor is
-        * useful for exceptions that are little more than wrappers for other
-        * throwables (for example, {@link PrivilegedActionException}).
-        * 
-        * @param cause
-        *            The cause (which is saved for later retrieval by the
-        *            {@link Throwable#getCause()} method). (A <code>null</code>
-        *            is permitted, and indicates that the cause is nonexistent or
-        *            unknown.)
-        * @param code
-        *            A SAML 2 Status indicated which error should be returned to
-        *            the requestor.
-        */
-       public AuthenticationRequestException(final Throwable cause,
-                       final Status status) {
-               super(cause);
-               this.status = status;
-       }
-
-       /**
-        * Constructs a new exception with the specified detail message and cause.
-        * 
-        * Note that the detail message associated with cause is not automatically
-        * incorporated in this exception's detail message.
-        * 
-        * @param message
-        *            The detail message (which is saved for later retrieval by the
-        *            {@link Throwable#getMessage()} method).
-        * @param cause
-        *            The cause (which is saved for later retrieval by the
-        *            {@link Throwable#getCause()} method). (A <code>null</code>
-        *            is permitted, and indicates that the cause is nonexistent or
-        *            unknown.)
-        */
-       public AuthenticationRequestException(final String message,
-                       final Throwable cause) {
-               super(message, cause);
-       }
-
-       /**
-        * Constructs a new exception with the specified detail message and cause.
-        * 
-        * Note that the detail message associated with cause is not automatically
-        * incorporated in this exception's detail message.
-        * 
-        * @param message
-        *            The detail message (which is saved for later retrieval by the
-        *            {@link Throwable#getMessage()} method).
-        * @param cause
-        *            The cause (which is saved for later retrieval by the
-        *            {@link Throwable#getCause()} method). (A <code>null</code>
-        *            is permitted, and indicates that the cause is nonexistent or
-        *            unknown.)
-        * @param code
-        *            A SAML 2 Status indicated which error should be returned to
-        *            the requestor.
-        */
-       public AuthenticationRequestException(final String message,
-                       final Throwable cause, final Status code) {
-               super(message, cause);
-               this.status = status;
-       }
-
-}
diff --git a/src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AuthenticationRequestProfileHandler.java b/src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AuthenticationRequestProfileHandler.java
new file mode 100644 (file)
index 0000000..b43266f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed 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 edu.internet2.middleware.shibboleth.idp.profile.saml2;
+
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.opensaml.saml2.core.AuthnRequest;
+import org.opensaml.saml2.core.Response;
+
+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.SSOConfiguration;
+import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
+
+/**
+ * SAML 2.0 authentication request profile handler.
+ */
+public class AuthenticationRequestProfileHandler extends AbstractSAML2ProfileHandler {
+
+    /** {@inheritDoc} */
+    public String getProfileId() {
+        return "urn:mace:shibboleth:2.0:idp:profiles:saml2:request:authentication";
+    }
+
+    /** {@inheritDoc} */
+    public void processRequest(ProfileRequest<ServletRequest> request, ProfileResponse<ServletResponse> response)
+            throws ProfileException {
+
+    }
+
+    /**
+     * Represents the internal state of a SAML 2.0 Authentiation Request while it's being processed by the IdP.
+     */
+    protected class AuthenticationRequestContext extends
+            SAML2ProfileRequestContext<AuthnRequest, Response, SSOConfiguration> {
+
+        /** The IdP's LoginContext. */
+        private LoginContext loginContext;
+
+        /**
+         * Constructor.
+         * 
+         * @param request current profile request
+         * @param response current profile response
+         */
+        public AuthenticationRequestContext(ProfileRequest<ServletRequest> request,
+                ProfileResponse<ServletResponse> response) {
+            super(request, response);
+        }
+
+        /**
+         * Gets the login context for this request.
+         * 
+         * @return login context for this request
+         */
+        public LoginContext getLoginContext() {
+            return loginContext;
+        }
+
+        /**
+         * Sets the login context for this request.
+         * 
+         * @param context login context for this request
+         */
+        public void setLoginContext(LoginContext context) {
+            loginContext = context;
+        }
+    }
+}
\ No newline at end of file