Add some logging messages
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / profile / saml2 / SSOProfileHandler.java
index a83d6a5..f198ba9 100644 (file)
@@ -21,17 +21,14 @@ import java.util.ArrayList;
 
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletException;
 
 import javax.servlet.RequestDispatcher;
 import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
 
 import org.apache.log4j.Logger;
 import org.opensaml.common.SAMLObjectBuilder;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
 
 import org.apache.log4j.Logger;
 import org.opensaml.common.SAMLObjectBuilder;
-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.common.binding.decoding.SAMLMessageDecoder;
+import org.opensaml.common.xml.SAMLConstants;
+import org.opensaml.saml2.binding.AuthnResponseEndpointSelector;
 import org.opensaml.saml2.core.AuthnContext;
 import org.opensaml.saml2.core.AuthnContextClassRef;
 import org.opensaml.saml2.core.AuthnContextDeclRef;
 import org.opensaml.saml2.core.AuthnContext;
 import org.opensaml.saml2.core.AuthnContextClassRef;
 import org.opensaml.saml2.core.AuthnContextDeclRef;
@@ -41,18 +38,28 @@ import org.opensaml.saml2.core.RequestedAuthnContext;
 import org.opensaml.saml2.core.Response;
 import org.opensaml.saml2.core.Statement;
 import org.opensaml.saml2.core.StatusCode;
 import org.opensaml.saml2.core.Response;
 import org.opensaml.saml2.core.Statement;
 import org.opensaml.saml2.core.StatusCode;
-import org.opensaml.saml2.core.Subject;
+import org.opensaml.saml2.core.SubjectLocality;
+import org.opensaml.saml2.metadata.AssertionConsumerService;
+import org.opensaml.saml2.metadata.Endpoint;
+import org.opensaml.saml2.metadata.EntityDescriptor;
 import org.opensaml.saml2.metadata.IDPSSODescriptor;
 import org.opensaml.saml2.metadata.SPSSODescriptor;
 import org.opensaml.saml2.metadata.IDPSSODescriptor;
 import org.opensaml.saml2.metadata.SPSSODescriptor;
+import org.opensaml.saml2.metadata.provider.MetadataProvider;
+import org.opensaml.saml2.metadata.provider.MetadataProviderException;
+import org.opensaml.ws.message.decoder.MessageDecodingException;
 import org.opensaml.ws.security.SecurityPolicyException;
 import org.opensaml.ws.security.SecurityPolicyException;
+import org.opensaml.ws.transport.http.HTTPInTransport;
+import org.opensaml.ws.transport.http.HTTPOutTransport;
+import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
+import org.opensaml.ws.transport.http.HttpServletResponseAdapter;
 import org.opensaml.xml.io.MarshallingException;
 import org.opensaml.xml.io.UnmarshallingException;
 
 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
 import org.opensaml.xml.io.MarshallingException;
 import org.opensaml.xml.io.UnmarshallingException;
 
 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.ProfileConfiguration;
 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml2.SSOConfiguration;
 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml2.SSOConfiguration;
+import edu.internet2.middleware.shibboleth.common.util.HttpHelper;
 import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
 import edu.internet2.middleware.shibboleth.idp.authn.Saml2LoginContext;
 
 import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
 import edu.internet2.middleware.shibboleth.idp.authn.Saml2LoginContext;
 
@@ -74,33 +81,25 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
     /** Builder of AuthnContextDeclRef objects. */
     private SAMLObjectBuilder<AuthnContextDeclRef> authnContextDeclRefBuilder;
 
     /** Builder of AuthnContextDeclRef objects. */
     private SAMLObjectBuilder<AuthnContextDeclRef> authnContextDeclRefBuilder;
 
+    /** Builder of SubjectLocality objects. */
+    private SAMLObjectBuilder<SubjectLocality> subjectLocalityBuilder;
+
     /** URL of the authentication manager servlet. */
     private String authenticationManagerPath;
 
     /** URI of request decoder. */
     private String decodingBinding;
 
     /** URL of the authentication manager servlet. */
     private String authenticationManagerPath;
 
     /** URI of request decoder. */
     private String decodingBinding;
 
-    /** URI of response encoder. */
-    private String encodingBinding;
-
     /**
      * Constructor.
      * 
      * @param authnManagerPath path to the authentication manager servlet
     /**
      * Constructor.
      * 
      * @param authnManagerPath path to the authentication manager servlet
-     * @param decoder URI of the request decoder to use
-     * @param encoder URI of the response encoder to use
      */
     @SuppressWarnings("unchecked")
      */
     @SuppressWarnings("unchecked")
-    public SSOProfileHandler(String authnManagerPath, String decoder, String encoder) {
+    public SSOProfileHandler(String authnManagerPath) {
         super();
 
         super();
 
-        if (authnManagerPath == null || decoder == null || encoder == null) {
-            throw new IllegalArgumentException("AuthN manager path, decoding, encoding bindings URI may not be null");
-        }
-
         authenticationManagerPath = authnManagerPath;
         authenticationManagerPath = authnManagerPath;
-        decodingBinding = decoder;
-        encodingBinding = encoder;
 
         authnStatementBuilder = (SAMLObjectBuilder<AuthnStatement>) getBuilderFactory().getBuilder(
                 AuthnStatement.DEFAULT_ELEMENT_NAME);
 
         authnStatementBuilder = (SAMLObjectBuilder<AuthnStatement>) getBuilderFactory().getBuilder(
                 AuthnStatement.DEFAULT_ELEMENT_NAME);
@@ -110,42 +109,8 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
                 AuthnContextClassRef.DEFAULT_ELEMENT_NAME);
         authnContextDeclRefBuilder = (SAMLObjectBuilder<AuthnContextDeclRef>) getBuilderFactory().getBuilder(
                 AuthnContextDeclRef.DEFAULT_ELEMENT_NAME);
                 AuthnContextClassRef.DEFAULT_ELEMENT_NAME);
         authnContextDeclRefBuilder = (SAMLObjectBuilder<AuthnContextDeclRef>) getBuilderFactory().getBuilder(
                 AuthnContextDeclRef.DEFAULT_ELEMENT_NAME);
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 AuthnStatement builder.
-     * 
-     * @return SAML 2 AuthnStatement builder
-     */
-    public SAMLObjectBuilder<AuthnStatement> getAuthnStatementBuilder() {
-        return authnStatementBuilder;
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 AuthnContext builder.
-     * 
-     * @return SAML 2 AuthnContext builder
-     */
-    public SAMLObjectBuilder<AuthnContext> getAuthnContextBuilder() {
-        return authnContextBuilder;
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 AuthnContextClassRef builder.
-     * 
-     * @return SAML 2 AuthnContextClassRef builder
-     */
-    public SAMLObjectBuilder<AuthnContextClassRef> getAuthnContextClassRefBuilder() {
-        return authnContextClassRefBuilder;
-    }
-
-    /**
-     * Convenience method for getting the SAML 2 AuthnContextDeclRef builder.
-     * 
-     * @return SAML 2 AuthnContextDeclRef builder
-     */
-    public SAMLObjectBuilder<AuthnContextDeclRef> getAuthnContextDeclRefBuilder() {
-        return authnContextDeclRefBuilder;
+        subjectLocalityBuilder = (SAMLObjectBuilder<SubjectLocality>) getBuilderFactory().getBuilder(
+                SubjectLocality.DEFAULT_ELEMENT_NAME);
     }
 
     /** {@inheritDoc} */
     }
 
     /** {@inheritDoc} */
@@ -154,14 +119,14 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
     }
 
     /** {@inheritDoc} */
     }
 
     /** {@inheritDoc} */
-    public void processRequest(ProfileRequest<ServletRequest> request, ProfileResponse<ServletResponse> response)
-            throws ProfileException {
+    public void processRequest(HTTPInTransport inTransport, HTTPOutTransport outTransport) throws ProfileException {
+        HttpServletRequest servletRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
+        HttpSession httpSession = servletRequest.getSession(true);
 
 
-        HttpSession httpSession = ((HttpServletRequest) request.getRawRequest()).getSession(true);
         if (httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY) == null) {
         if (httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY) == null) {
-            performAuthentication(request, response);
+            performAuthentication(inTransport, outTransport);
         } else {
         } else {
-            completeAuthenticationRequest(request, response);
+            completeAuthenticationRequest(inTransport, outTransport);
         }
     }
 
         }
     }
 
@@ -169,43 +134,50 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
      * Creates a {@link Saml2LoginContext} an sends the request off to the AuthenticationManager to begin the process of
      * authenticating the user.
      * 
      * Creates a {@link Saml2LoginContext} an sends the request off to the AuthenticationManager to begin the process of
      * authenticating the user.
      * 
-     * @param request current request
-     * @param response current response
+     * @param inTransport inbound request transport
+     * @param outTransport outbound response transport
      * 
      * @throws ProfileException thrown if there is a problem creating the login context and transferring control to the
      *             authentication manager
      */
      * 
      * @throws ProfileException thrown if there is a problem creating the login context and transferring control to the
      *             authentication manager
      */
-    protected void performAuthentication(ProfileRequest<ServletRequest> request,
-            ProfileResponse<ServletResponse> response) throws ProfileException {
-        HttpServletRequest httpRequest = (HttpServletRequest) request.getRawRequest();
+    protected void performAuthentication(HTTPInTransport inTransport, HTTPOutTransport outTransport)
+            throws ProfileException {
+        HttpServletRequest servletRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
 
 
-        AuthnRequest authnRequest = null;
         try {
         try {
-            MessageDecoder<ServletRequest> decoder = decodeRequest(request);
-            SAMLSecurityPolicy<ServletRequest> securityPolicy = decoder.getSecurityPolicy();
+            SSORequestContext requestContext = decodeRequest(inTransport, outTransport);
 
 
-            String relyingParty = securityPolicy.getIssuer();
-            authnRequest = (AuthnRequest) decoder.getSAMLMessage();
+            String relyingPartyId = requestContext.getInboundMessageIssuer();
+            RelyingPartyConfiguration rpConfig = getRelyingPartyConfiguration(relyingPartyId);
+            ProfileConfiguration ssoConfig = rpConfig.getProfileConfiguration(SSOConfiguration.PROFILE_ID);
+            if (ssoConfig == null) {
+                log.error("SAML 2 SSO profile is not configured for relying party " + requestContext.getInboundMessageIssuer());
+                throw new ProfileException("SAML 2 SSO profile is not configured for relying party "
+                        + requestContext.getInboundMessageIssuer());
+            }
 
 
-            Saml2LoginContext loginContext = new Saml2LoginContext(relyingParty, authnRequest);
-            loginContext.setAuthenticationManagerURL(authenticationManagerPath);
-            loginContext.setProfileHandlerURL(httpRequest.getRequestURI());
+            log.debug("Creating login context and transferring control to authentication engine");
+            Saml2LoginContext loginContext = new Saml2LoginContext(relyingPartyId, requestContext.getRelayState(),
+                    requestContext.getInboundSAMLMessage());
+            loginContext.setAuthenticationEngineURL(authenticationManagerPath);
+            loginContext.setProfileHandlerURL(HttpHelper.getRequestUriWithoutContext(servletRequest));
+            if (loginContext.getRequestedAuthenticationMethods().size() == 0) {
+                loginContext.getRequestedAuthenticationMethods().add(rpConfig.getDefaultAuthenticationMethod());
+            }
 
 
-            HttpSession httpSession = httpRequest.getSession();
+            HttpSession httpSession = servletRequest.getSession();
             httpSession.setAttribute(Saml2LoginContext.LOGIN_CONTEXT_KEY, loginContext);
             httpSession.setAttribute(Saml2LoginContext.LOGIN_CONTEXT_KEY, loginContext);
-            RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(authenticationManagerPath);
-            dispatcher.forward(httpRequest, response.getRawResponse());
+            RequestDispatcher dispatcher = servletRequest.getRequestDispatcher(authenticationManagerPath);
+            dispatcher.forward(servletRequest, ((HttpServletResponseAdapter) outTransport).getWrappedResponse());
         } catch (MarshallingException e) {
             log.error("Unable to marshall authentication request context");
             throw new ProfileException("Unable to marshall authentication request context", e);
         } catch (IOException ex) {
         } catch (MarshallingException e) {
             log.error("Unable to marshall authentication request context");
             throw new ProfileException("Unable to marshall authentication request context", e);
         } 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 to AuthenticationManager", ex);
+            throw new ProfileException("Error forwarding SAML 2 AuthnRequest to AuthenticationManager", ex);
         } catch (ServletException 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 to AuthenticationManager", ex);
+            throw new ProfileException("Error forwarding SAML 2 AuthnRequest to AuthenticationManager", ex);
         }
     }
 
         }
     }
 
@@ -213,72 +185,85 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
      * Creates a response to the {@link AuthnRequest} and sends the user, with response in tow, back to the relying
      * party after they've been authenticated.
      * 
      * Creates a response to the {@link AuthnRequest} and sends the user, with response in tow, back to the relying
      * party after they've been authenticated.
      * 
-     * @param request current request
-     * @param response current response
+     * @param inTransport inbound message transport
+     * @param outTransport outbound message transport
      * 
      * @throws ProfileException thrown if the response can not be created and sent back to the relying party
      */
      * 
      * @throws ProfileException thrown if the response can not be created and sent back to the relying party
      */
-    protected void completeAuthenticationRequest(ProfileRequest<ServletRequest> request,
-            ProfileResponse<ServletResponse> response) throws ProfileException {
-
-        HttpSession httpSession = ((HttpServletRequest) request.getRawRequest()).getSession(true);
+    protected void completeAuthenticationRequest(HTTPInTransport inTransport, HTTPOutTransport outTransport)
+            throws ProfileException {
+        HttpServletRequest servletRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
+        HttpSession httpSession = servletRequest.getSession();
 
         Saml2LoginContext loginContext = (Saml2LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
         httpSession.removeAttribute(LoginContext.LOGIN_CONTEXT_KEY);
 
 
         Saml2LoginContext loginContext = (Saml2LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
         httpSession.removeAttribute(LoginContext.LOGIN_CONTEXT_KEY);
 
-        SSORequestContext requestContext = buildRequestContext(loginContext, request, response);
-        
+        SSORequestContext requestContext = buildRequestContext(loginContext, inTransport, outTransport);
+
         checkSamlVersion(requestContext);
 
         Response samlResponse;
         try {
         checkSamlVersion(requestContext);
 
         Response samlResponse;
         try {
-            if (!loginContext.getAuthenticationOK()) {
+            if (loginContext.getPrincipalName() == null) {
+                log.error("User's login context did not contain a principal, user considered unauthenticiated.");
                 requestContext
                         .setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.AUTHN_FAILED_URI, null));
                 throw new ProfileException("User failed authentication");
             }
 
                 requestContext
                         .setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.AUTHN_FAILED_URI, null));
                 throw new ProfileException("User failed authentication");
             }
 
+            resolveAttributes(requestContext);
+
             ArrayList<Statement> statements = new ArrayList<Statement>();
             statements.add(buildAuthnStatement(requestContext));
             if (requestContext.getProfileConfiguration().includeAttributeStatement()) {
             ArrayList<Statement> statements = new ArrayList<Statement>();
             statements.add(buildAuthnStatement(requestContext));
             if (requestContext.getProfileConfiguration().includeAttributeStatement()) {
+                requestContext.setRequestedAttributes(requestContext.getPrincipalAttributes().keySet());
                 statements.add(buildAttributeStatement(requestContext));
             }
 
                 statements.add(buildAttributeStatement(requestContext));
             }
 
-            Subject assertionSubject = buildSubject(requestContext, "urn:oasis:names:tc:SAML:2.0:cm:bearer");
-
-            samlResponse = buildResponse(requestContext, assertionSubject, statements);
+            samlResponse = buildResponse(requestContext, "urn:oasis:names:tc:SAML:2.0:cm:bearer", statements);
         } catch (ProfileException e) {
             samlResponse = buildErrorResponse(requestContext);
         }
 
         } catch (ProfileException e) {
             samlResponse = buildErrorResponse(requestContext);
         }
 
-        requestContext.setSamlResponse(samlResponse);
+        requestContext.setOutboundSAMLMessage(samlResponse);
+        requestContext.setOutboundSAMLMessageId(samlResponse.getID());
+        requestContext.setOutboundSAMLMessageIssueInstant(samlResponse.getIssueInstant());
         encodeResponse(requestContext);
         writeAuditLogEntry(requestContext);
     }
 
     /**
         encodeResponse(requestContext);
         writeAuditLogEntry(requestContext);
     }
 
     /**
-     * Creates an appropriate message decoder, populates it, and decodes the incoming request.
+     * Decodes an incoming request and stores the information in a created request context.
      * 
      * 
-     * @param request current request
+     * @param inTransport inbound transport
+     * @param outTransport outbound transport
      * 
      * 
-     * @return message decoder containing the decoded message and other stateful information
+     * @return request context with decoded information
      * 
      * @throws ProfileException thrown if the incomming message failed decoding
      */
      * 
      * @throws ProfileException thrown if the incomming message failed decoding
      */
-    protected MessageDecoder<ServletRequest> decodeRequest(ProfileRequest<ServletRequest> request)
+    protected SSORequestContext decodeRequest(HTTPInTransport inTransport, HTTPOutTransport outTransport)
             throws ProfileException {
             throws ProfileException {
-        MessageDecoder<ServletRequest> decoder = getMessageDecoderFactory().getMessageDecoder(decodingBinding);
-        if (decoder == null) {
-            log.error("No request decoder was registered for binding type: " + decodingBinding);
-            throw new ProfileException("No request decoder was registered for binding type: " + decodingBinding);
+        if (log.isDebugEnabled()) {
+            log.debug("Decoding message with decoder binding " + decodingBinding);
         }
 
         }
 
-        populateMessageDecoder(decoder);
-        decoder.setRequest(request.getRawRequest());
+        SSORequestContext requestContext = new SSORequestContext();
+        requestContext.setMetadataProvider(getMetadataProvider());
+        
+        requestContext.setInboundMessageTransport(inTransport);
+        requestContext.setInboundSAMLProtocol(SAMLConstants.SAML20P_NS);
+        requestContext.setPeerEntityRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
+        
+        requestContext.setOutboundMessageTransport(outTransport);
+        requestContext.setOutboundSAMLProtocol(SAMLConstants.SAML20P_NS);
+
         try {
         try {
-            decoder.decode();
-            return decoder;
-        } catch (BindingException e) {
+            SAMLMessageDecoder decoder = getMessageDecoders().get(getInboundBinding());
+            requestContext.setMessageDecoder(decoder);
+            decoder.decode(requestContext);
+            return requestContext;
+        } catch (MessageDecodingException e) {
             log.error("Error decoding authentication request message", e);
             throw new ProfileException("Error decoding authentication request message", e);
         } catch (SecurityPolicyException e) {
             log.error("Error decoding authentication request message", e);
             throw new ProfileException("Error decoding authentication request message", e);
         } catch (SecurityPolicyException e) {
@@ -291,36 +276,64 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
      * Creates an authentication request context from the current environmental information.
      * 
      * @param loginContext current login context
      * Creates an authentication request context from the current environmental information.
      * 
      * @param loginContext current login context
-     * @param request current request
-     * @param response current response
+     * @param in inbound transport
+     * @param out outbount transport
      * 
      * @return created authentication request context
      * 
      * @throws ProfileException thrown if there is a problem creating the context
      */
      * 
      * @return created authentication request context
      * 
      * @throws ProfileException thrown if there is a problem creating the context
      */
-    protected SSORequestContext buildRequestContext(Saml2LoginContext loginContext,
-            ProfileRequest<ServletRequest> request, ProfileResponse<ServletResponse> response) throws ProfileException {
-        SSORequestContext requestContext = new SSORequestContext(request, response);
+    protected SSORequestContext buildRequestContext(Saml2LoginContext loginContext, HTTPInTransport in,
+            HTTPOutTransport out) throws ProfileException {
+        SSORequestContext requestContext = new SSORequestContext();
 
         try {
 
         try {
-            String relyingPartyId = loginContext.getRelyingPartyId();
-            AuthnRequest authnRequest = loginContext.getAuthenticationRequest();
-
-            requestContext.setRelyingPartyId(relyingPartyId);
+            requestContext.setMessageDecoder(getMessageDecoders().get(getInboundBinding()));
+            
+            requestContext.setLoginContext(loginContext);
+            requestContext.setPrincipalName(loginContext.getPrincipalName());
+            requestContext.setPrincipalAuthenticationMethod(loginContext.getAuthenticationMethod());
+            requestContext.setUserSession(getUserSession(in));
+            requestContext.setRelayState(loginContext.getRelayState());
+
+            requestContext.setInboundMessageTransport(in);
+            requestContext.setInboundSAMLProtocol(SAMLConstants.SAML20P_NS);
+            requestContext.setInboundMessage(loginContext.getAuthenticationRequest());
+            requestContext.setInboundSAMLMessage(loginContext.getAuthenticationRequest());
+            requestContext.setInboundSAMLMessageId(loginContext.getAuthenticationRequest().getID());
+
+            MetadataProvider metadataProvider = getMetadataProvider();
+            requestContext.setMetadataProvider(metadataProvider);
 
 
+            String relyingPartyId = loginContext.getRelyingPartyId();
+            requestContext.setInboundMessageIssuer(relyingPartyId);
+            EntityDescriptor relyingPartyMetadata = metadataProvider.getEntityDescriptor(relyingPartyId);
+            requestContext.setPeerEntityMetadata(relyingPartyMetadata);
+            requestContext.setPeerEntityRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
+            requestContext.setPeerEntityRoleMetadata(relyingPartyMetadata.getSPSSODescriptor(SAMLConstants.SAML20P_NS));
             RelyingPartyConfiguration rpConfig = getRelyingPartyConfiguration(relyingPartyId);
             requestContext.setRelyingPartyConfiguration(rpConfig);
             RelyingPartyConfiguration rpConfig = getRelyingPartyConfiguration(relyingPartyId);
             requestContext.setRelyingPartyConfiguration(rpConfig);
-
-            requestContext.setRelyingPartyRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
-
-            requestContext.setAssertingPartyId(requestContext.getRelyingPartyConfiguration().getProviderId());
-
-            requestContext.setAssertingPartyRole(IDPSSODescriptor.DEFAULT_ELEMENT_NAME);
-
-            requestContext.setProfileConfiguration((SSOConfiguration) rpConfig
-                    .getProfileConfiguration(SSOConfiguration.PROFILE_ID));
-
-            requestContext.setSamlRequest(authnRequest);
+            requestContext.setPeerEntityEndpoint(selectEndpoint(requestContext));
+
+            String assertingPartyId = rpConfig.getProviderId();
+            requestContext.setLocalEntityId(assertingPartyId);
+            EntityDescriptor assertingPartyMetadata = metadataProvider.getEntityDescriptor(assertingPartyId);
+            requestContext.setLocalEntityMetadata(assertingPartyMetadata);
+            requestContext.setLocalEntityRole(IDPSSODescriptor.DEFAULT_ELEMENT_NAME);
+            requestContext.setLocalEntityRoleMetadata(assertingPartyMetadata
+                    .getIDPSSODescriptor(SAMLConstants.SAML20P_NS));
+
+            requestContext.setOutboundMessageTransport(out);
+            requestContext.setOutboundSAMLProtocol(SAMLConstants.SAML20P_NS);
+            SSOConfiguration profileConfig = (SSOConfiguration) rpConfig
+                    .getProfileConfiguration(SSOConfiguration.PROFILE_ID);
+            requestContext.setProfileConfiguration(profileConfig);
+            requestContext.setOutboundMessageArtifactType(profileConfig.getOutboundArtifactType());
+            if (profileConfig.getSigningCredential() != null) {
+                requestContext.setOutboundSAMLMessageSigningCredential(profileConfig.getSigningCredential());
+            } else if (rpConfig.getDefaultSigningCredential() != null) {
+                requestContext.setOutboundSAMLMessageSigningCredential(rpConfig.getDefaultSigningCredential());
+            }
 
             return requestContext;
         } catch (UnmarshallingException e) {
 
             return requestContext;
         } catch (UnmarshallingException e) {
@@ -328,6 +341,11 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
             requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
                     "Error recovering request state"));
             throw new ProfileException("Error recovering request state", e);
             requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
                     "Error recovering request state"));
             throw new ProfileException("Error recovering request state", e);
+        } catch (MetadataProviderException e) {
+            log.error("Unable to locate metadata for asserting or relying party");
+            requestContext
+                    .setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null, "Error locating party metadata"));
+            throw new ProfileException("Error locating party metadata");
         }
     }
 
         }
     }
 
@@ -343,7 +361,7 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
 
         AuthnContext authnContext = buildAuthnContext(requestContext);
 
 
         AuthnContext authnContext = buildAuthnContext(requestContext);
 
-        AuthnStatement statement = getAuthnStatementBuilder().buildObject();
+        AuthnStatement statement = authnStatementBuilder.buildObject();
         statement.setAuthnContext(authnContext);
         statement.setAuthnInstant(loginContext.getAuthenticationInstant());
 
         statement.setAuthnContext(authnContext);
         statement.setAuthnInstant(loginContext.getAuthenticationInstant());
 
@@ -355,8 +373,7 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
                     loginContext.getAuthenticationDuration()));
         }
 
                     loginContext.getAuthenticationDuration()));
         }
 
-        // TODO
-        statement.setSubjectLocality(null);
+        statement.setSubjectLocality(buildSubjectLocality(requestContext));
 
         return statement;
     }
 
         return statement;
     }
@@ -369,16 +386,16 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
      * @return the built authn context
      */
     protected AuthnContext buildAuthnContext(SSORequestContext requestContext) {
      * @return the built authn context
      */
     protected AuthnContext buildAuthnContext(SSORequestContext requestContext) {
-        AuthnContext authnContext = getAuthnContextBuilder().buildObject();
+        AuthnContext authnContext = authnContextBuilder.buildObject();
 
         Saml2LoginContext loginContext = requestContext.getLoginContext();
 
         Saml2LoginContext loginContext = requestContext.getLoginContext();
-        AuthnRequest authnRequest = requestContext.getSamlRequest();
+        AuthnRequest authnRequest = requestContext.getInboundSAMLMessage();
         RequestedAuthnContext requestedAuthnContext = authnRequest.getRequestedAuthnContext();
         if (requestedAuthnContext != null) {
             if (requestedAuthnContext.getAuthnContextClassRefs() != null) {
                 for (AuthnContextClassRef classRef : requestedAuthnContext.getAuthnContextClassRefs()) {
                     if (classRef.getAuthnContextClassRef().equals(loginContext.getAuthenticationMethod())) {
         RequestedAuthnContext requestedAuthnContext = authnRequest.getRequestedAuthnContext();
         if (requestedAuthnContext != null) {
             if (requestedAuthnContext.getAuthnContextClassRefs() != null) {
                 for (AuthnContextClassRef classRef : requestedAuthnContext.getAuthnContextClassRefs()) {
                     if (classRef.getAuthnContextClassRef().equals(loginContext.getAuthenticationMethod())) {
-                        AuthnContextClassRef ref = getAuthnContextClassRefBuilder().buildObject();
+                        AuthnContextClassRef ref = authnContextClassRefBuilder.buildObject();
                         ref.setAuthnContextClassRef(loginContext.getAuthenticationMethod());
                         authnContext.setAuthnContextClassRef(ref);
                     }
                         ref.setAuthnContextClassRef(loginContext.getAuthenticationMethod());
                         authnContext.setAuthnContextClassRef(ref);
                     }
@@ -386,14 +403,14 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
             } else if (requestedAuthnContext.getAuthnContextDeclRefs() != null) {
                 for (AuthnContextDeclRef declRef : requestedAuthnContext.getAuthnContextDeclRefs()) {
                     if (declRef.getAuthnContextDeclRef().equals(loginContext.getAuthenticationMethod())) {
             } else if (requestedAuthnContext.getAuthnContextDeclRefs() != null) {
                 for (AuthnContextDeclRef declRef : requestedAuthnContext.getAuthnContextDeclRefs()) {
                     if (declRef.getAuthnContextDeclRef().equals(loginContext.getAuthenticationMethod())) {
-                        AuthnContextDeclRef ref = getAuthnContextDeclRefBuilder().buildObject();
+                        AuthnContextDeclRef ref = authnContextDeclRefBuilder.buildObject();
                         ref.setAuthnContextDeclRef(loginContext.getAuthenticationMethod());
                         authnContext.setAuthnContextDeclRef(ref);
                     }
                 }
             }
         } else {
                         ref.setAuthnContextDeclRef(loginContext.getAuthenticationMethod());
                         authnContext.setAuthnContextDeclRef(ref);
                     }
                 }
             }
         } else {
-            AuthnContextDeclRef ref = getAuthnContextDeclRefBuilder().buildObject();
+            AuthnContextDeclRef ref = authnContextDeclRefBuilder.buildObject();
             ref.setAuthnContextDeclRef(loginContext.getAuthenticationMethod());
             authnContext.setAuthnContextDeclRef(ref);
         }
             ref.setAuthnContextDeclRef(loginContext.getAuthenticationMethod());
             authnContext.setAuthnContextDeclRef(ref);
         }
@@ -402,53 +419,46 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
     }
 
     /**
     }
 
     /**
-     * Encodes the request's SAML response and writes it to the servlet response.
+     * Constructs the subject locality for the authentication statement.
      * 
      * 
-     * @param requestContext current request context
+     * @param requestContext curent request context
      * 
      * 
-     * @throws ProfileException thrown if no message encoder is registered for this profiles binding
+     * @return subject locality for the authentication statement
      */
      */
-    protected void encodeResponse(SSORequestContext 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(encodingBinding);
-        if (encoder == null) {
-            log.error("No response encoder was registered for binding type: " + encodingBinding);
-            throw new ProfileException("No response encoder was registered for binding type: " + encodingBinding);
-        }
+    protected SubjectLocality buildSubjectLocality(SSORequestContext requestContext) {
+        HTTPInTransport transport = (HTTPInTransport) requestContext.getInboundMessageTransport();
+        SubjectLocality subjectLocality = subjectLocalityBuilder.buildObject();
+        subjectLocality.setAddress(transport.getPeerAddress());
+        subjectLocality.setDNSName(transport.getPeerDomainName());
 
 
-        super.populateMessageEncoder(encoder);
-        encoder.setResponse(requestContext.getProfileResponse().getRawResponse());
-        encoder.setSamlMessage(requestContext.getSamlResponse());
-        requestContext.setMessageEncoder(encoder);
+        return subjectLocality;
+    }
 
 
-        try {
-            encoder.encode();
-        } catch (BindingException e) {
-            throw new ProfileException("Unable to encode response to relying party: "
-                    + requestContext.getRelyingPartyId(), e);
-        }
+    /**
+     * Selects the appropriate endpoint for the relying party and stores it in the request context.
+     * 
+     * @param requestContext current request context
+     * 
+     * @return Endpoint selected from the information provided in the request context
+     */
+    protected Endpoint selectEndpoint(SSORequestContext requestContext) {
+        AuthnResponseEndpointSelector endpointSelector = new AuthnResponseEndpointSelector();
+        endpointSelector.setEndpointType(AssertionConsumerService.DEFAULT_ELEMENT_NAME);
+        endpointSelector.setMetadataProvider(getMetadataProvider());
+        endpointSelector.setEntityMetadata(requestContext.getPeerEntityMetadata());
+        endpointSelector.setEntityRoleMetadata(requestContext.getPeerEntityRoleMetadata());
+        endpointSelector.setSamlRequest(requestContext.getInboundSAMLMessage());
+        endpointSelector.getSupportedIssuerBindings().addAll(getSupportedOutboundBindings());
+        return endpointSelector.selectEndpoint();
     }
 
     /** Represents the internal state of a SAML 2.0 SSO Request while it's being processed by the IdP. */
     }
 
     /** Represents the internal state of a SAML 2.0 SSO Request while it's being processed by the IdP. */
-    protected class SSORequestContext extends SAML2ProfileRequestContext<AuthnRequest, Response, SSOConfiguration> {
+    protected class SSORequestContext extends BaseSAML2ProfileRequestContext<AuthnRequest, Response, SSOConfiguration> {
 
         /** Current login context. */
         private Saml2LoginContext loginContext;
 
         /**
 
         /** Current login context. */
         private Saml2LoginContext loginContext;
 
         /**
-         * Constructor.
-         * 
-         * @param request current profile request
-         * @param response current profile response
-         */
-        public SSORequestContext(ProfileRequest<ServletRequest> request, ProfileResponse<ServletResponse> response) {
-            super(request, response);
-        }
-
-        /**
          * Gets the current login context.
          * 
          * @return current login context
          * Gets the current login context.
          * 
          * @return current login context