GUACAMOLE-53: Implement session affinity within the GuacamoleTunnelService.

This commit is contained in:
Michael Jumper
2016-06-07 20:51:34 -07:00
parent b7ac434d41
commit 10344aeba4
3 changed files with 100 additions and 0 deletions

View File

@@ -257,5 +257,16 @@ public class ModeledConnectionGroup extends ModeledGroupedDirectoryObject<Connec
} }
/**
* Returns whether individual users should be consistently assigned the same
* connection within a balancing group until they log out.
*
* @return
* Whether individual users should be consistently assigned the same
* connection within a balancing group until they log out.
*/
public boolean isSessionAffinityEnabled() {
return getModel().isSessionAffinityEnabled();
}
} }

View File

@@ -412,6 +412,43 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
} }
/**
* Filters the given collection of connection identifiers, returning a new
* collection which contains only those identifiers which are preferred. If
* no connection identifiers within the given collection are preferred, the
* collection is left untouched.
*
* @param user
* The user whose preferred connections should be used to filter the
* given collection of connection identifiers.
*
* @param identifiers
* The collection of connection identifiers that should be filtered.
*
* @return
* A collection of connection identifiers containing only the subset of
* connection identifiers which are also preferred or, if none of the
* provided identifiers are preferred, the original collection of
* identifiers.
*/
private Collection<String> getPreferredConnections(AuthenticatedUser user,
Collection<String> 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 * Returns a list of all balanced connections within a given connection
* group. If the connection group is not balancing, or it contains no * 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()) if (identifiers.isEmpty())
return Collections.<ModeledConnection>emptyList(); return Collections.<ModeledConnection>emptyList();
// Restrict to preferred connections if session affinity is enabled
if (connectionGroup.isSessionAffinityEnabled())
identifiers = getPreferredConnections(user, identifiers);
// Retrieve all children // Retrieve all children
Collection<ConnectionModel> models = connectionMapper.select(identifiers); Collection<ConnectionModel> models = connectionMapper.select(identifiers);
List<ModeledConnection> connections = new ArrayList<ModeledConnection>(models.size()); List<ModeledConnection> connections = new ArrayList<ModeledConnection>(models.size());
@@ -535,6 +576,10 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
throw e; throw e;
} }
// If session affinity is enabled, prefer this connection going forward
if (connectionGroup.isSessionAffinityEnabled())
user.preferConnection(connection.getIdentifier());
// Connect to acquired child // Connect to acquired child
return assignGuacamoleTunnel(new ActiveConnectionRecord(user, connectionGroup, connection), info); return assignGuacamoleTunnel(new ActiveConnectionRecord(user, connectionGroup, connection), info);

View File

@@ -19,6 +19,9 @@
package org.apache.guacamole.auth.jdbc.user; 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.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest; 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 + ")*$"); 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<String> preferredConnections =
Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
/** /**
* Derives the remote host of the authenticating user from the given * Derives the remote host of the authenticating user from the given
* credentials object. The remote host is derived from X-Forwarded-For * 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; 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 @Override
public AuthenticationProvider getAuthenticationProvider() { public AuthenticationProvider getAuthenticationProvider() {
return authenticationProvider; return authenticationProvider;