From 10344aeba4ce23939e03500dc55fc662eaeb3cc5 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 7 Jun 2016 20:51:34 -0700 Subject: [PATCH] GUACAMOLE-53: Implement session affinity within the GuacamoleTunnelService. --- .../ModeledConnectionGroup.java | 11 +++++ .../AbstractGuacamoleTunnelService.java | 45 +++++++++++++++++++ .../auth/jdbc/user/AuthenticatedUser.java | 44 ++++++++++++++++++ 3 files changed, 100 insertions(+) diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connectiongroup/ModeledConnectionGroup.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connectiongroup/ModeledConnectionGroup.java index 85f6cec6d..a0675f48d 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connectiongroup/ModeledConnectionGroup.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/connectiongroup/ModeledConnectionGroup.java @@ -257,5 +257,16 @@ public class ModeledConnectionGroup extends ModeledGroupedDirectoryObject getPreferredConnections(AuthenticatedUser user, + Collection identifiers) { + + // Search provided identifiers for any preferred connections + for (String identifier : identifiers) { + + // If at least one prefferred connection is found, assume it is the + // only preferred connection + if (user.isPreferredConnection(identifier)) + return Collections.singletonList(identifier); + + } + + // No preferred connections were found + return identifiers; + + } + /** * Returns a list of all balanced connections within a given connection * group. If the connection group is not balancing, or it contains no @@ -440,6 +477,10 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS if (identifiers.isEmpty()) return Collections.emptyList(); + // Restrict to preferred connections if session affinity is enabled + if (connectionGroup.isSessionAffinityEnabled()) + identifiers = getPreferredConnections(user, identifiers); + // Retrieve all children Collection models = connectionMapper.select(identifiers); List connections = new ArrayList(models.size()); @@ -535,6 +576,10 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS throw e; } + // If session affinity is enabled, prefer this connection going forward + if (connectionGroup.isSessionAffinityEnabled()) + user.preferConnection(connection.getIdentifier()); + // Connect to acquired child return assignGuacamoleTunnel(new ActiveConnectionRecord(user, connectionGroup, connection), info); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/AuthenticatedUser.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/AuthenticatedUser.java index 3322a2080..0696c8801 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/AuthenticatedUser.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/user/AuthenticatedUser.java @@ -19,6 +19,9 @@ package org.apache.guacamole.auth.jdbc.user; +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; @@ -73,6 +76,17 @@ public class AuthenticatedUser implements org.apache.guacamole.net.auth.Authenti */ private static final Pattern X_FORWARDED_FOR = Pattern.compile("^" + IP_ADDRESS_REGEX + "(, " + IP_ADDRESS_REGEX + ")*$"); + /** + * The connections which have been committed for use by this user in the + * context of a balancing connection group. Balancing connection groups + * will preferentially choose connections within this set, unless those + * connections are not children of the group in question. If a group DOES + * have at least one child connection within this set, no connections that + * are not in this set will be used. + */ + private final Set preferredConnections = + Collections.newSetFromMap(new ConcurrentHashMap()); + /** * Derives the remote host of the authenticating user from the given * credentials object. The remote host is derived from X-Forwarded-For @@ -157,6 +171,36 @@ public class AuthenticatedUser implements org.apache.guacamole.net.auth.Authenti return remoteHost; } + /** + * Returns whether the connection having the given identifier has been + * marked as preferred for this user's current Guacamole session. A + * preferred connection is always chosen in favor of other connections when + * it is a child of a balancing connection group. + * + * @param identifier + * The identifier of the connection to test. + * + * @return + * true if the connection having the given identifier has been marked + * as preferred, false otherwise. + */ + public boolean isPreferredConnection(String identifier) { + return preferredConnections.contains(identifier); + } + + /** + * Marks the connection having the given identifier as preferred for this + * user's current Guacamole session. A preferred connection is always chosen + * in favor of other connections when it is a child of a balancing + * connection group. + * + * @param identifier + * The identifier of the connection to prefer. + */ + public void preferConnection(String identifier) { + preferredConnections.add(identifier); + } + @Override public AuthenticationProvider getAuthenticationProvider() { return authenticationProvider;