Stop using request forwards between authentication engine and profile handler - SIDP-380
authorlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Mon, 16 Aug 2010 13:35:11 +0000 (13:35 +0000)
committerlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Mon, 16 Aug 2010 13:35:11 +0000 (13:35 +0000)
Make profile handler responsible for binding/unbinding LoginContext

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

doc/RELEASE-NOTES.txt
src/main/java/edu/internet2/middleware/shibboleth/idp/authn/AuthenticationEngine.java
src/main/java/edu/internet2/middleware/shibboleth/idp/authn/provider/UsernamePasswordLoginHandler.java
src/main/java/edu/internet2/middleware/shibboleth/idp/profile/saml1/ShibbolethSSOProfileHandler.java
src/main/java/edu/internet2/middleware/shibboleth/idp/profile/saml2/SSOProfileHandler.java
src/main/java/edu/internet2/middleware/shibboleth/idp/util/HttpServletHelper.java
src/test/java/edu/internet2/middleware/shibboleth/idp/system/conf1/SAML2SSOTestCase.java
src/test/java/edu/internet2/middleware/shibboleth/idp/system/conf1/ShibbolethSSOTestCase.java

index 539ab41..c3cabf8 100644 (file)
@@ -7,6 +7,7 @@ Changes in Release 2.2.0
 [SIDP-384] - Incorrect error message set for expired request in Shibboleth SSO Profile Handler
 [SIDP-382] - Less verbose logging for failed attribute queries due to missing name-id
 [SIDP-381] - Use duration notation for assertion lifetime in example config files
+[SIDP-380] - Use of forwards between profile handlers and authentication engine causes problems for uApprove
 [SIDP-379] - Usage of general AuthenticationException in UsernamePasswordLoginHandler
 [SIDP-374] - Switch to use StaticBasicParserPool instead of BasicParserPool
 [SIDP-373] - The SLF4J MDC state is not being properly cleared when request processing is done.
index 3a6169f..21a7a33 100644 (file)
@@ -40,6 +40,7 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.joda.time.DateTime;
 import org.opensaml.saml2.core.AuthnContext;
+import org.opensaml.util.URLBuilder;
 import org.opensaml.util.storage.StorageService;
 import org.opensaml.ws.transport.http.HTTPTransportUtils;
 import org.opensaml.xml.util.Base64;
@@ -146,6 +147,15 @@ public class AuthenticationEngine extends HttpServlet {
             forwardRequest("/error.jsp", httpRequest, httpResponse);
         } else {
             forwardRequest(loginContext.getAuthenticationEngineURL(), httpRequest, httpResponse);
+//            URLBuilder urlBuilder = HttpServletHelper.getServletContextUrl(httpRequest);
+//            urlBuilder.setPath(urlBuilder.getPath() + loginContext.getAuthenticationEngineURL());
+//            String authnEngineUrl = urlBuilder.buildURL();
+//            LOG.debug("Redirecting user to authentication engine at {}", authnEngineUrl);
+//            try{
+//                httpResponse.sendRedirect(authnEngineUrl);
+//            }catch(IOException e){
+//                LOG.warn("Error sending user back to authentication engine at " + authnEngineUrl, e);
+//            }
         }
     }
 
@@ -163,11 +173,15 @@ public class AuthenticationEngine extends HttpServlet {
             forwardRequest("/error.jsp", httpRequest, httpResponse);
         }
 
-        // Remove the login context from the replicated store and bind it to the request
-        HttpServletHelper.unbindLoginContext(storageService, context, httpRequest, httpResponse);
-        HttpServletHelper.bindLoginContext(loginContext, httpRequest);
-        LOG.debug("Returning control to profile handler at: {}", loginContext.getProfileHandlerURL());
-        forwardRequest(loginContext.getProfileHandlerURL(), httpRequest, httpResponse);
+        URLBuilder urlBuilder = HttpServletHelper.getServletContextUrl(httpRequest);
+        urlBuilder.setPath(urlBuilder.getPath() + loginContext.getProfileHandlerURL());
+        String profileUrl = urlBuilder.buildURL();
+        LOG.debug("Redirecting user to profile handler at {}", profileUrl);
+        try{
+            httpResponse.sendRedirect(profileUrl);
+        }catch(IOException e){
+            LOG.warn("Error sending user back to profile handler at " + profileUrl, e);
+        }
     }
 
     /**
index 9a17b7a..6f13563 100644 (file)
@@ -25,6 +25,8 @@ import org.opensaml.util.URLBuilder;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import edu.internet2.middleware.shibboleth.idp.util.HttpServletHelper;
+
 /**
  * Authenticate a username and password against a JAAS source.
  * 
@@ -59,17 +61,13 @@ public class UsernamePasswordLoginHandler extends AbstractLoginHandler {
     public void login(final HttpServletRequest httpRequest, final HttpServletResponse httpResponse) {
         // forward control to the servlet.
         try {
-            StringBuilder pathBuilder = new StringBuilder();
-            pathBuilder.append(httpRequest.getContextPath());
+            URLBuilder urlBuilder = HttpServletHelper.getServletContextUrl(httpRequest);
+            
+            StringBuilder pathBuilder = new StringBuilder(urlBuilder.getPath());
             if (!authenticationServletURL.startsWith("/")) {
                 pathBuilder.append("/");
             }
             pathBuilder.append(authenticationServletURL);
-
-            URLBuilder urlBuilder = new URLBuilder();
-            urlBuilder.setScheme(httpRequest.getScheme());
-            urlBuilder.setHost(httpRequest.getServerName());
-            urlBuilder.setPort(httpRequest.getServerPort());
             urlBuilder.setPath(pathBuilder.toString());
 
             log.debug("Redirecting to {}", urlBuilder.buildURL());
index 177e85b..ab4669f 100644 (file)
@@ -19,8 +19,7 @@ package edu.internet2.middleware.shibboleth.idp.profile.saml1;
 import java.io.IOException;
 import java.util.ArrayList;
 
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletException;
+import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -40,6 +39,7 @@ import org.opensaml.saml2.metadata.Endpoint;
 import org.opensaml.saml2.metadata.EntityDescriptor;
 import org.opensaml.saml2.metadata.IDPSSODescriptor;
 import org.opensaml.saml2.metadata.SPSSODescriptor;
+import org.opensaml.util.URLBuilder;
 import org.opensaml.ws.message.decoder.MessageDecodingException;
 import org.opensaml.ws.transport.http.HTTPInTransport;
 import org.opensaml.ws.transport.http.HTTPOutTransport;
@@ -59,7 +59,6 @@ import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfi
 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.SAMLMDRelyingPartyConfigurationManager;
 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml1.ShibbolethSSOConfiguration;
 import edu.internet2.middleware.shibboleth.common.util.HttpHelper;
-import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
 import edu.internet2.middleware.shibboleth.idp.authn.ShibbolethSSOLoginContext;
 import edu.internet2.middleware.shibboleth.idp.util.HttpServletHelper;
 
@@ -90,7 +89,11 @@ public class ShibbolethSSOProfileHandler extends AbstractSAML1ProfileHandler {
         if (DatatypeHelper.isEmpty(authnManagerPath)) {
             throw new IllegalArgumentException("Authentication manager path may not be null");
         }
-        authenticationManagerPath = authnManagerPath;
+        if (authnManagerPath.startsWith("/")) {
+            authenticationManagerPath = authnManagerPath;
+        } else {
+            authenticationManagerPath = "/" + authnManagerPath;
+        }
 
         authnStatementBuilder = (SAMLObjectBuilder<AuthenticationStatement>) getBuilderFactory().getBuilder(
                 AuthenticationStatement.DEFAULT_ELEMENT_NAME);
@@ -112,20 +115,25 @@ public class ShibbolethSSOProfileHandler extends AbstractSAML1ProfileHandler {
         log.debug("Processing incoming request");
 
         HttpServletRequest httpRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
-        LoginContext loginContext = HttpServletHelper.getLoginContext(httpRequest);
+        HttpServletResponse httpResponse = ((HttpServletResponseAdapter) outTransport).getWrappedResponse();
+        ServletContext servletContext = httpRequest.getSession().getServletContext();
+
+        ShibbolethSSOLoginContext loginContext = (ShibbolethSSOLoginContext) HttpServletHelper.getLoginContext(
+                getStorageService(), servletContext, httpRequest);
 
         if (loginContext == null) {
             log.debug("Incoming request does not contain a login context, processing as first leg of request");
             performAuthentication(inTransport, outTransport);
         } else {
             log.debug("Incoming request contains a login context, processing as second leg of request");
-            completeAuthenticationRequest(inTransport, outTransport);
+            HttpServletHelper.unbindLoginContext(getStorageService(), servletContext, httpRequest, httpResponse);
+            completeAuthenticationRequest(loginContext, inTransport, outTransport);
         }
     }
 
     /**
-     * Creates a {@link LoginContext} an sends the request off to the AuthenticationManager to begin the process of
-     * authenticating the user.
+     * Creates a {@link ShibbolethSSOLoginContext} an sends the request off to the AuthenticationManager to begin the
+     * process of authenticating the user.
      * 
      * @param inTransport inbound message transport
      * @param outTransport outbound message transport
@@ -153,20 +161,19 @@ public class ShibbolethSSOProfileHandler extends AbstractSAML1ProfileHandler {
             throw new ProfileException(msg);
         }
 
-        HttpServletHelper.bindLoginContext(loginContext, httpRequest);
+        HttpServletHelper.bindLoginContext(loginContext, getStorageService(), httpRequest.getSession()
+                .getServletContext(), httpRequest, httpResponse);
 
         try {
-            RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(authenticationManagerPath);
-            dispatcher.forward(httpRequest, httpResponse);
-            return;
+            URLBuilder urlBuilder = HttpServletHelper.getServletContextUrl(httpRequest);
+            urlBuilder.setPath(urlBuilder.getPath() + authenticationManagerPath);
+            String authnEngineUrl = urlBuilder.buildURL();
+            log.debug("Redirecting user to authentication engine at {}", authnEngineUrl);
+            httpResponse.sendRedirect(authnEngineUrl);
         } catch (IOException e) {
             String msg = "Error forwarding Shibboleth SSO request to AuthenticationManager";
             log.error(msg, e);
             throw new ProfileException(msg, e);
-        } catch (ServletException e) {
-            String msg = "Error forwarding Shibboleth SSO request to AuthenticationManager";
-            log.error(msg, e);
-            throw new ProfileException(msg, e);
         }
     }
 
@@ -182,8 +189,8 @@ public class ShibbolethSSOProfileHandler extends AbstractSAML1ProfileHandler {
     protected void decodeRequest(ShibbolethSSORequestContext requestContext, HTTPInTransport inTransport,
             HTTPOutTransport outTransport) throws ProfileException {
         if (log.isDebugEnabled()) {
-            log.debug("Decoding message with decoder binding {}",
-                    getInboundMessageDecoder(requestContext).getBindingURI());
+            log.debug("Decoding message with decoder binding {}", getInboundMessageDecoder(requestContext)
+                    .getBindingURI());
         }
 
         HttpServletRequest httpRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
@@ -230,16 +237,14 @@ public class ShibbolethSSOProfileHandler extends AbstractSAML1ProfileHandler {
      * Creates a response to the Shibboleth SSO and sends the user, with response in tow, back to the relying party
      * after they've been authenticated.
      * 
+     * @param loginContext login context for this request
      * @param inTransport inbound message transport
      * @param outTransport outbound message transport
      * 
      * @throws ProfileException thrown if the response can not be created and sent back to the relying party
      */
-    protected void completeAuthenticationRequest(HTTPInTransport inTransport, HTTPOutTransport outTransport)
-            throws ProfileException {
-        HttpServletRequest httpRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
-        ShibbolethSSOLoginContext loginContext = (ShibbolethSSOLoginContext) HttpServletHelper.getLoginContext(httpRequest);
-
+    protected void completeAuthenticationRequest(ShibbolethSSOLoginContext loginContext, HTTPInTransport inTransport,
+            HTTPOutTransport outTransport) throws ProfileException {
         ShibbolethSSORequestContext requestContext = buildRequestContext(loginContext, inTransport, outTransport);
 
         Response samlResponse;
@@ -250,16 +255,16 @@ public class ShibbolethSSOProfileHandler extends AbstractSAML1ProfileHandler {
             }
 
             resolveAttributes(requestContext);
-            
+
             ArrayList<Statement> statements = new ArrayList<Statement>();
             statements.add(buildAuthenticationStatement(requestContext));
             if (requestContext.getProfileConfiguration().includeAttributeStatement()) {
-                    AttributeStatement attributeStatement = buildAttributeStatement(requestContext,
-                            "urn:oasis:names:tc:SAML:1.0:cm:bearer");
-                    if (attributeStatement != null) {
-                        requestContext.setReleasedAttributes(requestContext.getAttributes().keySet());
-                        statements.add(attributeStatement);
-                    }
+                AttributeStatement attributeStatement = buildAttributeStatement(requestContext,
+                        "urn:oasis:names:tc:SAML:1.0:cm:bearer");
+                if (attributeStatement != null) {
+                    requestContext.setReleasedAttributes(requestContext.getAttributes().keySet());
+                    statements.add(attributeStatement);
+                }
             }
 
             samlResponse = buildResponse(requestContext, statements);
index af9ebaa..f40880b 100644 (file)
@@ -20,9 +20,9 @@ import java.io.IOException;
 import java.io.StringReader;
 import java.util.ArrayList;
 
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletException;
+import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
@@ -53,6 +53,7 @@ import org.opensaml.saml2.metadata.EntityDescriptor;
 import org.opensaml.saml2.metadata.IDPSSODescriptor;
 import org.opensaml.saml2.metadata.SPSSODescriptor;
 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
+import org.opensaml.util.URLBuilder;
 import org.opensaml.ws.message.decoder.MessageDecodingException;
 import org.opensaml.ws.transport.http.HTTPInTransport;
 import org.opensaml.ws.transport.http.HTTPOutTransport;
@@ -75,7 +76,6 @@ import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfi
 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.SAMLMDRelyingPartyConfigurationManager;
 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml2.SSOConfiguration;
 import edu.internet2.middleware.shibboleth.common.util.HttpHelper;
-import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
 import edu.internet2.middleware.shibboleth.idp.authn.PassiveAuthenticationException;
 import edu.internet2.middleware.shibboleth.idp.authn.Saml2LoginContext;
 import edu.internet2.middleware.shibboleth.idp.session.Session;
@@ -117,7 +117,14 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
     public SSOProfileHandler(String authnManagerPath) {
         super();
 
-        authenticationManagerPath = authnManagerPath;
+        if (DatatypeHelper.isEmpty(authnManagerPath)) {
+            throw new IllegalArgumentException("Authentication manager path may not be null");
+        }
+        if (authnManagerPath.startsWith("/")) {
+            authenticationManagerPath = authnManagerPath;
+        } else {
+            authenticationManagerPath = "/" + authnManagerPath;
+        }
 
         authnStatementBuilder = (SAMLObjectBuilder<AuthnStatement>) getBuilderFactory().getBuilder(
                 AuthnStatement.DEFAULT_ELEMENT_NAME);
@@ -141,14 +148,18 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
     /** {@inheritDoc} */
     public void processRequest(HTTPInTransport inTransport, HTTPOutTransport outTransport) throws ProfileException {
         HttpServletRequest httpRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
+        HttpServletResponse httpResponse = ((HttpServletResponseAdapter) outTransport).getWrappedResponse();
+        ServletContext servletContext = httpRequest.getSession().getServletContext();
 
-        LoginContext loginContext = HttpServletHelper.getLoginContext(httpRequest);
+        Saml2LoginContext loginContext = (Saml2LoginContext) HttpServletHelper.getLoginContext(getStorageService(),
+                servletContext, httpRequest);
         if (loginContext == null) {
             log.debug("Incoming request does not contain a login context, processing as first leg of request");
             performAuthentication(inTransport, outTransport);
         } else {
             log.debug("Incoming request contains a login context, processing as second leg of request");
-            completeAuthenticationRequest(inTransport, outTransport);
+            HttpServletHelper.unbindLoginContext(getStorageService(), servletContext, httpRequest, httpResponse);
+            completeAuthenticationRequest(loginContext, inTransport, outTransport);
         }
     }
 
@@ -164,7 +175,9 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
      */
     protected void performAuthentication(HTTPInTransport inTransport, HTTPOutTransport outTransport)
             throws ProfileException {
-        HttpServletRequest servletRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
+        HttpServletRequest httpRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
+        HttpServletResponse httpResponse = ((HttpServletResponseAdapter) outTransport).getWrappedResponse();
+        
         SSORequestContext requestContext = new SSORequestContext();
 
         try {
@@ -184,21 +197,23 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
             Saml2LoginContext loginContext = new Saml2LoginContext(relyingPartyId, requestContext.getRelayState(),
                     requestContext.getInboundSAMLMessage());
             loginContext.setAuthenticationEngineURL(authenticationManagerPath);
-            loginContext.setProfileHandlerURL(HttpHelper.getRequestUriWithoutContext(servletRequest));
+            loginContext.setProfileHandlerURL(HttpHelper.getRequestUriWithoutContext(httpRequest));
             loginContext.setDefaultAuthenticationMethod(rpConfig.getDefaultAuthenticationMethod());
 
-            HttpServletHelper.bindLoginContext(loginContext, servletRequest);
-            RequestDispatcher dispatcher = servletRequest.getRequestDispatcher(authenticationManagerPath);
-            dispatcher.forward(servletRequest, ((HttpServletResponseAdapter) outTransport).getWrappedResponse());
+            HttpServletHelper.bindLoginContext(loginContext, getStorageService(), httpRequest.getSession()
+                    .getServletContext(), httpRequest, httpResponse);
+            
+            URLBuilder urlBuilder = HttpServletHelper.getServletContextUrl(httpRequest);
+            urlBuilder.setPath(urlBuilder.getPath() + authenticationManagerPath);
+            String authnEngineUrl = urlBuilder.buildURL();
+            log.debug("Redirecting user to authentication engine at {}", authnEngineUrl);
+            httpResponse.sendRedirect(authnEngineUrl);
         } 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 to AuthenticationManager", ex);
             throw new ProfileException("Error forwarding SAML 2 AuthnRequest to AuthenticationManager", ex);
-        } catch (ServletException ex) {
-            log.error("Error forwarding SAML 2 AuthnRequest to AuthenticationManager", ex);
-            throw new ProfileException("Error forwarding SAML 2 AuthnRequest to AuthenticationManager", ex);
         }
     }
 
@@ -206,23 +221,21 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
      * Creates a response to the {@link AuthnRequest} and sends the user, with response in tow, back to the relying
      * party after they've been authenticated.
      * 
+     * @param loginContext login context for this request
      * @param inTransport inbound message transport
      * @param outTransport outbound message transport
      * 
      * @throws ProfileException thrown if the response can not be created and sent back to the relying party
      */
-    protected void completeAuthenticationRequest(HTTPInTransport inTransport, HTTPOutTransport outTransport)
-            throws ProfileException {
-        HttpServletRequest httpRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
-        Saml2LoginContext loginContext = (Saml2LoginContext) HttpServletHelper.getLoginContext(httpRequest);
-
+    protected void completeAuthenticationRequest(Saml2LoginContext loginContext, HTTPInTransport inTransport,
+            HTTPOutTransport outTransport) throws ProfileException {
         SSORequestContext requestContext = buildRequestContext(loginContext, inTransport, outTransport);
 
         Response samlResponse;
         try {
             checkSamlVersion(requestContext);
             checkNameIDPolicy(requestContext);
-            
+
             if (loginContext.getAuthenticationFailure() != null) {
                 if (loginContext.getAuthenticationFailure() instanceof PassiveAuthenticationException) {
                     requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.NO_PASSIVE_URI,
@@ -343,12 +356,12 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
         if (nameIdPolcy == null) {
             return;
         }
-        
+
         String spNameQualifier = DatatypeHelper.safeTrimOrNullString(nameIdPolcy.getSPNameQualifier());
         if (spNameQualifier == null) {
             return;
         }
-        
+
         log.debug("Checking if message issuer is a member of affiliation '{}'", spNameQualifier);
         try {
             EntityDescriptor affiliation = getMetadataProvider().getEntityDescriptor(spNameQualifier);
@@ -563,43 +576,43 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
 
         return subjectLocality;
     }
-    
+
     /** {@inheritDoc} */
     protected String getRequiredNameIDFormat(BaseSAMLProfileRequestContext requestContext) {
         String requiredNameFormat = null;
         AuthnRequest authnRequest = (AuthnRequest) requestContext.getInboundSAMLMessage();
         NameIDPolicy nameIdPolicy = authnRequest.getNameIDPolicy();
-        if(nameIdPolicy != null){
-             requiredNameFormat = DatatypeHelper.safeTrimOrNullString(nameIdPolicy.getFormat());
+        if (nameIdPolicy != null) {
+            requiredNameFormat = DatatypeHelper.safeTrimOrNullString(nameIdPolicy.getFormat());
             // Check for unspec'd or encryption formats, which aren't relevant for this section of code.
             if (requiredNameFormat != null
                     && (NameID.ENCRYPTED.equals(requiredNameFormat) || NameID.UNSPECIFIED.equals(requiredNameFormat))) {
                 requiredNameFormat = null;
             }
         }
-        
+
         return requiredNameFormat;
     }
 
     /** {@inheritDoc} */
     protected NameID buildNameId(BaseSAML2ProfileRequestContext<?, ?, ?> requestContext) throws ProfileException {
         NameID nameId = super.buildNameId(requestContext);
-        if(nameId != null){
+        if (nameId != null) {
             AuthnRequest authnRequest = (AuthnRequest) requestContext.getInboundSAMLMessage();
             NameIDPolicy nameIdPolicy = authnRequest.getNameIDPolicy();
-            if(nameIdPolicy != null){
+            if (nameIdPolicy != null) {
                 String spNameQualifier = DatatypeHelper.safeTrimOrNullString(nameIdPolicy.getSPNameQualifier());
-                if(spNameQualifier != null){
+                if (spNameQualifier != null) {
                     nameId.setSPNameQualifier(spNameQualifier);
-                }else{
+                } else {
                     nameId.setSPNameQualifier(requestContext.getInboundMessageIssuer());
                 }
             }
         }
-        
+
         return nameId;
     }
-    
+
     /**
      * Selects the appropriate endpoint for the relying party and stores it in the request context.
      * 
index 1a7801f..77aa449 100644 (file)
@@ -25,6 +25,7 @@ import javax.servlet.http.HttpServletResponse;
 
 import org.opensaml.saml2.metadata.EntityDescriptor;
 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
+import org.opensaml.util.URLBuilder;
 import org.opensaml.util.storage.StorageService;
 import org.opensaml.xml.util.DatatypeHelper;
 import org.slf4j.Logger;
@@ -47,7 +48,7 @@ public class HttpServletHelper {
 
     /** Name of the context initialization parameter that stores the domain to use for all cookies. */
     public static final String COOKIE_DOMAIN_PARAM = "cookieDomain";
-    
+
     /** Name of the cookie containing the IdP session ID: {@value} . */
     public static final String IDP_SESSION_COOKIE = "_idp_session";
 
@@ -122,6 +123,8 @@ public class HttpServletHelper {
      * 
      * @param loginContext login context to be bound
      * @param httpRequest current HTTP request
+     * 
+     * @deprecated
      */
     public static void bindLoginContext(LoginContext loginContext, HttpServletRequest httpRequest) {
         if (httpRequest == null) {
@@ -129,7 +132,7 @@ public class HttpServletHelper {
         }
         httpRequest.setAttribute(LOGIN_CTX_KEY_NAME, loginContext);
     }
-    
+
     /**
      * Binds a {@link LoginContext} to the issuer of the current request. The binding is done by creating a random UUID,
      * placing that in a cookie in the request, and storing the context in to the storage service under that key.
@@ -152,32 +155,29 @@ public class HttpServletHelper {
             return;
         }
 
-        bindLoginContext(loginContext, httpRequest);
-
         String parition = getContextParam(context, LOGIN_CTX_PARTITION_CTX_PARAM, DEFAULT_LOGIN_CTX_PARITION);
-        log.debug("LoginContext parition: {}", parition);
 
         String contextKey = UUID.randomUUID().toString();
         while (storageService.contains(parition, contextKey)) {
             contextKey = UUID.randomUUID().toString();
         }
-        log.debug("LoginContext key: {}", contextKey);
 
         LoginContextEntry entry = new LoginContextEntry(loginContext, 1800000);
+        log.debug("Storing LoginContext to StorageService partition {}, key {}", parition, contextKey);
         storageService.put(parition, contextKey, entry);
 
         String cookieDomain = getCookieDomain(context);
-        
+
         Cookie contextKeyCookie = new Cookie(LOGIN_CTX_KEY_NAME, contextKey);
         contextKeyCookie.setVersion(1);
-        if(cookieDomain != null){
+        if (cookieDomain != null) {
             contextKeyCookie.setDomain(cookieDomain);
         }
         contextKeyCookie.setPath("".equals(httpRequest.getContextPath()) ? "/" : httpRequest.getContextPath());
         contextKeyCookie.setSecure(httpRequest.isSecure());
         httpResponse.addCookie(contextKeyCookie);
     }
-    
+
     /**
      * Gets the domain to use for all cookies.
      * 
@@ -185,7 +185,7 @@ public class HttpServletHelper {
      * 
      * @return domain to use for all cookies
      */
-    public static String getCookieDomain(ServletContext context){
+    public static String getCookieDomain(ServletContext context) {
         return context.getInitParameter(COOKIE_DOMAIN_PARAM);
     }
 
@@ -283,6 +283,7 @@ public class HttpServletHelper {
      * @param httpRequest current HTTP request
      * 
      * @return the login context or null if no login context is bound to the request
+     * @deprecated
      */
     public static LoginContext getLoginContext(HttpServletRequest httpRequest) {
         return (LoginContext) httpRequest.getAttribute(LOGIN_CTX_KEY_NAME);
@@ -310,36 +311,33 @@ public class HttpServletHelper {
             throw new IllegalArgumentException("HTTP request may not be null");
         }
 
-        LoginContext loginContext = getLoginContext(httpRequest);
-        if (loginContext == null) {
-            log.debug("LoginContext not bound to HTTP request, retrieving it from storage service");
-            Cookie loginContextKeyCookie = getCookie(httpRequest, LOGIN_CTX_KEY_NAME);
-            if (loginContextKeyCookie == null) {
-                log.debug("LoginContext key cookie was not present in request");
-                return null;
-            }
+        Cookie loginContextKeyCookie = getCookie(httpRequest, LOGIN_CTX_KEY_NAME);
+        if (loginContextKeyCookie == null) {
+            log.debug("LoginContext key cookie was not present in request");
+            return null;
+        }
 
-            String loginContextKey = DatatypeHelper.safeTrimOrNullString(loginContextKeyCookie.getValue());
-            if (loginContextKey == null) {
-                log.warn("Corrupted LoginContext Key cookie, it did not contain a value");
-            }
-            log.debug("LoginContext key is '{}'", loginContextKey);
-
-            String partition = getContextParam(context, LOGIN_CTX_PARTITION_CTX_PARAM, DEFAULT_LOGIN_CTX_PARITION);
-            log.debug("parition: {}", partition);
-            LoginContextEntry entry = (LoginContextEntry) storageService.get(partition, loginContextKey);
-            if (entry != null) {
-                if (entry.isExpired()) {
-                    log.debug("LoginContext found but it was expired");
-                } else {
-                    loginContext = entry.getLoginContext();
-                }
+        String loginContextKey = DatatypeHelper.safeTrimOrNullString(loginContextKeyCookie.getValue());
+        if (loginContextKey == null) {
+            log.warn("Corrupted LoginContext Key cookie, it did not contain a value");
+        }
+
+        String partition = getContextParam(context, LOGIN_CTX_PARTITION_CTX_PARAM, DEFAULT_LOGIN_CTX_PARITION);
+        log.debug("Looking up LoginContext with key {} from StorageService parition: {}", loginContextKey, partition);
+        LoginContextEntry entry = (LoginContextEntry) storageService.get(partition, loginContextKey);
+        if (entry != null) {
+            if (entry.isExpired()) {
+                log.debug("LoginContext found but it was expired");
             } else {
-                log.debug("No login context in storage service");
+                log.debug("Retrieved LoginContext with key {} from StorageService parition: {}", loginContextKey,
+                        partition);
+                return entry.getLoginContext();
             }
+        } else {
+            log.debug("No login context in storage service");
         }
 
-        return loginContext;
+        return null;
     }
 
     /**
@@ -377,7 +375,7 @@ public class HttpServletHelper {
         return getRelyingPartyConfigurationManager(context, getContextParam(context, RP_CONFIG_MNGR_SID_CTX_PARAM,
                 DEFAULT_RP_CONFIG_MNGR_SID));
     }
-    
+
     /**
      * Gets the {@link RelyingPartyConfigurationManager} bound to the Servlet context.
      * 
@@ -390,7 +388,7 @@ public class HttpServletHelper {
             String serviceId) {
         return (RelyingPartyConfigurationManager) context.getAttribute(serviceId);
     }
-    
+
     /**
      * Gets the {@link RelyingPartyConfigurationManager} service bound to the Servlet context.
      * 
@@ -414,6 +412,7 @@ public class HttpServletHelper {
      * @return the service or null if there is no such service bound to the context
      * 
      * @deprecated use {@link #getRelyingPartyConfigurationManager(ServletContext, String)
+
      */
     public static RelyingPartyConfigurationManager getRelyingPartyConfirmationManager(ServletContext context,
             String serviceId) {
@@ -563,31 +562,64 @@ public class HttpServletHelper {
      */
     public static LoginContext unbindLoginContext(StorageService storageService, ServletContext context,
             HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
-        if (storageService == null || context == null || httpRequest == null || httpResponse == null) {
-            return null;
+        log.debug("Unbinding LoginContext");
+        if (storageService == null) {
+            throw new IllegalArgumentException("Storage service may not be null");
+        }
+        if (context == null) {
+            throw new IllegalArgumentException("Servlet context may not be null");
+        }
+        if (httpRequest == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
+        }
+        if (httpResponse == null) {
+            throw new IllegalArgumentException("HTTP request may not be null");
         }
 
         Cookie loginContextKeyCookie = getCookie(httpRequest, LOGIN_CTX_KEY_NAME);
         if (loginContextKeyCookie == null) {
+            log.debug("No LoginContext cookie available, no unbinding necessary.");
             return null;
         }
 
         String loginContextKey = DatatypeHelper.safeTrimOrNullString(loginContextKeyCookie.getValue());
         if (loginContextKey == null) {
             log.warn("Corrupted LoginContext Key cookie, it did not contain a value");
+            return null;
         }
 
-        httpRequest.setAttribute(LOGIN_CTX_KEY_NAME, null);
+        log.debug("Expiring LoginContext cookie");
         loginContextKeyCookie.setMaxAge(0);
         loginContextKeyCookie.setPath("".equals(httpRequest.getContextPath()) ? "/" : httpRequest.getContextPath());
         loginContextKeyCookie.setVersion(1);
         httpResponse.addCookie(loginContextKeyCookie);
 
-        LoginContextEntry entry = (LoginContextEntry) storageService.remove(getContextParam(context,
-                LOGIN_CTX_PARTITION_CTX_PARAM, DEFAULT_LOGIN_CTX_PARITION), loginContextKey);
+        String storageServicePartition = getContextParam(context, LOGIN_CTX_PARTITION_CTX_PARAM,
+                DEFAULT_LOGIN_CTX_PARITION);
+        
+        log.debug("Removing LoginContext, with key {}, from StorageService partition {}", loginContextKey,
+                storageServicePartition);
+        LoginContextEntry entry = (LoginContextEntry) storageService.remove(storageServicePartition, loginContextKey);
         if (entry != null && !entry.isExpired()) {
             return entry.getLoginContext();
         }
+
         return null;
     }
+
+    /**
+     * Builds a URL, up to and including the servlet context path. URL does not include a trailing "/".
+     * 
+     * @param httpRequest httpRequest made to the servlet in question
+     * 
+     * @return URL builder containing the scheme, server name, server port, and context path
+     */
+    public static URLBuilder getServletContextUrl(HttpServletRequest httpRequest) {
+        URLBuilder urlBuilder = new URLBuilder();
+        urlBuilder.setScheme(httpRequest.getScheme());
+        urlBuilder.setHost(httpRequest.getServerName());
+        urlBuilder.setPort(httpRequest.getServerPort());
+        urlBuilder.setPath(httpRequest.getContextPath());
+        return urlBuilder;
+    }
 }
\ No newline at end of file
index 9bf3601..edb215f 100644 (file)
@@ -19,6 +19,7 @@ package edu.internet2.middleware.shibboleth.idp.system.conf1;
 import java.security.Principal;
 
 import javax.security.auth.Subject;
+import javax.servlet.http.HttpServletRequest;
 
 import org.joda.time.DateTime;
 import org.opensaml.common.SAMLObjectBuilder;
@@ -34,11 +35,14 @@ import org.opensaml.xml.util.Base64;
 import org.opensaml.xml.util.XMLHelper;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.mock.web.MockServletContext;
 import org.w3c.dom.Element;
 
 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
 import edu.internet2.middleware.shibboleth.common.profile.ProfileHandler;
 import edu.internet2.middleware.shibboleth.common.profile.ProfileHandlerManager;
+import edu.internet2.middleware.shibboleth.common.profile.provider.AbstractShibbolethProfileHandler;
+import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
 import edu.internet2.middleware.shibboleth.idp.authn.Saml2LoginContext;
 import edu.internet2.middleware.shibboleth.idp.authn.UsernamePrincipal;
 import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
@@ -52,12 +56,14 @@ public class SAML2SSOTestCase extends BaseConf1TestCase {
 
     /** Tests initial leg of the SSO request where request is decoded and sent to the authentication engine. */
     public void testFirstAuthenticationLeg() throws Exception {
+        MockServletContext servletContext = new MockServletContext();
+        
         MockHttpServletRequest servletRequest = buildServletRequest("urn:example.org:sp1");
         MockHttpServletResponse servletResponse = new MockHttpServletResponse();
 
         ProfileHandlerManager handlerManager = (ProfileHandlerManager) getApplicationContext().getBean(
                 "shibboleth.HandlerManager");
-        ProfileHandler handler = handlerManager.getProfileHandler(servletRequest);
+        AbstractShibbolethProfileHandler handler = (AbstractShibbolethProfileHandler) handlerManager.getProfileHandler(servletRequest);
         assertNotNull(handler);
 
         // Process request
@@ -65,7 +71,8 @@ public class SAML2SSOTestCase extends BaseConf1TestCase {
         HTTPOutTransport profileResponse = new HttpServletResponseAdapter(servletResponse, false);
         handler.processRequest(profileRequest, profileResponse);
 
-        Saml2LoginContext loginContext = (Saml2LoginContext) HttpServletHelper.getLoginContext(servletRequest);
+        servletRequest.setCookies(servletResponse.getCookies());
+        Saml2LoginContext loginContext = (Saml2LoginContext) HttpServletHelper.getLoginContext(handler.getStorageService(), servletContext, servletRequest);
 
         assertNotNull(loginContext);
         assertEquals(false, loginContext.getAuthenticationAttempted());
@@ -76,20 +83,23 @@ public class SAML2SSOTestCase extends BaseConf1TestCase {
         assertEquals("urn:example.org:sp1", loginContext.getRelyingPartyId());
         assertEquals(0, loginContext.getRequestedAuthenticationMethods().size());
 
-        assertEquals("/AuthnEngine", servletResponse.getForwardedUrl());
+        assertTrue(servletResponse.getRedirectedUrl().endsWith("/AuthnEngine"));
     }
 
     /** Tests second leg of the SSO request where request returns to SSO handler and AuthN statement is generated. */
     public void testSecondAuthenticationLeg() throws Exception {
+        MockServletContext servletContext = new MockServletContext();
         MockHttpServletRequest servletRequest = buildServletRequest("urn:example.org:sp1");
         MockHttpServletResponse servletResponse = new MockHttpServletResponse();
 
-        HttpServletHelper.bindLoginContext(buildLoginContext("urn:example.org:sp1"), servletRequest);
-
         ProfileHandlerManager handlerManager = (ProfileHandlerManager) getApplicationContext().getBean(
                 "shibboleth.HandlerManager");
-        ProfileHandler handler = handlerManager.getProfileHandler(servletRequest);
+        AbstractShibbolethProfileHandler handler = (AbstractShibbolethProfileHandler) handlerManager.getProfileHandler(servletRequest);
         assertNotNull(handler);
+        
+        HttpServletHelper.bindLoginContext(buildLoginContext("urn:example.org:sp1"), handler.getStorageService(), servletContext,
+                servletRequest, servletResponse);
+        servletRequest.setCookies(servletResponse.getCookies());
 
         // Process request
         HTTPInTransport profileRequest = new HttpServletRequestAdapter(servletRequest);
index 67b8fc6..d0eb544 100644 (file)
@@ -27,10 +27,12 @@ import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
 import org.opensaml.ws.transport.http.HttpServletResponseAdapter;
 import org.springframework.mock.web.MockHttpServletRequest;
 import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.mock.web.MockServletContext;
 
 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
 import edu.internet2.middleware.shibboleth.common.profile.ProfileHandler;
 import edu.internet2.middleware.shibboleth.common.profile.ProfileHandlerManager;
+import edu.internet2.middleware.shibboleth.common.profile.provider.AbstractShibbolethProfileHandler;
 import edu.internet2.middleware.shibboleth.idp.authn.ShibbolethSSOLoginContext;
 import edu.internet2.middleware.shibboleth.idp.authn.UsernamePrincipal;
 import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
@@ -44,12 +46,15 @@ public class ShibbolethSSOTestCase extends BaseConf1TestCase {
 
     /** Tests initial leg of the SSO request where request is decoded and sent to the authentication engine. */
     public void testFirstAuthenticationLeg() throws Exception {
+        MockServletContext servletContext = new MockServletContext();
+
         MockHttpServletRequest servletRequest = buildServletRequest();
         MockHttpServletResponse servletResponse = new MockHttpServletResponse();
 
         ProfileHandlerManager handlerManager = (ProfileHandlerManager) getApplicationContext().getBean(
                 "shibboleth.HandlerManager");
-        ProfileHandler handler = handlerManager.getProfileHandler(servletRequest);
+        AbstractShibbolethProfileHandler handler = (AbstractShibbolethProfileHandler) handlerManager
+                .getProfileHandler(servletRequest);
         assertNotNull(handler);
 
         // Process request
@@ -57,8 +62,9 @@ public class ShibbolethSSOTestCase extends BaseConf1TestCase {
         HTTPOutTransport profileResponse = new HttpServletResponseAdapter(servletResponse, false);
         handler.processRequest(profileRequest, profileResponse);
 
-        ShibbolethSSOLoginContext loginContext = (ShibbolethSSOLoginContext) HttpServletHelper
-                .getLoginContext(servletRequest);
+        servletRequest.setCookies(servletResponse.getCookies());
+        ShibbolethSSOLoginContext loginContext = (ShibbolethSSOLoginContext) HttpServletHelper.getLoginContext(handler
+                .getStorageService(), servletContext, servletRequest);
 
         assertNotNull(loginContext);
         assertEquals(false, loginContext.getAuthenticationAttempted());
@@ -69,23 +75,27 @@ public class ShibbolethSSOTestCase extends BaseConf1TestCase {
         assertEquals("urn:example.org:sp1", loginContext.getRelyingPartyId());
         assertEquals(0, loginContext.getRequestedAuthenticationMethods().size());
         assertEquals("https://example.org/mySP", loginContext.getSpAssertionConsumerService());
-        assertEquals("https://example.org/mySP", loginContext.getSpTarget());
+        assertEquals("https://example.org/mySP", loginContext.getSpAssertionConsumerService());
 
-        assertEquals("/AuthnEngine", servletResponse.getForwardedUrl());
+        assertTrue(servletResponse.getRedirectedUrl().endsWith("/AuthnEngine"));
     }
 
     /** Tests second leg of the SSO request where request returns to SSO handler and AuthN statement is generated. */
     public void testSecondAuthenticationLeg() throws Exception {
+        MockServletContext servletContext = new MockServletContext();
         MockHttpServletRequest servletRequest = buildServletRequest();
         MockHttpServletResponse servletResponse = new MockHttpServletResponse();
 
-        HttpServletHelper.bindLoginContext(buildLoginContext(), servletRequest);
-
         ProfileHandlerManager handlerManager = (ProfileHandlerManager) getApplicationContext().getBean(
                 "shibboleth.HandlerManager");
-        ProfileHandler handler = handlerManager.getProfileHandler(servletRequest);
+        AbstractShibbolethProfileHandler handler = (AbstractShibbolethProfileHandler) handlerManager
+                .getProfileHandler(servletRequest);
         assertNotNull(handler);
 
+        HttpServletHelper.bindLoginContext(buildLoginContext(), handler.getStorageService(), servletContext,
+                servletRequest, servletResponse);
+        servletRequest.setCookies(servletResponse.getCookies());
+
         // Process request
         HTTPInTransport profileRequest = new HttpServletRequestAdapter(servletRequest);
         HTTPOutTransport profileResponse = new HttpServletResponseAdapter(servletResponse, false);