Policy based NameID format and assertion signing code
authorlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Mon, 4 Jun 2007 23:36:06 +0000 (23:36 +0000)
committerlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Mon, 4 Jun 2007 23:36:06 +0000 (23:36 +0000)
Unit tests for basic attribute query flows
SAML2 NameID encoder
Lots of logging

git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@2231 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

resources/classpath/schema/shibboleth-2.0-idp-profile.xsd
resources/conf/internal.xml
src/edu/internet2/middleware/shibboleth/idp/config/profile/AbstractSAMLProfileHandlerBeanDefinitionParser.java
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AbstractSAML2ProfileHandler.java
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AttributeQueryProfileHandler.java
tests/data/conf1/attribute-resolver.xml
tests/data/conf1/internal.xml
tests/edu/internet2/middleware/shibboleth/idp/system/conf1/SAML2AttributeQueryTestCase.java

index f2067d3..0cff17d 100644 (file)
                         </documentation>
                     </annotation>
                 </attribute>
+                <attribute name="idGeneratorId" type="string" default="shibboleth.IdGenerator">
+                    <annotation>
+                        <documentation>
+                            The component ID of a generator used to generated things like response and assertion IDs.
+
+                            This setting should not be changed from its default unless the deployer fully understands
+                            the inter-relationship between IdP components.
+                        </documentation>
+                    </annotation>
+                </attribute>
             </extension>
         </complexContent>
     </complexType>
index bdc659b..6a44618 100644 (file)
             </list>
         </constructor-arg>
     </bean>
+    
+    <bean id="shibboleth.IdGenerator" class="org.opensaml.common.impl.SecureRandomIdentifierGenerator">
+        <constructor-arg value="SHA1PRNG" />
+    </bean>
 
     <bean id="shibboleth.VelocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean" >
         <property name="velocityProperties">
index e19346b..8fb2588 100644 (file)
@@ -34,5 +34,7 @@ public abstract class AbstractSAMLProfileHandlerBeanDefinitionParser extends
         builder.addPropertyReference("messageDecoderFactory", config.getAttributeNS(null, "messageDecoderFactoryId"));
 
         builder.addPropertyReference("messageEncoderFactory", config.getAttributeNS(null, "messageEncoderFactoryId"));
+        
+        builder.addPropertyReference("idGenerator", config.getAttributeNS(null, "idGeneratorId"));
     }
 }
\ No newline at end of file
index a30e1fd..95533e2 100644 (file)
@@ -19,15 +19,16 @@ package edu.internet2.middleware.shibboleth.idp.profile.saml2;
 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.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;
@@ -52,6 +53,7 @@ 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;
@@ -61,6 +63,9 @@ 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.log.AuditLogEntry;
 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
 import edu.internet2.middleware.shibboleth.common.profile.ProfileRequest;
@@ -79,6 +84,9 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
     /** 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);
+
     /** For building response. */
     private SAMLObjectBuilder<Response> responseBuilder;
 
@@ -301,6 +309,7 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
 
         // create the SAML response and add the assertion
         Response samlResponse = getResponseBuilder().buildObject();
+        samlResponse.setIssueInstant(issueInstant);
         populateStatusResponse(requestContext, samlResponse);
 
         samlResponse.getAssertions().add(assertion);
@@ -323,7 +332,6 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      * @return the built assertion
      */
     protected Assertion buildAssertion(SAML2ProfileRequestContext requestContext, DateTime issueInstant) {
-
         Assertion assertion = getAssertionBuilder().buildObject();
         assertion.setID(getIdGenerator().generateIdentifier());
         assertion.setIssueInstant(issueInstant);
@@ -407,8 +415,9 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      */
     protected void populateStatusResponse(SAML2ProfileRequestContext requestContext, StatusResponseType response) {
         response.setID(getIdGenerator().generateIdentifier());
-        response.setInResponseTo(requestContext.getSamlRequest().getID());
-        response.setIssueInstant(response.getIssueInstant());
+        if (requestContext.getSamlRequest() != null) {
+            response.setInResponseTo(requestContext.getSamlRequest().getID());
+        }
         response.setVersion(SAMLVersion.VERSION_20);
         response.setIssuer(buildEntityIssuer(requestContext));
     }
@@ -419,23 +428,67 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      * 
      * @param requestContext current request context
      * @param assertion assertion to sign
+     * 
+     * @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(SAML2ProfileRequestContext requestContext, Assertion assertion) {
+    protected void signAssertion(SAML2ProfileRequestContext 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(), SAML20_PROTOCOL_URI);
+        } catch (MetadataProviderException e) {
+            throw new ProfileException("Unable to lookup entity metadata for relying party "
+                    + requestContext.getRelyingPartyId());
+        }
         AbstractSAML2ProfileConfiguration profileConfig = requestContext.getProfileConfiguration();
 
-        if (!profileConfig.getSignAssertions()) {
+        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 = 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);
@@ -455,7 +508,6 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      * @return a Status object.
      */
     protected Status buildStatus(String topLevelCode, String secondLevelCode, String secondLevelFailureMessage) {
-
         Status status = getStatusBuilder().buildObject();
 
         StatusCode statusCode = getStatusCodeBuilder().buildObject();
@@ -484,9 +536,14 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      * @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(SAML2ProfileRequestContext requestContext, String confirmationMethod) {
-        NameID nameID = requestContext.getSubjectNameID();
+    protected Subject buildSubject(SAML2ProfileRequestContext requestContext, String confirmationMethod)
+            throws ProfileException {
+        NameID nameID = buildNameId(requestContext);
+        requestContext.setSubjectNameID(nameID);
         // TODO handle encryption
 
         SubjectConfirmation subjectConfirmation = getSubjectConfirmationBuilder().buildObject();
@@ -500,26 +557,53 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
     }
 
     /**
-     * Constructs an SAML response message carrying a request error.
+     * Builds a NameID appropriate for this request. NameIDs are built by inspecting the SAML request and metadata,
+     * picking a name format that was requested by the relying party or is mutually supported by both the relying party
+     * and asserting party as described in their metadata entries. Once a set of supported name formats is determined
+     * the principals attributes are inspected for an attribtue supported an attribute encoder whose category is one of
+     * the supported name formats.
      * 
      * @param requestContext current request context
-     * @param topLevelCode The top-level status code. Should be from saml-core-2.0-os, sec. 3.2.2.2
-     * @param secondLevelCode An optional second-level failure code. Should be from saml-core-2.0-is, sec 3.2.2.2. If
-     *            null, no second-level Status element will be set.
-     * @param secondLevelFailureMessage An optional second-level failure message
      * 
-     * @return the constructed error response
+     * @return the NameID appropriate for this request
+     * 
+     * @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 Response buildErrorResponse(SAML2ProfileRequestContext requestContext, String topLevelCode,
-            String secondLevelCode, String secondLevelFailureMessage) {
-        Response samlResponse = getResponseBuilder().buildObject();
-        samlResponse.setIssueInstant(new DateTime());
-        populateStatusResponse(requestContext, samlResponse);
+    protected NameID buildNameId(SAML2ProfileRequestContext requestContext) throws ProfileException {
+        if (log.isDebugEnabled()) {
+            log.debug("Building assertion NameID for principal/relying party:" + requestContext.getPrincipalName()
+                    + "/" + requestContext.getRelyingPartyId());
+        }
+        Map<String, BaseAttribute> principalAttributes = requestContext.getPrincipalAttributes();
+        List<String> supportedNameFormats = getNameIDFormat(requestContext);
 
-        Status status = buildStatus(topLevelCode, secondLevelCode, secondLevelFailureMessage);
-        samlResponse.setStatus(status);
+        if (log.isDebugEnabled()) {
+            log.debug("Supported NameID formats: " + supportedNameFormats);
+        }
 
-        return samlResponse;
+        if (principalAttributes != null && supportedNameFormats != null) {
+            try {
+                AttributeEncoder<NameID> nameIdEncoder = null;
+                for (BaseAttribute attribute : principalAttributes.values()) {
+                    for (String nameFormat : supportedNameFormats) {
+                        nameIdEncoder = attribute.getEncoderByCategory(nameFormat);
+                        if (nameIdEncoder != null) {
+                            if (log.isDebugEnabled()) {
+                                log.debug("Using attribute " + attribute.getId() + " suppoting NameID format "
+                                        + nameFormat + " to create the NameID for principal "
+                                        + requestContext.getPrincipalName());
+                            }
+                            return nameIdEncoder.encode(attribute);
+                        }
+                    }
+                }
+            } catch (AttributeEncodingException e) {
+                throw new ProfileException("Unable to encode NameID attribute", e);
+            }
+        }
+
+        throw new ProfileException("No principal attributes support NameID construction");
     }
 
     /**
@@ -536,7 +620,7 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
 
         try {
             RoleDescriptor assertingPartyRole = getMetadataProvider().getRole(requestContext.getAssertingPartyId(),
-                    requestContext.getAssertingPartyRole(), SAMLConstants.SAML20P_NS);
+                    requestContext.getAssertingPartyRole(), SAML20_PROTOCOL_URI);
             List<String> assertingPartySupportedFormats = getEntitySupportedFormats(assertingPartyRole);
 
             String nameFormat = null;
@@ -554,7 +638,7 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
 
             if (nameFormats.isEmpty()) {
                 RoleDescriptor relyingPartyRole = getMetadataProvider().getRole(requestContext.getRelyingPartyId(),
-                        requestContext.getRelyingPartyRole(), SAMLConstants.SAML20P_NS);
+                        requestContext.getRelyingPartyRole(), SAML20_PROTOCOL_URI);
                 List<String> relyingPartySupportedFormats = getEntitySupportedFormats(relyingPartyRole);
 
                 assertingPartySupportedFormats.retainAll(relyingPartySupportedFormats);
@@ -602,6 +686,29 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
     }
 
     /**
+     * Constructs an SAML response message carrying a request error.
+     * 
+     * @param requestContext current request context
+     * @param topLevelCode The top-level status code. Should be from saml-core-2.0-os, sec. 3.2.2.2
+     * @param secondLevelCode An optional second-level failure code. Should be from saml-core-2.0-is, sec 3.2.2.2. If
+     *            null, no second-level Status element will be set.
+     * @param secondLevelFailureMessage An optional second-level failure message
+     * 
+     * @return the constructed error response
+     */
+    protected Response buildErrorResponse(SAML2ProfileRequestContext requestContext, String topLevelCode,
+            String secondLevelCode, String secondLevelFailureMessage) {
+        Response samlResponse = getResponseBuilder().buildObject();
+        samlResponse.setIssueInstant(new DateTime());
+        populateStatusResponse(requestContext, samlResponse);
+
+        Status status = buildStatus(topLevelCode, secondLevelCode, secondLevelFailureMessage);
+        samlResponse.setStatus(status);
+
+        return samlResponse;
+    }
+
+    /**
      * Writes an aduit log entry indicating the successful response to the attribute request.
      * 
      * @param context current request context
@@ -627,9 +734,7 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      * @param <ResponseType> type of SAML 2 response
      * @param <ProfileConfigurationType> configuration type for this profile
      */
-    protected class SAML2ProfileRequestContext<RequestType extends RequestAbstractType, 
-                                               ResponseType extends StatusResponseType, 
-                                               ProfileConfigurationType extends AbstractSAML2ProfileConfiguration>
+    protected class SAML2ProfileRequestContext<RequestType extends RequestAbstractType, ResponseType extends StatusResponseType, ProfileConfigurationType extends AbstractSAML2ProfileConfiguration>
             extends SAMLProfileRequestContext {
 
         /** SAML request message. */
index 31bcebc..fe0fbc6 100644 (file)
@@ -45,6 +45,7 @@ import edu.internet2.middleware.shibboleth.common.attribute.provider.ShibbolethS
 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.saml2.AttributeQueryConfiguration;
 import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
 import edu.internet2.middleware.shibboleth.idp.session.Session;
@@ -75,25 +76,11 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
         try {
             decodeRequest(requestContext);
 
-            // populate request context with information from decoded message
-            SAMLSecurityPolicy securityPolicy = requestContext.getMessageDecoder().getSecurityPolicy();
-            requestContext.setRelyingPartyId(securityPolicy.getIssuer());
-            requestContext
-                    .setRelyingPartyConfiguration(getRelyingPartyConfiguration(requestContext.getRelyingPartyId()));
-            requestContext.setRelyingPartyRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
-            requestContext.setAssertingPartyId(requestContext.getRelyingPartyConfiguration().getProviderId());
-            requestContext.setAssertingPartyRole(AttributeAuthorityDescriptor.DEFAULT_ELEMENT_NAME);
-            requestContext.setProfileConfiguration((AttributeQueryConfiguration) getProfileConfiguration(requestContext
-                    .getRelyingPartyId(), AttributeQueryConfiguration.PROFILE_ID));
-            requestContext.setSamlRequest((AttributeQuery) requestContext.getMessageDecoder().getSAMLMessage());
-
-            // TODO principal
-
-            // create the SAML attribute statement
+            // Lookup principal name and attributes, create attribute statement from information
             ArrayList<Statement> statements = new ArrayList<Statement>();
             statements.add(buildAttributeStatement(requestContext));
-            
-            //TODO NameID
+
+            // create the assertion subject
             Subject assertionSubject = buildSubject(requestContext, "urn:oasis:names:tc:SAML:2.0:cm:sender-vouches");
 
             // create the SAML response
@@ -104,6 +91,9 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
         } catch (AttributeRequestException e) {
             samlResponse = buildErrorResponse(requestContext, StatusCode.RESPONDER_URI,
                     StatusCode.INVALID_ATTR_NAME_VALUE_URI, e.getMessage());
+        } catch (ProfileException e) {
+            samlResponse = buildErrorResponse(requestContext, StatusCode.RESPONDER_URI, StatusCode.REQUEST_DENIED_URI,
+                    e.getMessage());
         }
 
         requestContext.setSamlResponse(samlResponse);
@@ -122,23 +112,44 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
      *             security policy requirements
      */
     protected void decodeRequest(AttributeQueryContext requestContext) throws ProfileException, SecurityPolicyException {
+        if (log.isDebugEnabled()) {
+            log.debug("Decoding incomming request");
+        }
         MessageDecoder<ServletRequest> decoder = getMessageDecoderFactory().getMessageDecoder(BINDING);
         if (decoder == null) {
             throw new ProfileException("No request decoder was registered for binding type: " + BINDING);
         }
-
         super.populateMessageDecoder(decoder);
+
         decoder.setRequest(requestContext.getProfileRequest().getRawRequest());
         requestContext.setMessageDecoder(decoder);
 
         try {
             decoder.decode();
             if (log.isDebugEnabled()) {
-                log.debug("decoded http servlet request");
+                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());
         }
     }
 
@@ -155,6 +166,12 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
     protected AttributeStatement buildAttributeStatement(AttributeQueryContext requestContext) throws ProfileException,
             AttributeRequestException {
 
+        if (log.isDebugEnabled()) {
+            log.debug("Creating attribute statement in response to SAML request "
+                    + requestContext.getSamlRequest().getID() + " from relying party "
+                    + requestContext.getRelyingPartyId());
+        }
+
         try {
             AttributeQueryConfiguration profileConfiguration = requestContext.getProfileConfiguration();
             if (profileConfiguration == null) {
@@ -165,6 +182,21 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
 
             SAML2AttributeAuthority attributeAuthority = profileConfiguration.getAttributeAuthority();
 
+            ShibbolethSAMLAttributeRequestContext<NameID, AttributeQuery> attributeRequestContext = buildAttributeRequestContext(requestContext);
+
+            if (log.isDebugEnabled()) {
+                log.debug("Resolving principal name for subject of SAML request "
+                        + requestContext.getSamlRequest().getID() + " 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 "
+                        + requestContext.getSamlRequest().getID() + " from relying party "
+                        + requestContext.getRelyingPartyId());
+            }
             Map<String, BaseAttribute> principalAttributes = attributeAuthority
                     .getAttributes(buildAttributeRequestContext(requestContext));
 
@@ -173,7 +205,8 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
             return attributeAuthority.buildAttributeStatement(requestContext.getSamlRequest(), principalAttributes
                     .values());
         } catch (AttributeRequestException e) {
-            log.error("Error resolving attributes", e);
+            log.error("Error resolving attributes for SAML request " + requestContext.getSamlRequest().getID()
+                    + " from relying party " + requestContext.getRelyingPartyId(), e);
             throw e;
         }
     }
@@ -191,6 +224,11 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
         ShibbolethSAMLAttributeRequestContext<NameID, AttributeQuery> queryContext = new ShibbolethSAMLAttributeRequestContext<NameID, 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);
@@ -203,9 +241,6 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
             }
         }
 
-        queryContext.setProfileConfiguration(requestContext.getProfileConfiguration());
-        queryContext.setRequest(requestContext.getProfileRequest());
-
         return queryContext;
     }
 
@@ -217,6 +252,10 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
      * @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 " + requestContext.getSamlRequest().getID()
+                    + " from relying party " + requestContext.getRelyingPartyId());
+        }
         MessageEncoder<ServletResponse> encoder = getMessageEncoderFactory().getMessageEncoder(BINDING);
         if (encoder == null) {
             throw new ProfileException("No response encoder was registered for binding type: " + BINDING);
@@ -226,6 +265,13 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
         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. */
index 70d5125..3c7461b 100644 (file)
@@ -3,16 +3,19 @@
 <AttributeResolver xmlns="urn:mace:shibboleth:2.0:resolver"
                    xmlns:resolver="urn:mace:shibboleth:2.0:resolver"
                    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-                   xmlns:pc="urn:mace:shibboleth:2.0:resolver:pc:direct"
+                   xmlns:pc="urn:mace:shibboleth:2.0:resolver:pc"
                    xmlns:simple="urn:mace:shibboleth:2.0:resolver:ad:simple"
                       xmlns:static="urn:mace:shibboleth:2.0:resolver:dc:static"
+                   xmlns:enc="urn:mace:shibboleth:2.0:attribute:encoder"
                       xsi:schemaLocation="urn:mace:shibboleth:2.0:resolver classpath:/schema/shibboleth-2.0-attribute-resolver.xsd
-                                       urn:mace:shibboleth:2.0:resolver:pc:direct classpath:/schema/shibboleth-2.0-attribute-resolver-pc-direct.xsd
+                                       urn:mace:shibboleth:2.0:resolver:pc classpath:/schema/shibboleth-2.0-attribute-resolver-pc.xsd
                                        urn:mace:shibboleth:2.0:resolver:ad:simple classpath:/schema/shibboleth-2.0-attribute-resolver-ad-simple.xsd
-                                       urn:mace:shibboleth:2.0:resolver:dc:static classpath:/schema/shibboleth-2.0-attribute-resolver-dc-static.xsd">
+                                       urn:mace:shibboleth:2.0:resolver:dc:static classpath:/schema/shibboleth-2.0-attribute-resolver-dc-static.xsd
+                                       urn:mace:shibboleth:2.0:attribute:encoder classpath:/schema/shibboleth-2.0-attribute-encoder.xsd">
                                        
     <resolver:AttributeDefinition xsi:type="simple:Simple" id="uid">
         <resolver:DataConnectorDependency ref="static" />
+        <resolver:AttributeEncoder xsi:type="enc:SAML2StringNameID" />
     </resolver:AttributeDefinition>
     
     <resolver:AttributeDefinition xsi:type="simple:Simple" id="cn">
index 777f129..08b586e 100644 (file)
             </list>
         </constructor-arg>
     </bean>
+    
+    <bean id="shibboleth.IdGenerator" class="org.opensaml.common.impl.SecureRandomIdentifierGenerator">
+        <constructor-arg value="SHA1PRNG" />
+    </bean>
 
     <bean id="shibboleth.VelocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean" >
         <property name="velocityProperties">
index 7e256c7..e7b5355 100644 (file)
@@ -24,6 +24,7 @@ import org.opensaml.common.SAMLVersion;
 import org.opensaml.saml2.core.AttributeQuery;
 import org.opensaml.saml2.core.Issuer;
 import org.opensaml.saml2.core.NameID;
+import org.opensaml.saml2.core.StatusCode;
 import org.opensaml.saml2.core.Subject;
 import org.opensaml.ws.soap.common.SOAPObjectBuilder;
 import org.opensaml.ws.soap.soap11.Body;
@@ -69,8 +70,8 @@ public class SAML2AttributeQueryTestCase extends BaseConf1TestCase {
         handler.processRequest(profileRequest, profileResponse);
 
         String response = servletResponse.getContentAsString();
-        assertTrue(response.contains("urn:oasis:names:tc:SAML:2.0:status:Requester"));
-        assertTrue(response.contains("urn:oasis:names:tc:SAML:2.0:status:RequestDenied"));
+        assertTrue(response.contains(StatusCode.REQUESTER_URI));
+        assertTrue(response.contains(StatusCode.REQUEST_DENIED_URI));
     }
     
     /** Test a request where the Issuer is authenticated and has not requested any specific attributes. */
@@ -95,8 +96,8 @@ public class SAML2AttributeQueryTestCase extends BaseConf1TestCase {
         handler.processRequest(profileRequest, profileResponse);
 
         String response = servletResponse.getContentAsString();
-        assertTrue(response.contains("urn:oasis:names:tc:SAML:2.0:status:Requester"));
-        assertTrue(response.contains("urn:oasis:names:tc:SAML:2.0:status:RequestDenied"));
+        assertTrue(response.contains(StatusCode.RESPONDER_URI));
+        assertTrue(response.contains(StatusCode.INVALID_ATTR_NAME_VALUE_URI));
     }
     
     /** Test a request where the Issuer is authenticated and has not requested any specific attributes. */