Lots of session management cleanup; no longer dependent on container session
authorlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Thu, 10 Jan 2008 11:31:24 +0000 (11:31 +0000)
committerlajoie <lajoie@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Thu, 10 Jan 2008 11:31:24 +0000 (11:31 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@2539 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

resources/WEB-INF/web.xml
src/edu/internet2/middleware/shibboleth/idp/authn/AuthenticationEngine.java
src/edu/internet2/middleware/shibboleth/idp/profile/AbstractSAMLProfileHandler.java
src/edu/internet2/middleware/shibboleth/idp/profile/saml2/SSOProfileHandler.java
src/edu/internet2/middleware/shibboleth/idp/session/IdPSessionFilter.java [new file with mode: 0644]
src/edu/internet2/middleware/shibboleth/idp/session/Session.java
src/edu/internet2/middleware/shibboleth/idp/session/impl/SessionImpl.java
src/edu/internet2/middleware/shibboleth/idp/session/impl/SessionManagerImpl.java

index a38feee..366aa29 100644 (file)
         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
     </listener>
 
-    <!-- Hook IdP sessions into HTTP servlet container sessions. -->
-    <listener>
-        <listener-class>edu.internet2.middleware.shibboleth.idp.session.ContainerSessionListener</listener-class>
-    </listener>
+    <!--  Add IdP Session object to incoming profile requests -->
+    <filter>
+        <filter-name>IdPSessionFilter</filter-name>
+        <filter-class>edu.internet2.middleware.shibboleth.idp.session.IdPSessionFilter</filter-class>
+    </filter>
     
-    <!-- 
-        Session time inactivity timeout, in minutes.
-        
-        A timeout of 0 or less means the session never timesout.  Such a setting is strongly discouraged and will 
-        almost certainly lead to memory exhaustion under moderate to heavy loads or in servers with prolonged uptime.
-    -->
-    <session-config>
-        <session-timeout>30</session-timeout>
-    </session-config>
+    <filter-mapping>
+        <filter-name>IdSessionFilter</filter-name>
+        <url-pattern>/profile/*</url-pattern>
+    </filter-mapping>
 
     <!-- Profile Request Dispatcher -->
     <servlet>
index ba85465..65aed43 100644 (file)
@@ -17,8 +17,6 @@
 package edu.internet2.middleware.shibboleth.idp.authn;
 
 import java.io.IOException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.List;
 
 import javax.security.auth.Subject;
@@ -141,8 +139,7 @@ public class AuthenticationEngine extends HttpServlet {
         }
 
         if (!loginContext.getAuthenticationAttempted()) {
-            String shibSessionId = (String) httpSession.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
-            Session shibSession = getSessionManager().getSession(shibSessionId);
+            Session shibSession = (Session) httpSession.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
 
             AuthenticationMethodInformation authenticationMethod = getUsableExistingAuthenticationMethod(loginContext,
                     shibSession);
@@ -176,8 +173,7 @@ public class AuthenticationEngine extends HttpServlet {
             AuthenticationMethodInformation authenticationMethod) {
         HttpSession httpSession = httpRequest.getSession();
 
-        String shibSessionId = (String) httpSession.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
-        Session shibSession = getSessionManager().getSession(shibSessionId);
+        Session shibSession = (Session) httpSession.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
 
         LOG.debug("Populating login context with existing session and authentication method information.");
         LoginContext loginContext = (LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
@@ -256,22 +252,12 @@ public class AuthenticationEngine extends HttpServlet {
         loginContext.setPrincipalName(principalName);
         loginContext.setAuthenticationInstant(new DateTime());
 
-        String shibSessionId = (String) httpSession.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
-        Session shibSession = getSessionManager().getSession(shibSessionId);
-
+        Session shibSession = (Session) httpSession.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
         if (shibSession == null) {
             LOG.debug("Creating shibboleth session for principal {}", principalName);
-
-            InetAddress addr;
-            try {
-                addr = InetAddress.getByName(httpRequest.getRemoteAddr());
-            } catch (UnknownHostException ex) {
-                addr = null;
-            }
-
-            shibSession = (Session) getSessionManager().createSession(addr, loginContext.getPrincipalName());
+            shibSession = (Session) getSessionManager().createSession(loginContext.getPrincipalName());
             loginContext.setSessionID(shibSession.getSessionID());
-            httpSession.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, shibSession.getSessionID());
+            httpSession.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, shibSession);
         }
 
         LOG.debug("Recording authentication and service information in Shibboleth session for principal: {}",
index 595c10f..4c75e1d 100644 (file)
@@ -177,25 +177,8 @@ public abstract class AbstractSAMLProfileHandler extends
      * @return user's session
      */
     protected Session getUserSession(InTransport inTransport) {
-        String sessionId = getUserSessionId(inTransport);
-        return getSessionManager().getSession(sessionId);
-    }
-
-    /**
-     * Gets the user's session ID from the current request.
-     * 
-     * @param inTransport current inbound transport
-     * 
-     * @return user's session ID
-     */
-    protected String getUserSessionId(InTransport inTransport) {
         HttpServletRequest rawRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
-
-        if (rawRequest != null) {
-            return (String) rawRequest.getSession().getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
-        }
-
-        return null;
+        return (Session) rawRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
     }
 
     /**
index 63e94e2..8f9f8a4 100644 (file)
@@ -66,6 +66,7 @@ import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml2.SS
 import edu.internet2.middleware.shibboleth.common.util.HttpHelper;
 import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
 import edu.internet2.middleware.shibboleth.idp.authn.Saml2LoginContext;
+import edu.internet2.middleware.shibboleth.idp.session.Session;
 
 /** SAML 2.0 SSO request profile handler. */
 public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
@@ -437,8 +438,10 @@ public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
         statement.setAuthnContext(authnContext);
         statement.setAuthnInstant(loginContext.getAuthenticationInstant());
 
-        // TODO
-        statement.setSessionIndex(null);
+        Session session = getUserSession(requestContext.getInboundMessageTransport());
+        if(session != null){
+            statement.setSessionIndex(session.getSessionID());
+        }
 
         if (loginContext.getAuthenticationDuration() > 0) {
             statement.setSessionNotOnOrAfter(loginContext.getAuthenticationInstant().plus(
diff --git a/src/edu/internet2/middleware/shibboleth/idp/session/IdPSessionFilter.java b/src/edu/internet2/middleware/shibboleth/idp/session/IdPSessionFilter.java
new file mode 100644 (file)
index 0000000..bace811
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2008 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.session;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.joda.time.DateTime;
+
+import edu.internet2.middleware.shibboleth.common.session.SessionManager;
+
+/**
+ * A filter that adds the current users {@link Session} the request, if the user has a session.
+ */
+public class IdPSessionFilter implements Filter {
+
+    /** Name of the IdP Cookie containing the IdP session ID. */
+    public static final String IDP_SESSION_COOKIE_NAME = "_idp_session";
+
+    /** IdP session manager. */
+    private SessionManager<Session> sessionManager;
+
+    /** {@inheritDoc} */
+    public void destroy() {
+
+    }
+
+    /** {@inheritDoc} */
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException,
+            ServletException {
+        HttpServletRequest httpRequest = (HttpServletRequest) request;
+        HttpServletResponse httpResponse = (HttpServletResponse) response;
+
+        Cookie idpSessionCookie = getIdPSessionCookie(httpRequest);
+        if (idpSessionCookie != null) {
+            Session idpSession = sessionManager.getSession(idpSessionCookie.getValue());
+            if (idpSession != null) {
+                idpSession.setLastActivityInstant(new DateTime());
+                httpRequest.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, idpSession);
+                addIdPSessionCookieToResponse(httpRequest, httpResponse, idpSession);
+            }
+        }
+
+        filterChain.doFilter(request, response);
+    }
+
+    /** {@inheritDoc} */
+    public void init(FilterConfig filterConfig) throws ServletException {
+        sessionManager = (SessionManager<Session>) filterConfig.getServletContext().getAttribute("sessionManager");
+    }
+
+    /**
+     * Gets the IdP session cookie from the current request, if the user currently has a session.
+     * 
+     * @param request current HTTP request
+     * 
+     * @return the user's current IdP session cookie, if they have a current session, otherwise null
+     */
+    protected Cookie getIdPSessionCookie(HttpServletRequest request) {
+        Cookie[] requestCookies = request.getCookies();
+
+        if (requestCookies != null) {
+            for (Cookie requestCookie : requestCookies) {
+                if (requestCookie.getDomain().equals(request.getLocalName())
+                        && requestCookie.getPath().equals(request.getContextPath())
+                        && requestCookie.getName().equalsIgnoreCase(IDP_SESSION_COOKIE_NAME)) {
+                    return requestCookie;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Adds a cookie, containing the user's IdP session ID, to the response.
+     * 
+     * @param request current HTTP request
+     * @param response current HTTP response
+     * @param userSession user's currentSession
+     */
+    protected void addIdPSessionCookieToResponse(HttpServletRequest request, HttpServletResponse response,
+            Session userSession) {
+        Cookie sessionCookie = new Cookie(IDP_SESSION_COOKIE_NAME, userSession.getSessionID());
+        sessionCookie.setDomain(request.getLocalName());
+        sessionCookie.setPath(request.getContextPath());
+        sessionCookie.setSecure(false);
+
+        int maxAge = (int) (userSession.getInactivityTimeout() / 1000);
+        sessionCookie.setMaxAge(maxAge);
+
+        response.addCookie(sessionCookie);
+    }
+}
\ No newline at end of file
index e77bb33..1cff894 100644 (file)
@@ -23,8 +23,8 @@ import java.util.Map;
  */
 public interface Session extends edu.internet2.middleware.shibboleth.common.session.Session {
 
-    /** Name of the HttpSession attribute a users Shibboleth session Id is bound to. */
-    public static final String HTTP_SESSION_BINDING_ATTRIBUTE = "ShibbolethSessionId";
+    /** Name of the HttpSession attribute to which a users IdP session is bound. */
+    public static final String HTTP_SESSION_BINDING_ATTRIBUTE = "ShibbolethIdPSession";
 
     /**
      * Gets the methods by which the user has authenticated to the IdP.
index 56d2a23..1dc0668 100644 (file)
@@ -16,7 +16,6 @@
 
 package edu.internet2.middleware.shibboleth.idp.session.impl;
 
-import java.net.InetAddress;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -33,7 +32,7 @@ public class SessionImpl extends AbstractSession implements Session {
     /** Serial version UID. */
     private static final long serialVersionUID = 2927868242208211623L;
 
-    /** The list of methods used to authentictate the user. */
+    /** The list of methods used to authenticate the user. */
     private HashMap<String, AuthenticationMethodInformation> authnMethods;
 
     /** The list of services to which the user has logged in. */
@@ -42,11 +41,12 @@ public class SessionImpl extends AbstractSession implements Session {
     /**
      * Default constructor.
      * 
-     * @param presenter IP address of the presenter
+     * @param sessionId ID of the session
      * @param principal principal ID of the user
+     * @param timeout inactivity timeout for the session in milliseconds
      */
-    public SessionImpl(InetAddress presenter, String principal) {
-        super(presenter, principal);
+    public SessionImpl(String sessionId, String principal, long timeout) {
+        super(sessionId, principal, timeout);
 
         authnMethods = new HashMap<String, AuthenticationMethodInformation>();
         servicesInformation = new HashMap<String, ServiceInformation>();
index 0894a32..a511704 100644 (file)
 
 package edu.internet2.middleware.shibboleth.idp.session.impl;
 
-import java.net.InetAddress;
+import java.security.SecureRandom;
 
 import org.joda.time.DateTime;
 import org.opensaml.util.storage.ExpiringObject;
 import org.opensaml.util.storage.StorageService;
+import org.opensaml.xml.util.Base64;
 import org.opensaml.xml.util.DatatypeHelper;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationContextAware;
@@ -37,6 +38,12 @@ public class SessionManagerImpl implements SessionManager<Session>, ApplicationC
 
     /** Spring context used to publish login and logout events. */
     private ApplicationContext appCtx;
+    
+    /** Number of random bits within a session ID. */
+    private final int sessionIDSize = 32;
+    
+    /** A {@link SecureRandom} PRNG to generate session IDs. */
+    private final SecureRandom prng = new SecureRandom();
 
     /** Backing service used to store sessions. */
     private StorageService<String, SessionManagerEntry> sessionStore;
@@ -83,8 +90,13 @@ public class SessionManagerImpl implements SessionManager<Session>, ApplicationC
     }
 
     /** {@inheritDoc} */
-    public Session createSession(InetAddress presenter, String principal) {
-        Session session = new SessionImpl(presenter, principal);
+    public Session createSession(String principal) {
+        // generate a random session ID
+        byte[] sid = new byte[sessionIDSize];
+        prng.nextBytes(sid);
+        String sessionID = Base64.encodeBytes(sid);
+        
+        Session session = new SessionImpl(sessionID, principal, sessionLifetime);
         SessionManagerEntry sessionEntry = new SessionManagerEntry(this, session, sessionLifetime);
         sessionStore.put(partition, session.getSessionID(), sessionEntry);
         appCtx.publishEvent(new LoginEvent(session));