From 1c36eab1c7d81a8b3adbf28d3930f8c07152bb81 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Thu, 19 Sep 2013 20:58:40 -0700 Subject: [PATCH] Ticket #362: Added session timeout. --- .../properties/LongGuacamoleProperty.java | 67 +++++++++++ .../properties/BasicGuacamoleProperties.java | 11 ++ .../rest/auth/BasicTokenUserContextMap.java | 106 +++++++++++++++++- .../basic/rest/auth/TokenUserContextMap.java | 23 +++- 4 files changed, 202 insertions(+), 5 deletions(-) create mode 100644 guacamole-ext/src/main/java/org/glyptodon/guacamole/properties/LongGuacamoleProperty.java diff --git a/guacamole-ext/src/main/java/org/glyptodon/guacamole/properties/LongGuacamoleProperty.java b/guacamole-ext/src/main/java/org/glyptodon/guacamole/properties/LongGuacamoleProperty.java new file mode 100644 index 000000000..7f6c86aac --- /dev/null +++ b/guacamole-ext/src/main/java/org/glyptodon/guacamole/properties/LongGuacamoleProperty.java @@ -0,0 +1,67 @@ + +package org.glyptodon.guacamole.properties; + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is guacamole-ext. + * + * The Initial Developer of the Original Code is + * Michael Jumper. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleServerException; + +/** + * A GuacamoleProperty whose value is an long. + * + * @author James Muehlner + */ +public abstract class LongGuacamoleProperty implements GuacamoleProperty { + + @Override + public Long parseValue(String value) throws GuacamoleException { + + // If no property provided, return null. + if (value == null) + return null; + + try { + Long longValue = new Long(value); + return longValue; + } + catch (NumberFormatException e) { + throw new GuacamoleServerException("Property \"" + getName() + "\" must be an long.", e); + } + + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/BasicGuacamoleProperties.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/BasicGuacamoleProperties.java index 91e1d384e..a6054399c 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/BasicGuacamoleProperties.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/properties/BasicGuacamoleProperties.java @@ -20,6 +20,7 @@ package org.glyptodon.guacamole.net.basic.properties; */ import org.glyptodon.guacamole.properties.FileGuacamoleProperty; +import org.glyptodon.guacamole.properties.LongGuacamoleProperty; /** * Properties used by the default Guacamole web application. @@ -64,4 +65,14 @@ public class BasicGuacamoleProperties { }; + /** + * The session timeout for the API, in milliseconds. + */ + public static final LongGuacamoleProperty API_SESSION_TIMEOUT = new LongGuacamoleProperty() { + + @Override + public String getName() { return "api-session-timeout"; } + + }; + } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java index b6ba99882..204abf46c 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/BasicTokenUserContextMap.java @@ -18,13 +18,113 @@ package org.glyptodon.guacamole.net.basic.rest.auth; * along with this program. If not, see . */ +import java.util.Date; import java.util.HashMap; +import java.util.Map; +import org.glyptodon.guacamole.GuacamoleException; import org.glyptodon.guacamole.net.auth.UserContext; +import org.glyptodon.guacamole.net.basic.properties.BasicGuacamoleProperties; +import org.glyptodon.guacamole.properties.GuacamoleProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * A Basic, HashMap-based implementation of the TokenUserContextMap. + * A basic, HashMap-based implementation of the TokenUserContextMap with support + * for session timeouts. * * @author James Muehlner */ -public class BasicTokenUserContextMap extends HashMap - implements TokenUserContextMap {} +public class BasicTokenUserContextMap implements TokenUserContextMap { + + /** + * Logger for this class. + */ + private static Logger logger = LoggerFactory.getLogger(BasicTokenUserContextMap.class); + + /** + * The last time a user with a specific auth token accessed the API. + */ + private Map lastAccessTimeMap = new HashMap(); + + /** + * Keeps track of the authToken to UserContext mapping. + */ + private Map userContextMap = new HashMap(); + + /** + * The session timeout configuration for an API session. + */ + private final long SESSION_TIMEOUT; + + /** + * Create a new BasicTokenUserContextMap and initialize the session timeout value. + */ + public BasicTokenUserContextMap() { + + // Set up the authToken => userContext hashmap + super(); + + // Set up the SESSION_TIMEOUT value, with a one hour default. + long sessionTimeoutValue = 3600000l; + try { + sessionTimeoutValue = GuacamoleProperties.getProperty(BasicGuacamoleProperties.API_SESSION_TIMEOUT, 3600000l); + } catch (GuacamoleException e) { + logger.error("Unexpected GuacamoleException caught while reading API_SESSION_TIMEOUT property.", e); + } + + SESSION_TIMEOUT = sessionTimeoutValue; + } + + /** + * Evict an authentication token from the map of logged in users and last + * access times. + * + * @param authToken The authentication token to evict. + */ + private void evict(String authToken) { + userContextMap.remove(authToken); + lastAccessTimeMap.remove(authToken); + } + + /** + * Log that the user represented by this auth token has just used the API. + * + * @param authToken The authentication token to record access time for. + */ + private void logAccessTime(String authToken) { + lastAccessTimeMap.put(authToken, new Date().getTime()); + } + + private boolean sessionHasTimedOut(String authToken) { + if(!lastAccessTimeMap.containsKey(authToken)) + return true; + + long lastAccessTime = lastAccessTimeMap.get(authToken); + long currentTime = new Date().getTime(); + + return currentTime - lastAccessTime > SESSION_TIMEOUT; + } + + @Override + public UserContext get(String authToken) { + + // If the session has timed out, evict the token and force the user to log in again + if(sessionHasTimedOut(authToken)) { + evict(authToken); + return null; + } + + // Update the last access time and return the UserContext + logAccessTime(authToken); + return userContextMap.get(authToken); + } + + @Override + public void put(String authToken, UserContext userContext) { + + // Update the last access time, and create the token/UserContext mapping + logAccessTime(authToken); + userContextMap.put(authToken, userContext); + } + +} diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenUserContextMap.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenUserContextMap.java index 0f106ecfc..55ad45a8d 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenUserContextMap.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/auth/TokenUserContextMap.java @@ -18,7 +18,6 @@ package org.glyptodon.guacamole.net.basic.rest.auth; * along with this program. If not, see . */ -import java.util.Map; import org.glyptodon.guacamole.net.auth.UserContext; /** @@ -27,4 +26,24 @@ import org.glyptodon.guacamole.net.auth.UserContext; * * @author James Muehlner */ -public interface TokenUserContextMap extends Map {} +public interface TokenUserContextMap { + + /** + * Registers that a user has just logged in with the specified authToken and + * UserContext. + * + * @param authToken The authentication token for the logged in user. + * @param userContext The UserContext for the logged in user. + */ + public void put(String authToken, UserContext userContext); + + /** + * Get the UserContext for a logged in user. If the auth token does not + * represent a user who is currently logged in, returns null. + * + * @param authToken The authentication token for the logged in user. + * @return The UserContext for the given auth token, if the auth token + * represents a currently logged in user, null otherwise. + */ + public UserContext get(String authToken); +}