Cleanup Session management and timeout
authorgilbert <gilbert@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Tue, 28 Jun 2005 17:32:05 +0000 (17:32 +0000)
committergilbert <gilbert@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Tue, 28 Jun 2005 17:32:05 +0000 (17:32 +0000)
git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@1673 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/edu/internet2/middleware/shibboleth/serviceprovider/ServiceProviderConfig.java
src/edu/internet2/middleware/shibboleth/serviceprovider/ServiceProviderContext.java
src/edu/internet2/middleware/shibboleth/serviceprovider/ServletContextInitializer.java
src/edu/internet2/middleware/shibboleth/serviceprovider/Session.java
src/edu/internet2/middleware/shibboleth/serviceprovider/SessionManager.java
tests/edu/internet2/middleware/shibboleth/serviceprovider/TestContextInitializer.java

index 08a95c2..c444735 100644 (file)
@@ -167,6 +167,7 @@ import org.xml.sax.SAXException;
 
 import x0.maceShibboleth1.AttributeAcceptancePolicyDocument;
 import x0.maceShibbolethTargetConfig1.ApplicationDocument;
+import x0.maceShibbolethTargetConfig1.GlobalConfigurationType;
 import x0.maceShibbolethTargetConfig1.LocalConfigurationType;
 import x0.maceShibbolethTargetConfig1.PluggableType;
 import x0.maceShibbolethTargetConfig1.RequestMapDocument;
@@ -178,6 +179,7 @@ import x0.maceShibbolethTargetConfig1.ApplicationsDocument.Applications;
 import x0.maceShibbolethTargetConfig1.HostDocument.Host;
 import x0.maceShibbolethTargetConfig1.HostDocument.Host.Scheme.Enum;
 import x0.maceShibbolethTargetConfig1.PathDocument.Path;
+import x0.maceShibbolethTargetConfig1.SessionsDocument.Sessions;
 import edu.internet2.middleware.shibboleth.aap.AAP;
 import edu.internet2.middleware.shibboleth.aap.AttributeRule;
 import edu.internet2.middleware.shibboleth.aap.provider.XMLAAPProvider;
@@ -352,10 +354,22 @@ public class ServiceProviderConfig {
                        throw new ShibbolethConfigurationException("Problem parsing XML in "+configFilePath, e);
                }
         loadConfigBean(configDoc);
-
+        
                return;
        }
-       
+    
+    public long getDefaultAttributeLifetime() {
+        return config.getGlobal().getMemorySessionCache().getDefaultLifetime();
+    }
+
+    public long getAAConnectTimeout() {
+        return config.getGlobal().getMemorySessionCache().getAAConnectTimeout();
+    }
+    public long getAATimeout() {
+        return config.getGlobal().getMemorySessionCache().getAATimeout();
+    }
+    
+    
        /*
         * Given a URL, determine its ApplicationId from the RequestMap config.
         * 
@@ -941,7 +955,16 @@ public class ServiceProviderConfig {
                void addAapUri(String uri) {
                        aapUris.add(uri);
                }
-               
+        
+        long getMaxSessionLife() {
+            Sessions sessions = applicationConfig.getSessions();
+            return sessions.getLifetime();
+        }
+        long getUnusedSessionTimeout() {
+            Sessions sessions = applicationConfig.getSessions();
+            return sessions.getTimeout();
+        }
+        
                /**
                 * Return the current array of objects that implement the
                 * ...metadata.Metadata interface
index 7db7211..99e1ecd 100644 (file)
@@ -126,16 +126,18 @@ public class ServiceProviderContext {
         */
        private ServiceProviderContext() {
        }
+    
+    public void initialize() {
+        // Post-construction initialization of elements that require
+        // a reference back to the context.
+        sessionManager = new SessionManager();
+    }
        
        
        
        // property accessor methods
 
-       public synchronized SessionManager getSessionManager() {
-           // deferred allocation, since sessionManger needs a reference
-           // back to context.
-           if (sessionManager==null)
-                   sessionManager = new SessionManager();
+       public SessionManager getSessionManager() {
                return sessionManager;
        }
 
index b142688..87f299e 100644 (file)
@@ -79,7 +79,7 @@ public class ServletContextInitializer {
                
                try {
                        log.info("Initializing Service Provider.");
-                       
+                       context.initialize();
                        ServiceProviderConfig config = new ServiceProviderConfig();
                        context.setServiceProviderConfig(config);
                        
@@ -90,8 +90,6 @@ public class ServletContextInitializer {
                        
                        // could config.addOrReplaceXXXImplementor()
                        
-                       context.setServiceProviderConfig(config);
-
                        log.info("Service Provider initialization complete.");
 
                } catch (ShibbolethConfigurationException ex) {
index 8a747ce..05c9e7c 100644 (file)
@@ -33,7 +33,7 @@ import org.opensaml.SAMLResponse;
 /**
  * Session object holds Authentication and Attribute Assertions for one
  * remote Browser/User.<br>
- * Each session generates its own UUID key.<br>
+ * Each session generates its own random key.<br>
  * The collection of Session objects may be checkpointed to disk using
  * any attractive persistence framework, Object Relational mapping, 
  * or, hell, just serialize the objects to a flat file if you like.
@@ -41,25 +41,30 @@ import org.opensaml.SAMLResponse;
  *  @author Howard Gilbert
  */
 public class Session implements Serializable {
-       
-       // Default values from Shibboleth documentation
-       private static final int DEFAULTTIMEOUT = 1800000;
-       public static final int DEFAULTLIFETIME = 3600000;
-       
-       Session(String key) {
-               // Should only be created by SessionManager
+    private long maxSessionLife;
+    private long unusedSessionTimeout;
+    private long defaultAttributeLifetime;
+       
+    /**
+     * Create a Session object. Only used by the Session Manager, so it has package scope.
+     * 
+     * @param key Random generated sessionId string
+     * @param maxSessionLife Maximum time this Session can remain valid
+     * @param unusedSessionTimeout Discard an unused Session
+     * @param defaultAttributeLifetime Default attribute validity time
+     */
+       Session(String key, 
+            long maxSessionLife, 
+            long unusedSessionTimeout, 
+            long defaultAttributeLifetime) {
                if (key==null)
                        throw new IllegalArgumentException();
            this.key=key;
-           this.timestamp = System.currentTimeMillis();
-       }
-       
-       /**
-        * For testing, create a Session that may already be timed out.
-        */
-       Session(String key, long timestamp) {
-           this.key=key;
-           this.timestamp = timestamp;
+           this.lastused = System.currentTimeMillis();
+        this.created = this.lastused;
+        this.maxSessionLife=maxSessionLife;
+        this.unusedSessionTimeout=unusedSessionTimeout;
+        this.defaultAttributeLifetime=defaultAttributeLifetime;
        }
        
        // Properties
@@ -93,30 +98,16 @@ public class Session implements Serializable {
                this.entityId = entityId;
        }
        
-       private long lifetime = DEFAULTLIFETIME;
-       public long getLifetime() {
-               return lifetime;
-       }
-       public void setLifetime(long lifetime) {
-               this.lifetime = lifetime;
-       }
-       
-       private long timeout=DEFAULTTIMEOUT;
-       public long getTimeout() {
-               return timeout;
-       }
-       public void setTimeout(long timeout) {
-               this.timeout = timeout;
-       }
-       
-    // private persisted variable
-       private long timestamp = 0;
+       private long lastused = 0;
+    private long created = 0;
        
        public boolean isExpired() {
                long now = System.currentTimeMillis();
-               if (lifetime>0 && timestamp+lifetime<now)
+               if (maxSessionLife>0 && 
+                created+maxSessionLife<now)
                        return true;
-               if (timeout>0 && timestamp+timeout<now)
+               if (unusedSessionTimeout>0 && 
+                lastused+unusedSessionTimeout<now)
                        return true;
                return false;
        }
@@ -149,10 +140,37 @@ public class Session implements Serializable {
                this.attributeResponse = attributeResponse;
        }
 
-       
-       public void renew(){
-               timestamp = System.currentTimeMillis();
+       /**
+     * Called by Session Manager when the Session is used. Reset the 
+     * unused timer.
+        */
+       void renew(){
+               lastused = System.currentTimeMillis();
        }
+    
+    public long getDefaultAttributeLifetime() {
+        return defaultAttributeLifetime;
+    }
+
+    public void setDefaultAttributeLifetime(long defaultAttributeLifetime) {
+        this.defaultAttributeLifetime = defaultAttributeLifetime;
+    }
+
+    public long getMaxSessionLife() {
+        return maxSessionLife;
+    }
+
+    public void setMaxSessionLife(long maxSessionLife) {
+        this.maxSessionLife = maxSessionLife;
+    }
+
+    public long getUnusedSessionTimeout() {
+        return unusedSessionTimeout;
+    }
+
+    public void setUnusedSessionTimeout(long unusedSessionTimeout) {
+        this.unusedSessionTimeout = unusedSessionTimeout;
+    }
        
 
 }
index 5be592a..50b547f 100644 (file)
@@ -43,8 +43,6 @@ import org.opensaml.SAMLAuthenticationStatement;
 import org.opensaml.SAMLResponse;
 import org.opensaml.SAMLStatement;
 
-import x0.maceShibbolethTargetConfig1.SessionsDocument.Sessions;
-
 import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderConfig.ApplicationInfo;
 
 /**
@@ -69,15 +67,13 @@ public class SessionManager {
        private TreeMap sessions = new TreeMap(); // The memory cache of Sessions
        
        private static ServiceProviderContext context = ServiceProviderContext.getInstance();
-
-       private static SecureRandom rand = new SecureRandom();
-       private static final String table = "0123456789" +
-               "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+
-               "abcdefgjikjlmnopqrstuvwxyz"+
-               "$@";
+    
+    
     
     /**
-     * Generate a 16 byte random ASCII string.
+     * Generate a 16 byte random ASCII string using 
+     * cryptgraphically strong Java random generator.
+     * 
      * @return generated string
      */
        public String generateKey() {
@@ -94,12 +90,17 @@ public class SessionManager {
            } while (null!=sessions.get(key));
            return key;
        }
+    private static SecureRandom rand = new SecureRandom();
+    private static final String table = "0123456789" +
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"+
+        "abcdefgjikjlmnopqrstuvwxyz"+
+        "$@";
        
        
        /**
      * Find a Session object given its sessionID key. 
      * 
-     * <p>Will not match uninitialized Sessions.</p>
+     * <p>Will not match uninitialized (reserved) Sessions.</p>
      * 
      * @param sessionId ID and key of session
      * @param applicationId Sanity check, must match session contents
@@ -231,47 +232,53 @@ public class SessionManager {
        public 
                        String 
        newSession(
-                       String applicationId, 
-                       String ipaddr,
-                       String entityId,
-                       SAMLAssertion assertion,
-                       SAMLAuthenticationStatement authenticationStatement,
+                       String applicationId, // not null
+                       String ipaddr,        // may be null
+                       String entityId,      // not null
+                       SAMLAssertion assertion, //not null
+                       SAMLAuthenticationStatement authenticationStatement, // may be null
                        String emptySessionId // may be null
                        ){
                
+        if (applicationId==null)
+            throw new IllegalArgumentException("applicationId null");
+        if (entityId==null)
+            throw new IllegalArgumentException("entityId null");
+        if (assertion==null)
+            throw new IllegalArgumentException("assertion null");
+        
                ServiceProviderConfig config = context.getServiceProviderConfig();
                ApplicationInfo appinfo = config.getApplication(applicationId);
-               Sessions appSessionValues = appinfo.getApplicationConfig().getSessions();
-               
                String sessionId = null;
-               boolean isUpdate = false;
+               boolean isUpdate = false; // Assume new object
                
-               Session session;
-               if (emptySessionId==null) {
-                   session = new Session(generateKey());
-               } else {
+        /*
+         * If the Id of a reserved, empty session is provided, then find
+         * that object and fill it in. Otherwise, create a new object.
+         */
+               Session session = null;
+               if (emptySessionId!=null) {
                    session = findEmptySession(emptySessionId);
-                   if (session==null) {
-                           session = new Session(generateKey());
-                   } else {
-                       isUpdate=true;
-                   }
+               }
+               if (session==null) {
+                   session = new Session(generateKey(),
+                           appinfo.getMaxSessionLife(), 
+                           appinfo.getUnusedSessionTimeout(), 
+                           config.getDefaultAttributeLifetime());
+               } else {
+                   isUpdate=true; // mark so object is updated, not added
                }
                session.setApplicationId(applicationId);
-               session.setIpaddr(ipaddr);
+               session.setIpaddr(ipaddr); // may be null
                session.setEntityId(entityId);
                
                session.setAuthenticationAssertion(assertion);
-               session.setAuthenticationStatement(authenticationStatement);
-               
-               // Get lifetime and timeout from Applications/Sessions in config file 
-               session.setLifetime(appSessionValues.getLifetime()*1000);
-               session.setTimeout(appSessionValues.getTimeout()*1000);
+               session.setAuthenticationStatement(authenticationStatement); // may be null
                
                sessionId = session.getKey();
 
                if (isUpdate)
-                       update(session);
+                       update(session);  
                else
                        add(session);
                
@@ -292,9 +299,16 @@ public class SessionManager {
 reserveSession(
        String applicationId 
        ){
-
+        if (applicationId==null)
+            throw new IllegalArgumentException("applicationId null");
+            
+        ServiceProviderConfig config = context.getServiceProviderConfig();
+        ApplicationInfo appinfo = config.getApplication(applicationId);
            String sessionId = null;
-           Session session= new Session(generateKey());
+           Session session= new Session(generateKey(),
+                appinfo.getMaxSessionLife(), 
+                appinfo.getUnusedSessionTimeout(), 
+                config.getDefaultAttributeLifetime());
            session.setApplicationId(applicationId);
            
            sessionId = session.getKey();
@@ -316,7 +330,7 @@ reserveSession(
                        SessionCache cache) {
                
                if (cache==null)
-                       throw new IllegalArgumentException();
+                       throw new IllegalArgumentException("Session cache is null");
            log.info("Enabling Session Cache");
                /*
                 * The following code supports dynamic switching from
index 27d8a36..25a68c6 100644 (file)
@@ -14,7 +14,9 @@ public class TestContextInitializer extends SPTestCase {
      * @param configFileName URL format string pointing to configuration file
      * @throws ShibbolethConfigurationException
      */
-       public void initServiceProvider(String configFileName) throws ShibbolethConfigurationException{
+       public void initServiceProvider(String configFileName) 
+        throws ShibbolethConfigurationException{
+            context.initialize();
                        ServiceProviderConfig config = new ServiceProviderConfig();
                        context.setServiceProviderConfig(config);
                        config.loadConfigObjects(configFileName);