More refactoring, moving more into the abstract SAML 2 profile handler
authorlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Thu, 7 Jun 2007 23:30:35 +0000 (23:30 +0000)
committerlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Thu, 7 Jun 2007 23:30:35 +0000 (23:30 +0000)
Better handling of errors, more informative StatusMessage
Rework authentication request profile handler (not yet tested)

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

src/edu/internet2/middleware/shibboleth/idp/authn/Saml2LoginContext.java
src/edu/internet2/middleware/shibboleth/idp/profile/AbstractSAMLProfileHandler.java
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AbstractAuthenticationRequest.java [deleted file]
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/AuthenticationRequestBrowserPost.java [deleted file]
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AuthenticationRequestProfileHandler.java

index d3bc207..174bf82 100644 (file)
@@ -193,5 +193,4 @@ public class Saml2LoginContext extends LoginContext implements Serializable {
             throw new UnmarshallingException("Unable to read serialized authentication request");
         }
     }
-
 }
\ No newline at end of file
index 79c226a..0fcac40 100644 (file)
@@ -23,6 +23,7 @@ import javax.xml.namespace.QName;
 
 import org.apache.log4j.Logger;
 import org.opensaml.common.IdentifierGenerator;
+import org.opensaml.common.SAMLObject;
 import org.opensaml.common.binding.decoding.MessageDecoderFactory;
 import org.opensaml.common.binding.encoding.MessageEncoderFactory;
 import org.opensaml.saml2.metadata.provider.MetadataProvider;
@@ -152,8 +153,10 @@ public abstract class AbstractSAMLProfileHandler extends
     
     /**
      * Contextual object used to accumlate information as profile requests are being processed.
+     * 
+     * @param <StatusType> type of Status object
      */
-    protected class SAMLProfileRequestContext extends ShibbolethProfileRequestContext {
+    protected class SAMLProfileRequestContext<StatusType extends SAMLObject> extends ShibbolethProfileRequestContext {
         
         /** Role descriptor name that the asserting party is operating in. */
         private QName assertingPartyRole;
diff --git a/src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AbstractAuthenticationRequest.java b/src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AbstractAuthenticationRequest.java
deleted file mode 100644 (file)
index 031cfef..0000000
+++ /dev/null
@@ -1,1005 +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 java.io.IOException;
-import java.io.InputStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.LinkedList;
-import java.util.List;
-
-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.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-import org.apache.log4j.Logger;
-import org.joda.time.DateTime;
-import org.opensaml.Configuration;
-import org.opensaml.common.SAMLObjectBuilder;
-import org.opensaml.common.binding.BindingException;
-import org.opensaml.common.binding.encoding.MessageEncoder;
-import org.opensaml.saml2.core.Assertion;
-import org.opensaml.saml2.core.AuthnContext;
-import org.opensaml.saml2.core.AuthnContextClassRef;
-import org.opensaml.saml2.core.AuthnContextDeclRef;
-import org.opensaml.saml2.core.AuthnRequest;
-import org.opensaml.saml2.core.AuthnStatement;
-import org.opensaml.saml2.core.GetComplete;
-import org.opensaml.saml2.core.IDPEntry;
-import org.opensaml.saml2.core.IDPList;
-import org.opensaml.saml2.core.Issuer;
-import org.opensaml.saml2.core.RequestedAuthnContext;
-import org.opensaml.saml2.core.Response;
-import org.opensaml.saml2.core.Scoping;
-import org.opensaml.saml2.core.Status;
-import org.opensaml.saml2.core.StatusCode;
-import org.opensaml.saml2.core.Subject;
-import org.opensaml.saml2.core.SubjectConfirmation;
-import org.opensaml.saml2.metadata.AssertionConsumerService;
-import org.opensaml.saml2.metadata.Endpoint;
-import org.opensaml.saml2.metadata.RoleDescriptor;
-import org.opensaml.saml2.metadata.SPSSODescriptor;
-import org.opensaml.saml2.metadata.provider.MetadataProvider;
-import org.opensaml.saml2.metadata.provider.MetadataProviderException;
-import org.opensaml.xml.io.Unmarshaller;
-import org.opensaml.xml.io.UnmarshallingException;
-import org.opensaml.xml.parse.BasicParserPool;
-import org.opensaml.xml.parse.ParserPool;
-import org.opensaml.xml.parse.XMLParserException;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-
-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.AbstractSAML2ProfileConfiguration;
-import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml2.SSOConfiguration;
-import edu.internet2.middleware.shibboleth.idp.authn.AuthenticationManager;
-import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
-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))) {
-                return false;
-            }
-
-            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))) {
-                return false;
-            }
-
-            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))) {
-                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))) {
-                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))) {
-                return false;
-            }
-
-            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))) {
-                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);
-    }
-
-    /**
-     * Set the Authentication Mananger.
-     * 
-     * @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.
-     */
-    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));
-
-            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.
-     * 
-     * @return The populated Response object.
-     */
-    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>
-     */
-    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.
-     */
-    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);
-        } 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);
-        } 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);
-        }
-    }
-
-    /**
-     * 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,
-            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()
-                    + " : 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).
-     * 
-     * @return An AuthnCtx object on success or <code>null</code> on failure.
-     */
-    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();
-
-        if (authnClasses != null) {
-            for (AuthnContextClassRef classRef : authnClasses) {
-                if (classRef != null) {
-                    String s = classRef.getAuthnContextClassRef();
-                    if (s != null && authnMethod.equals(s)) {
-                        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();
-                        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.
-     */
-    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());
-
-        // check for nameID constraints.
-        verifySubject(requestContext);
-    }
-
-    /**
-     * Get and verify the Subject element.
-     * 
-     * @param requestContext The context for the current request.
-     * 
-     * @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()
-                    + " is malformed: It does not contain a Subject.");
-            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();
-        if (confMethods != null || confMethods.size() > 0) {
-            log.error("SAML 2 AuthnRequest " + authnRequest.getID()
-                    + " is malformed: It contains SubjectConfirmation elements.");
-            failureStatus = buildStatus(StatusCode.REQUESTER_URI, null, "SAML 2 AuthnRequest " + authnRequest.getID()
-                    + " is malformed: It 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.
-     * 
-     * 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 {
-
-        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);
-        }
-
-        ssoConfig = (SSOConfiguration) temp;
-
-        try {
-            spDescriptor = getMetadataProvider().getEntityDescriptor(relyingParty.getRelyingPartyId())
-                    .getSPSSODescriptor(SAML20_PROTOCOL_URI);
-        } catch (MetadataProviderException ex) {
-            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);
-        }
-
-        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);
-        }
-
-        // 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.
-     */
-    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");
-            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);
-        }
-
-        SPSSODescriptor spDescriptor;
-        try {
-            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);
-        }
-
-        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 "
-                                + 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) {
-            if (acsURL.equals(acs.getLocation())) {
-                if (protocolBinding != null) {
-                    if (protocolBinding.equals(acs.getBinding())) {
-                        requestContext.setAssertionConsumerService(acs);
-                        return;
-                    }
-                }
-            }
-        }
-
-        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.
-     */
-    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) {
-                String s = entry.getProviderID();
-                if (s != null) {
-                    idpEntries.add(s);
-                }
-            }
-        }
-
-        // If the IDPList is incomplete, retrieve the complete list
-        // and add the entries to idpEntries.
-        GetComplete getComplete = idpList.getGetComplete();
-        IDPList referencedIdPs = getCompleteIDPList(getComplete);
-        if (referencedIdPs != null) {
-            List<IDPEntry> referencedIDPEntries = referencedIdPs.getIDPEntrys();
-            if (referencedIDPEntries != null) {
-                for (IDPEntry entry : referencedIDPEntries) {
-                    String s = entry.getProviderID();
-                    if (s != null) {
-                        idpEntries.add(s);
-                    }
-                }
-            }
-        }
-
-        // iterate over all the IDPEntries we've gathered,
-        // and check if we're in scope.
-        for (String requestProviderId : idpEntries) {
-            if (providerId.equals(requestProviderId)) {
-                log.debug("Found Scoping match for IdP: (" + providerId + ")");
-                return;
-            }
-        }
-
-        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);
-    }
-
-    /**
-     * 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.
-     */
-    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);
-            idpList = (IDPList) unmarshaller.unmarshall(docElement);
-
-        } catch (MalformedURLException 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);
-        } catch (XMLParserException 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);
-        } finally {
-
-            if (istream != null) {
-                try {
-                    istream.close();
-                } catch (IOException ex) {
-                    // pass
-                }
-            }
-        }
-
-        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 {
-
-        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());
-        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);
-        }
-
-    }
-
-}
index 63b2dbb..9c0b720 100644 (file)
@@ -33,6 +33,8 @@ import org.opensaml.common.xml.SAMLConstants;
 import org.opensaml.log.Level;
 import org.opensaml.saml2.core.Advice;
 import org.opensaml.saml2.core.Assertion;
+import org.opensaml.saml2.core.AttributeQuery;
+import org.opensaml.saml2.core.AttributeStatement;
 import org.opensaml.saml2.core.Audience;
 import org.opensaml.saml2.core.AudienceRestriction;
 import org.opensaml.saml2.core.AuthnRequest;
@@ -67,12 +69,16 @@ import edu.internet2.middleware.shibboleth.common.attribute.AttributeRequestExce
 import edu.internet2.middleware.shibboleth.common.attribute.BaseAttribute;
 import edu.internet2.middleware.shibboleth.common.attribute.encoding.AttributeEncoder;
 import edu.internet2.middleware.shibboleth.common.attribute.encoding.AttributeEncodingException;
+import edu.internet2.middleware.shibboleth.common.attribute.provider.SAML2AttributeAuthority;
+import edu.internet2.middleware.shibboleth.common.attribute.provider.ShibbolethSAMLAttributeRequestContext;
 import edu.internet2.middleware.shibboleth.common.log.AuditLogEntry;
 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
 import edu.internet2.middleware.shibboleth.common.profile.ProfileRequest;
 import edu.internet2.middleware.shibboleth.common.profile.ProfileResponse;
 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml2.AbstractSAML2ProfileConfiguration;
 import edu.internet2.middleware.shibboleth.idp.profile.AbstractSAMLProfileHandler;
+import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
+import edu.internet2.middleware.shibboleth.idp.session.Session;
 
 /**
  * Common implementation details for profile handlers.
@@ -291,10 +297,9 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      * @return the built response
      * 
      * @throws ProfileException thrown if there is a problem creating the SAML response
-     * @throws AttributeRequestException thrown if there is a problem resolving attributes
      */
     protected Response buildResponse(SAML2ProfileRequestContext requestContext, Subject assertionSubject,
-            List<Statement> statements) throws ProfileException, AttributeRequestException {
+            List<Statement> statements) throws ProfileException {
 
         DateTime issueInstant = new DateTime();
 
@@ -421,6 +426,128 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
     }
 
     /**
+     * Executes a query for attributes and builds a SAML attribute statement from the results.
+     * 
+     * @param requestContext current request context
+     * 
+     * @return attribute statement resulting from the query
+     * 
+     * @throws ProfileException thrown if there is a problem making the query
+     */
+    protected AttributeStatement buildAttributeStatement(SAML2ProfileRequestContext requestContext)
+            throws ProfileException {
+
+        if (log.isDebugEnabled()) {
+            log.debug("Creating attribute statement in response to SAML request "
+                    + requestContext.getSamlRequest().getID() + " from relying party "
+                    + requestContext.getRelyingPartyId());
+        }
+
+        AbstractSAML2ProfileConfiguration profileConfiguration = requestContext.getProfileConfiguration();
+        SAML2AttributeAuthority attributeAuthority = profileConfiguration.getAttributeAuthority();
+
+        if (log.isDebugEnabled()) {
+            log.debug("Resolving principal name for subject of SAML request " + requestContext.getSamlRequest().getID()
+                    + " from relying party " + requestContext.getRelyingPartyId());
+        }
+
+        try {
+            if (log.isDebugEnabled()) {
+                log.debug("Resolving attributes for principal " + requestContext.getPrincipalName()
+                        + " of SAML request " + requestContext.getSamlRequest().getID() + " from relying party "
+                        + requestContext.getRelyingPartyId());
+            }
+            Map<String, BaseAttribute> principalAttributes = attributeAuthority
+                    .getAttributes(buildAttributeRequestContext(requestContext));
+
+            requestContext.setPrincipalAttributes(principalAttributes);
+
+            if (requestContext.getSamlRequest() instanceof AttributeQuery) {
+                return attributeAuthority.buildAttributeStatement((AttributeQuery) requestContext.getSamlRequest(),
+                        principalAttributes.values());
+            } else {
+                return attributeAuthority.buildAttributeStatement(null, principalAttributes.values());
+            }
+        } catch (AttributeRequestException e) {
+            log.error("Error resolving attributes for SAML request " + requestContext.getSamlRequest().getID()
+                    + " from relying party " + requestContext.getRelyingPartyId(), e);
+            requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null, "Error resolving attributes"));
+            throw new ProfileException("Error resolving attributes for SAML request "
+                    + requestContext.getSamlRequest().getID() + " from relying party "
+                    + requestContext.getRelyingPartyId(), e);
+        }
+    }
+
+    /**
+     * Resolves the principal name of the subject of the request.
+     * 
+     * @param requestContext current request context
+     * 
+     * @throws ProfileException thrown if the principal name can not be resolved
+     */
+    protected void resolvePrincipal(SAML2ProfileRequestContext requestContext) throws ProfileException {
+        AbstractSAML2ProfileConfiguration profileConfiguration = requestContext.getProfileConfiguration();
+        SAML2AttributeAuthority attributeAuthority = profileConfiguration.getAttributeAuthority();
+
+        if (log.isDebugEnabled()) {
+            log.debug("Resolving principal name for subject of SAML request " + requestContext.getSamlRequest().getID()
+                    + " from relying party " + requestContext.getRelyingPartyId());
+        }
+
+        try {
+            String principal = attributeAuthority.getPrincipal(buildAttributeRequestContext(requestContext));
+            requestContext.setPrincipalName(principal);
+        } catch (AttributeRequestException e) {
+            log.error("Error resolving attributes for SAML request " + requestContext.getSamlRequest().getID()
+                    + " from relying party " + requestContext.getRelyingPartyId(), e);
+            requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.UNKNOWN_PRINCIPAL_URI,
+                    "Error resolving principal"));
+            throw new ProfileException("Error resolving attributes for SAML request "
+                    + requestContext.getSamlRequest().getID() + " from relying party "
+                    + requestContext.getRelyingPartyId(), e);
+        }
+    }
+
+    /**
+     * Creates an attribute query context from the current profile request context.
+     * 
+     * @param requestContext current profile request
+     * 
+     * @return created query context
+     */
+    protected ShibbolethSAMLAttributeRequestContext<NameID, AttributeQuery> buildAttributeRequestContext(
+            SAML2ProfileRequestContext requestContext) {
+
+        ShibbolethSAMLAttributeRequestContext<NameID, AttributeQuery> queryContext;
+        if (requestContext.getSamlRequest() instanceof AttributeQuery) {
+            queryContext = new ShibbolethSAMLAttributeRequestContext<NameID, AttributeQuery>(getMetadataProvider(),
+                    requestContext.getRelyingPartyConfiguration(), (AttributeQuery) requestContext.getSamlRequest());
+        } else {
+            queryContext = new ShibbolethSAMLAttributeRequestContext<NameID, AttributeQuery>(getMetadataProvider(),
+                    requestContext.getRelyingPartyConfiguration());
+        }
+
+        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;
+    }
+
+    /**
      * Signs the given assertion if either the current profile configuration or the relying party configuration contains
      * signing credentials.
      * 
@@ -501,11 +628,11 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      * @param topLevelCode The top-level status code. Should be from saml-core-2.0-os, sec. 3.2.2.2
      * @param secondLevelCode An optional second-level failure code. Should be from saml-core-2.0-is, sec 3.2.2.2. If
      *            null, no second-level Status element will be set.
-     * @param secondLevelFailureMessage An optional second-level failure message
+     * @param failureMessage An optional second-level failure message
      * 
      * @return a Status object.
      */
-    protected Status buildStatus(String topLevelCode, String secondLevelCode, String secondLevelFailureMessage) {
+    protected Status buildStatus(String topLevelCode, String secondLevelCode, String failureMessage) {
         Status status = getStatusBuilder().buildObject();
 
         StatusCode statusCode = getStatusCodeBuilder().buildObject();
@@ -518,9 +645,9 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
             statusCode.setStatusCode(secondLevelStatusCode);
         }
 
-        if (secondLevelFailureMessage != null) {
+        if (failureMessage != null) {
             StatusMessage msg = getStatusMessageBuilder().buildObject();
-            msg.setMessage(secondLevelFailureMessage);
+            msg.setMessage(failureMessage);
             status.setStatusMessage(msg);
         }
 
@@ -597,10 +724,14 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
                     }
                 }
             } catch (AttributeEncodingException e) {
+                requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
+                        "Unable to construct NameID"));
                 throw new ProfileException("Unable to encode NameID attribute", e);
             }
         }
 
+        requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.INVALID_NAMEID_POLICY_URI,
+                "Unable to construct NameID"));
         throw new ProfileException("No principal attributes support NameID construction");
     }
 
@@ -629,6 +760,8 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
                     if (assertingPartySupportedFormats.contains(nameFormat)) {
                         nameFormats.add(nameFormat);
                     } else {
+                        requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI,
+                                StatusCode.INVALID_NAMEID_POLICY_URI, "Format not supported: " + nameFormat));
                         throw new ProfileException("NameID format required by relying party is not supported");
                     }
                 }
@@ -649,6 +782,8 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
             return nameFormats;
 
         } catch (MetadataProviderException e) {
+            requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
+                    "Unable to lookup entity metadata"));
             throw new ProfileException("Unable to determine lookup entity metadata", e);
         }
     }
@@ -687,21 +822,15 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
      * Constructs an SAML response message carrying a request error.
      * 
      * @param requestContext current request context
-     * @param topLevelCode The top-level status code. Should be from saml-core-2.0-os, sec. 3.2.2.2
-     * @param secondLevelCode An optional second-level failure code. Should be from saml-core-2.0-is, sec 3.2.2.2. If
-     *            null, no second-level Status element will be set.
-     * @param secondLevelFailureMessage An optional second-level failure message
      * 
      * @return the constructed error response
      */
-    protected Response buildErrorResponse(SAML2ProfileRequestContext requestContext, String topLevelCode,
-            String secondLevelCode, String secondLevelFailureMessage) {
+    protected Response buildErrorResponse(SAML2ProfileRequestContext requestContext) {
         Response samlResponse = getResponseBuilder().buildObject();
         samlResponse.setIssueInstant(new DateTime());
         populateStatusResponse(requestContext, samlResponse);
 
-        Status status = buildStatus(topLevelCode, secondLevelCode, secondLevelFailureMessage);
-        samlResponse.setStatus(status);
+        samlResponse.setStatus(requestContext.getFailureStatus());
 
         return samlResponse;
     }
@@ -747,6 +876,9 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
         /** The NameID of the subject of this request. */
         private NameID subjectNameID;
 
+        /** The request failure status. */
+        private Status failureStatus;
+
         /**
          * Constructor.
          * 
@@ -829,5 +961,23 @@ public abstract class AbstractSAML2ProfileHandler extends AbstractSAMLProfileHan
         public void setSamlResponse(ResponseType response) {
             samlResponse = response;
         }
+
+        /**
+         * Gets the status reflecting a request failure.
+         * 
+         * @return status reflecting a request failure
+         */
+        public Status getFailureStatus() {
+            return failureStatus;
+        }
+
+        /**
+         * Sets the status reflecting a request failure.
+         * 
+         * @param status status reflecting a request failure
+         */
+        public void setFailureStatus(Status status) {
+            failureStatus = status;
+        }
     }
 }
\ No newline at end of file
index 11ec69d..5134346 100644 (file)
@@ -86,15 +86,8 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
 
             // create the SAML response
             samlResponse = buildResponse(requestContext, assertionSubject, statements);
-        } catch (SecurityPolicyException e) {
-            samlResponse = buildErrorResponse(requestContext, StatusCode.REQUESTER_URI, StatusCode.REQUEST_DENIED_URI,
-                    e.getMessage());
-        } 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());
+            samlResponse = buildErrorResponse(requestContext);
         }
 
         requestContext.setSamlResponse(samlResponse);
@@ -109,10 +102,8 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
      * @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 {
+    protected void decodeRequest(AttributeQueryContext requestContext) throws ProfileException {
         if (log.isDebugEnabled()) {
             log.debug("Decoding incomming request");
         }
@@ -134,7 +125,13 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
             }
         } catch (BindingException e) {
             log.error("Error decoding attribute query message", e);
+            requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null, "Error decoding message"));
             throw new ProfileException("Error decoding attribute query message");
+        } catch (SecurityPolicyException e) {
+            log.error("Message did not meet security policy requirements", e);
+            requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.REQUEST_DENIED_URI,
+                    "Message did not meet security policy requirements"));
+            throw new ProfileException("Message did not meet security policy requirements", e);
         } finally {
             // Set as much information as can be retrieved from the decoded message
             SAMLSecurityPolicy securityPolicy = requestContext.getMessageDecoder().getSecurityPolicy();
@@ -157,97 +154,6 @@ public class AttributeQueryProfileHandler extends AbstractSAML2ProfileHandler {
     }
 
     /**
-     * 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 "
-                    + requestContext.getSamlRequest().getID() + " from relying party "
-                    + requestContext.getRelyingPartyId());
-        }
-
-        try {
-            AttributeQueryConfiguration profileConfiguration = requestContext.getProfileConfiguration();
-            if (profileConfiguration == null) {
-                log.error("No SAML 2 attribute query profile configuration is defined for relying party: "
-                        + requestContext.getRelyingPartyId());
-                throw new AttributeRequestException("SAML 2 attribute query is not configured for this relying party");
-            }
-
-            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));
-
-            requestContext.setPrincipalAttributes(principalAttributes);
-
-            return attributeAuthority.buildAttributeStatement(requestContext.getSamlRequest(), principalAttributes
-                    .values());
-        } catch (AttributeRequestException e) {
-            log.error("Error resolving attributes for SAML request " + requestContext.getSamlRequest().getID()
-                    + " 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<NameID, AttributeQuery> buildAttributeRequestContext(
-            AttributeQueryContext requestContext) {
-
-        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);
-            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
diff --git a/src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AuthenticationRequestBrowserPost.java b/src/edu/internet2/middleware/shibboleth/idp/profile/saml2/AuthenticationRequestBrowserPost.java
deleted file mode 100644 (file)
index ac10e20..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * 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 javax.servlet.http.HttpServletRequest;
-
-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.idp.authn.LoginContext;
-
-import org.apache.log4j.Logger;
-import org.joda.time.DateTime;
-import org.opensaml.common.SAMLObject;
-import org.opensaml.common.binding.BindingException;
-import org.opensaml.common.binding.decoding.MessageDecoder;
-import org.opensaml.saml2.core.AuthnRequest;
-import org.opensaml.saml2.core.Response;
-import org.opensaml.ws.security.SecurityPolicyException;
-
-/**
- * Browser POST binding for SAML 2 AuthenticationRequest.
- */
-public class AuthenticationRequestBrowserPost extends AbstractAuthenticationRequest {
-    
-    /** Class logger. */
-    private static final Logger log = Logger.getLogger(AuthenticationRequestBrowserPost.class);
-    
-    /** SAML 2 Profile ID. */
-    protected static final String PROFILE_ID = "urn:oasis:names:tc:SAML:2.0:profiles:SSO:browser";
-    
-    /** SAML 2 Binding URI. */
-    protected static final String BINDING_URI = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST";
-    
-    /** SubjectConfirmation method for Web Browser SSO profile. */
-    protected static final String SUBJ_CONF_METHOD_URI = "urn:oasis:namurn:oasis:names:tc:SAML:2.0:cm:bearer";
-    
-    /** Constructor. */
-    public AuthenticationRequestBrowserPost() {
-        super();
-    }
-    
-    /** {@inheritDoc} */
-    public String getProfileId() {
-        return PROFILE_ID;
-    }
-    
-    /** {@inheritDoc} */
-    public void processRequest(final ProfileRequest<ServletRequest> request,
-            final ProfileResponse<ServletResponse> response)
-            throws ProfileException {
-        
-        // Only http servlets are supported for now.
-        if (!(request.getRawRequest() instanceof HttpServletRequest)) {
-            log.error("Received a non-HTTP request");
-            throw new ProfileException("Received a non-HTTP request");
-        }
-        
-        // This method is called twice.
-        // On the first time, there will be no AuthenticationRequestContext object. We redirect control to the
-        // AuthenticationManager to authenticate the user. The AuthenticationManager then redirects control
-        // back to this servlet. On the "return leg" connection, there will be a AuthenticationRequestContext object.
-        
-        HttpServletRequest req = (HttpServletRequest) request.getRawRequest();
-        Object o = req.getSession().getAttribute(REQUEST_CONTEXT_SESSION_KEY);
-        if (o != null && !(o instanceof AuthenticationRequestContext)) {
-            log.error("SAML 2 AuthnRequest: Invalid session data found for AuthenticationRequestContext");
-            throw new ProfileException("SAML 2 AuthnRequest: Invalid session data found for AuthenticationRequestContext");
-        }
-        
-        if (o == null) {
-            setupNewRequest(request, response);
-        } else {
-            
-            AuthenticationRequestContext requestContext = (AuthenticationRequestContext)o;
-            
-            // clean up the HttpSession.
-            requestContext.getHttpSession().removeAttribute(REQUEST_CONTEXT_SESSION_KEY);
-            requestContext.getHttpSession().removeAttribute(LoginContext.LOGIN_CONTEXT_KEY);
-            
-            finishProcessingRequest(requestContext);
-        }
-    }
-    
-    /**
-     * Begin processing a SAML 2.0 AuthnRequest.
-     *
-     * This ensures that the request is well-formed and that
-     * appropriate metadata can be found for the SP.
-     * Once these conditions are met, control is passed to
-     * the AuthenticationManager to authenticate the user.
-     * 
-     * @param request The ProfileRequest.
-     * @param response The ProfileResponse
-     * 
-     * @throws ProfileException On error.
-     */
-    protected void setupNewRequest(final ProfileRequest<ServletRequest> request,
-            final ProfileResponse<ServletResponse> response) throws ProfileException {
-        
-        // If the user hasn't been authenticated, validate the AuthnRequest
-        // and redirect to AuthenticationManager to authenticate the user.
-        
-        AuthenticationRequestContext requestContext = new AuthenticationRequestContext();
-        
-        requestContext.setProfileRequest(request);
-        requestContext.setProfileResponse(response);
-        
-        try {
-            // decode the AuthnRequest
-            MessageDecoder<ServletRequest> decoder = getMessageDecoderFactory().getMessageDecoder(BINDING_URI);
-            if (decoder == null) {
-                log.error("SAML 2 AuthnRequest: No MessageDecoder registered for " + BINDING_URI);
-                throw new ProfileException("SAML 2 AuthnRequest: No MessageDecoder registered for " + BINDING_URI);
-            }
-            
-            decoder.setMetadataProvider(getMetadataProvider());
-            populateMessageDecoder(decoder);
-            decoder.decode();
-            
-            SAMLObject samlObject = decoder.getSAMLMessage();
-            if (!(samlObject instanceof AuthnRequest)) {
-                log.error("SAML 2 AuthnRequest: Received message is not a SAML 2 Authentication Request");
-                throw new ProfileException("SAML 2 AuthnRequest: Received message is not a SAML 2 Authentication Request");
-            }
-            
-            requestContext.setAuthnRequest((AuthnRequest) samlObject);
-            requestContext.setIssuer(decoder.getSecurityPolicy().getIssuer());
-            validateRequestAgainstMetadata(requestContext);
-            verifyAuthnRequest(requestContext);
-            authenticateUser(requestContext);
-            
-        } catch (BindingException ex) {
-            log.error("SAML 2 Authentication Request: Unable to decode SAML 2 Authentication Request", ex);
-            throw new ProfileException(
-                    "SAML 2 Authentication Request: Unable to decode SAML 2 Authentication Request", ex);
-        } catch (SecurityPolicyException ex) {
-            log.error("SAML 2 Authentication Request: Security error while decoding SAML 2 Authentication Request", ex);
-        } catch (AuthenticationRequestException ex) {
-            
-            // AuthN failed. Send the failure status.
-            requestContext.setResponse(buildResponse(requestContext.getAuthnRequest().getID(), 
-                    new DateTime(), requestContext.getIssuer(), ex.getStatus()));
-            encodeResponse(BINDING_URI, requestContext);
-        }
-    }
-    
-    /**
-     * Process the "return leg" of a SAML 2 Authentication Request.
-     *
-     * This evaluates the AuthenticationManager's LoginContext and generates an Authentication Assertion.
-     *
-     * @param requestContext The context for this request.
-     *
-     * @throws ProfileException On error.
-     */
-    protected void finishProcessingRequest(final AuthenticationRequestContext requestContext) throws ProfileException {
-        
-        // The user has already been authenticated,
-        // so generate an AuthenticationStatement.
-        evaluateRequest(requestContext);
-        encodeResponse(BINDING_URI, requestContext);
-    }
-    
-}
index b43266f..13a67fb 100644 (file)
 
 package edu.internet2.middleware.shibboleth.idp.profile.saml2;
 
+import java.io.IOException;
+import java.util.ArrayList;
+
+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 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.core.AuthnContext;
+import org.opensaml.saml2.core.AuthnContextClassRef;
+import org.opensaml.saml2.core.AuthnContextDeclRef;
 import org.opensaml.saml2.core.AuthnRequest;
+import org.opensaml.saml2.core.AuthnStatement;
+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.Subject;
+import org.opensaml.saml2.metadata.IDPSSODescriptor;
+import org.opensaml.saml2.metadata.SPSSODescriptor;
+import org.opensaml.ws.security.SecurityPolicyException;
+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.RelyingPartyConfiguration;
 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml2.SSOConfiguration;
 import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
+import edu.internet2.middleware.shibboleth.idp.authn.Saml2LoginContext;
 
 /**
  * SAML 2.0 authentication request profile handler.
  */
 public class AuthenticationRequestProfileHandler extends AbstractSAML2ProfileHandler {
 
+    /** Class logger. */
+    private final Logger log = Logger.getLogger(AuthenticationRequestProfileHandler.class);
+
+    /** Builder of AuthnStatement objects. */
+    private SAMLObjectBuilder<AuthnStatement> authnStatementBuilder;
+
+    /** Builder of AuthnContext objects. */
+    private SAMLObjectBuilder<AuthnContext> authnContextBuilder;
+
+    /** Builder of AuthnContextClassRef objects. */
+    private SAMLObjectBuilder<AuthnContextClassRef> authnContextClassRefBuilder;
+
+    /** Builder of AuthnContextDeclRef objects. */
+    private SAMLObjectBuilder<AuthnContextDeclRef> authnContextDeclRefBuilder;
+
+    /** URL of the authentication manager servlet. */
+    private String authenticationManagerURL;
+
+    /** URI of request decoder. */
+    private String decodingBinding;
+
+    /** URI of response encoder. */
+    private String encodingBinding;
+
+    /**
+     * Constructor.
+     * 
+     * @param decoder URI of the request decoder to use
+     * @param encoder URI of the response encoder to use
+     */
+    @SuppressWarnings("unchecked")
+    public AuthenticationRequestProfileHandler(String decoder, String encoder) {
+        super();
+
+        if (decoder == null || encoder == null) {
+            throw new IllegalArgumentException("Message decoding and encoding binding URI may not be null");
+        }
+
+        authnStatementBuilder = (SAMLObjectBuilder<AuthnStatement>) getBuilderFactory().getBuilder(
+                AuthnStatement.DEFAULT_ELEMENT_NAME);
+        authnContextBuilder = (SAMLObjectBuilder<AuthnContext>) getBuilderFactory().getBuilder(
+                AuthnContext.DEFAULT_ELEMENT_NAME);
+        authnContextClassRefBuilder = (SAMLObjectBuilder<AuthnContextClassRef>) getBuilderFactory().getBuilder(
+                AuthnContextClassRef.DEFAULT_ELEMENT_NAME);
+        authnContextDeclRefBuilder = (SAMLObjectBuilder<AuthnContextDeclRef>) getBuilderFactory().getBuilder(
+                AuthnContextDeclRef.DEFAULT_ELEMENT_NAME);
+    }
+
     /** {@inheritDoc} */
     public String getProfileId() {
         return "urn:mace:shibboleth:2.0:idp:profiles:saml2:request:authentication";
@@ -42,6 +118,271 @@ public class AuthenticationRequestProfileHandler extends AbstractSAML2ProfileHan
     public void processRequest(ProfileRequest<ServletRequest> request, ProfileResponse<ServletResponse> response)
             throws ProfileException {
 
+        HttpSession httpSession = ((HttpServletRequest) request.getRawRequest()).getSession(true);
+        if (httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY) == null) {
+            performAuthentication(request, response);
+        } else {
+            completeAuthenticationRequest(request, response);
+        }
+    }
+
+    /**
+     * 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
+     * 
+     * @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();
+
+        AuthnRequest authnRequest = null;
+        try {
+            MessageDecoder<ServletRequest> decoder = decodeRequest(request);
+            SAMLSecurityPolicy<ServletRequest> securityPolicy = decoder.getSecurityPolicy();
+
+            String relyingParty = securityPolicy.getIssuer();
+            authnRequest = (AuthnRequest) decoder.getSAMLMessage();
+
+            Saml2LoginContext loginContext = new Saml2LoginContext(relyingParty, authnRequest);
+            loginContext.setProfileHandlerURL(httpRequest.getRequestURI());
+
+            HttpSession httpSession = httpRequest.getSession();
+            httpSession.setAttribute(Saml2LoginContext.LOGIN_CONTEXT_KEY, loginContext);
+            RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(authenticationManagerURL);
+            dispatcher.forward(httpRequest, response.getRawResponse());
+        } 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);
+        } 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);
+        }
+    }
+
+    /**
+     * 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
+     * 
+     * @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);
+        Saml2LoginContext loginContext = (Saml2LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
+        AuthenticationRequestContext requestContext = buildRequestContext(loginContext, request, response);
+
+        Response samlResponse;
+        try {
+            if (!loginContext.getAuthenticationOK()) {
+                requestContext
+                        .setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.AUTHN_FAILED_URI, null));
+                throw new ProfileException("User failed authentication");
+            }
+
+            ArrayList<Statement> statements = new ArrayList<Statement>();
+            statements.add(buildAuthnStatement(requestContext));
+            statements.add(buildAttributeStatement(requestContext));
+
+            Subject assertionSubject = buildSubject(requestContext, "urn:oasis:names:tc:SAML:2.0:cm:bearer");
+
+            samlResponse = buildResponse(requestContext, assertionSubject, statements);
+        } catch (ProfileException e) {
+            samlResponse = buildErrorResponse(requestContext);
+        }
+
+        requestContext.setSamlResponse(samlResponse);
+        encodeResponse(requestContext);
+        writeAuditLogEntry(requestContext);
+    }
+
+    /**
+     * Creates an appropriate message decoder, populates it, and decodes the incoming request.
+     * 
+     * @param request current request
+     * 
+     * @return message decoder containing the decoded message and other stateful information
+     * 
+     * @throws ProfileException thrown if the incomming message failed decoding
+     */
+    protected MessageDecoder<ServletRequest> decodeRequest(ProfileRequest<ServletRequest> request)
+            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);
+        }
+
+        populateMessageDecoder(decoder);
+        decoder.setRequest(request.getRawRequest());
+        try {
+            decoder.decode();
+            return decoder;
+        } catch (BindingException e) {
+            log.error("Error decoding authentication request message", e);
+            throw new ProfileException("Error decoding authentication request message", e);
+        } catch (SecurityPolicyException e) {
+            log.error("Message did not meet security policy requirements", e);
+            throw new ProfileException("Message did not meet security policy requirements", e);
+        }
+    }
+
+    /**
+     * Creates an authentication request context from the current environmental information.
+     * 
+     * @param loginContext current login context
+     * @param request current request
+     * @param response current response
+     * 
+     * @return created authentication request context
+     * 
+     * @throws ProfileException thrown if there is a problem creating the context
+     */
+    protected AuthenticationRequestContext buildRequestContext(Saml2LoginContext loginContext,
+            ProfileRequest<ServletRequest> request, ProfileResponse<ServletResponse> response) throws ProfileException {
+        AuthenticationRequestContext requestContext = new AuthenticationRequestContext(request, response);
+
+        try {
+            String relyingPartyId = loginContext.getRelyingPartyId();
+            AuthnRequest authnRequest = loginContext.getAuthenticationRequest();
+
+            requestContext.setRelyingPartyId(relyingPartyId);
+
+            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);
+
+            return requestContext;
+        } catch (UnmarshallingException e) {
+            log.error("Unable to unmarshall authentication request context");
+            requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
+                    "Error recovering request state"));
+            throw new ProfileException("Error recovering request state", e);
+        }
+    }
+
+    /**
+     * Creates an authentication statement for the current request.
+     * 
+     * @param requestContext current request context
+     * 
+     * @return constructed authentication statement
+     */
+    protected AuthnStatement buildAuthnStatement(AuthenticationRequestContext requestContext) {
+        Saml2LoginContext loginContext = requestContext.getLoginContext();
+
+        AuthnContext authnContext = buildAuthnContext(requestContext);
+
+        AuthnStatement statement = authnStatementBuilder.buildObject();
+        statement.setAuthnContext(authnContext);
+        statement.setAuthnInstant(loginContext.getAuthenticationInstant());
+
+        // TODO
+        statement.setSessionIndex(null);
+
+        if (loginContext.getAuthenticationDuration() > 0) {
+            statement.setSessionNotOnOrAfter(loginContext.getAuthenticationInstant().plus(
+                    loginContext.getAuthenticationDuration()));
+        }
+
+        // TODO
+        statement.setSubjectLocality(null);
+
+        return statement;
+    }
+
+    /**
+     * Creates an {@link AuthnContext} for a succesful authentication request.
+     * 
+     * @param requestContext current request
+     * 
+     * @return the built authn context
+     */
+    protected AuthnContext buildAuthnContext(AuthenticationRequestContext requestContext) {
+        AuthnContext authnContext = authnContextBuilder.buildObject();
+
+        Saml2LoginContext loginContext = requestContext.getLoginContext();
+        AuthnRequest authnRequest = requestContext.getSamlRequest();
+        RequestedAuthnContext requestedAuthnContext = authnRequest.getRequestedAuthnContext();
+        if (requestedAuthnContext != null) {
+            if (requestedAuthnContext.getAuthnContextClassRefs() != null) {
+                for (AuthnContextClassRef classRef : requestedAuthnContext.getAuthnContextClassRefs()) {
+                    if (classRef.getAuthnContextClassRef().equals(loginContext.getAuthenticationMethod())) {
+                        AuthnContextClassRef ref = authnContextClassRefBuilder.buildObject();
+                        ref.setAuthnContextClassRef(loginContext.getAuthenticationMethod());
+                        authnContext.setAuthnContextClassRef(ref);
+                    }
+                }
+            } else if (requestedAuthnContext.getAuthnContextDeclRefs() != null) {
+                for (AuthnContextDeclRef declRef : requestedAuthnContext.getAuthnContextDeclRefs()) {
+                    if (declRef.getAuthnContextDeclRef().equals(loginContext.getAuthenticationMethod())) {
+                        AuthnContextDeclRef ref = authnContextDeclRefBuilder.buildObject();
+                        ref.setAuthnContextDeclRef(loginContext.getAuthenticationMethod());
+                        authnContext.setAuthnContextDeclRef(ref);
+                    }
+                }
+            }
+        } else {
+            AuthnContextDeclRef ref = authnContextDeclRefBuilder.buildObject();
+            ref.setAuthnContextDeclRef(loginContext.getAuthenticationMethod());
+            authnContext.setAuthnContextDeclRef(ref);
+        }
+
+        return authnContext;
+    }
+
+    /**
+     * 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(AuthenticationRequestContext 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);
+        }
+
+        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);
+        }
     }
 
     /**
@@ -50,8 +391,8 @@ public class AuthenticationRequestProfileHandler extends AbstractSAML2ProfileHan
     protected class AuthenticationRequestContext extends
             SAML2ProfileRequestContext<AuthnRequest, Response, SSOConfiguration> {
 
-        /** The IdP's LoginContext. */
-        private LoginContext loginContext;
+        /** Current login context. */
+        private Saml2LoginContext loginContext;
 
         /**
          * Constructor.
@@ -65,20 +406,20 @@ public class AuthenticationRequestProfileHandler extends AbstractSAML2ProfileHan
         }
 
         /**
-         * Gets the login context for this request.
+         * Gets the current login context.
          * 
-         * @return login context for this request
+         * @return current login context
          */
-        public LoginContext getLoginContext() {
+        public Saml2LoginContext getLoginContext() {
             return loginContext;
         }
 
         /**
-         * Sets the login context for this request.
+         * Sets the current login context.
          * 
-         * @param context login context for this request
+         * @param context current login context
          */
-        public void setLoginContext(LoginContext context) {
+        public void setLoginContext(Saml2LoginContext context) {
             loginContext = context;
         }
     }