GUACAMOLE-5: Implement joining of shared connections via temporary credentials.

This commit is contained in:
Michael Jumper
2016-07-21 14:20:28 -07:00
parent bfa5c38123
commit 19f80dac95
6 changed files with 363 additions and 64 deletions

View File

@@ -75,6 +75,13 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC
*/ */
private String username; private String username;
/**
* The connection ID of the connection as determined by guacd, not to be
* confused with the connection identifier determined by the database. This
* is the ID that must be supplied to guacd if joining this connection.
*/
private String connectionID;
/** /**
* The underlying GuacamoleTunnel. * The underlying GuacamoleTunnel.
*/ */
@@ -107,6 +114,7 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC
// Copy all non-sensitive data from given record // Copy all non-sensitive data from given record
this.connection = activeConnectionRecord.getConnection(); this.connection = activeConnectionRecord.getConnection();
this.connectionID = activeConnectionRecord.getConnectionID();
this.sharingProfileIdentifier = activeConnectionRecord.getSharingProfileIdentifier(); this.sharingProfileIdentifier = activeConnectionRecord.getSharingProfileIdentifier();
this.identifier = activeConnectionRecord.getUUID().toString(); this.identifier = activeConnectionRecord.getUUID().toString();
this.startDate = activeConnectionRecord.getStartDate(); this.startDate = activeConnectionRecord.getStartDate();
@@ -142,6 +150,19 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC
return connection; return connection;
} }
/**
* Returns the connection ID of the in-progress connection as determined by
* guacd, not to be confused with the connection identifier determined by
* the database. This is the ID that must be supplied to guacd if joining
* this connection.
*
* @return
* The ID of the in-progress connection, as determined by guacd.
*/
public String getConnectionID() {
return connectionID;
}
@Override @Override
public String getConnectionIdentifier() { public String getConnectionIdentifier() {
return connection.getIdentifier(); return connection.getIdentifier();

View File

@@ -19,16 +19,17 @@
package org.apache.guacamole.auth.jdbc.sharing; package org.apache.guacamole.auth.jdbc.sharing;
import com.google.inject.Inject;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleUnsupportedException;
import org.apache.guacamole.auth.jdbc.activeconnection.TrackedActiveConnection; import org.apache.guacamole.auth.jdbc.activeconnection.TrackedActiveConnection;
import org.apache.guacamole.auth.jdbc.connectiongroup.RootConnectionGroup; import org.apache.guacamole.auth.jdbc.connectiongroup.RootConnectionGroup;
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile; import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionRecord; import org.apache.guacamole.net.auth.ConnectionRecord;
@@ -43,6 +44,12 @@ import org.apache.guacamole.protocol.GuacamoleConfiguration;
*/ */
public class SharedConnection implements Connection { public class SharedConnection implements Connection {
/**
* Service for establishing tunnels to Guacamole connections.
*/
@Inject
private GuacamoleTunnelService tunnelService;
/** /**
* Randomly-generated unique identifier, guaranteeing this shared connection * Randomly-generated unique identifier, guaranteeing this shared connection
* does not duplicate the identifying information of the underlying * does not duplicate the identifying information of the underlying
@@ -130,8 +137,8 @@ public class SharedConnection implements Connection {
@Override @Override
public GuacamoleTunnel connect(GuacamoleClientInformation info) public GuacamoleTunnel connect(GuacamoleClientInformation info)
throws GuacamoleException { throws GuacamoleException {
// STUB return tunnelService.getGuacamoleTunnel(user, activeConnection,
throw new GuacamoleUnsupportedException("Connecting to shared connections is not yet implemented."); sharingProfile, info);
} }
@Override @Override

View File

@@ -38,10 +38,11 @@ import org.apache.guacamole.auth.jdbc.connection.ConnectionRecordMapper;
import org.apache.guacamole.auth.jdbc.connection.ConnectionModel; import org.apache.guacamole.auth.jdbc.connection.ConnectionModel;
import org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel; import org.apache.guacamole.auth.jdbc.connection.ConnectionRecordModel;
import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterModel; import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterModel;
import org.apache.guacamole.auth.jdbc.user.UserModel;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleResourceNotFoundException;
import org.apache.guacamole.GuacamoleSecurityException; import org.apache.guacamole.GuacamoleSecurityException;
import org.apache.guacamole.auth.jdbc.JDBCEnvironment; import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
import org.apache.guacamole.auth.jdbc.activeconnection.TrackedActiveConnection;
import org.apache.guacamole.auth.jdbc.connection.ConnectionMapper; import org.apache.guacamole.auth.jdbc.connection.ConnectionMapper;
import org.apache.guacamole.environment.Environment; import org.apache.guacamole.environment.Environment;
import org.apache.guacamole.net.GuacamoleSocket; import org.apache.guacamole.net.GuacamoleSocket;
@@ -55,6 +56,11 @@ import org.apache.guacamole.token.StandardTokens;
import org.apache.guacamole.token.TokenFilter; import org.apache.guacamole.token.TokenFilter;
import org.mybatis.guice.transactional.Transactional; import org.mybatis.guice.transactional.Transactional;
import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper; import org.apache.guacamole.auth.jdbc.connection.ConnectionParameterMapper;
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionUser;
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterMapper;
import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileParameterModel;
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
/** /**
@@ -88,7 +94,13 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
* Mapper for accessing connection parameters. * Mapper for accessing connection parameters.
*/ */
@Inject @Inject
private ConnectionParameterMapper parameterMapper; private ConnectionParameterMapper connectionParameterMapper;
/**
* Mapper for accessing sharing profile parameters.
*/
@Inject
private SharingProfileParameterMapper sharingProfileParameterMapper;
/** /**
* Mapper for accessing connection history. * Mapper for accessing connection history.
@@ -141,7 +153,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
* @throws GuacamoleException * @throws GuacamoleException
* If access is denied to the given user for any reason. * If access is denied to the given user for any reason.
*/ */
protected abstract ModeledConnection acquire(AuthenticatedUser user, protected abstract ModeledConnection acquire(RemoteAuthenticatedUser user,
List<ModeledConnection> connections) throws GuacamoleException; List<ModeledConnection> connections) throws GuacamoleException;
/** /**
@@ -155,7 +167,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
* @param connection * @param connection
* The connection being released. * The connection being released.
*/ */
protected abstract void release(AuthenticatedUser user, protected abstract void release(RemoteAuthenticatedUser user,
ModeledConnection connection); ModeledConnection connection);
/** /**
@@ -172,7 +184,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
* @throws GuacamoleException * @throws GuacamoleException
* If access is denied to the given user for any reason. * If access is denied to the given user for any reason.
*/ */
protected abstract void acquire(AuthenticatedUser user, protected abstract void acquire(RemoteAuthenticatedUser user,
ModeledConnectionGroup connectionGroup) throws GuacamoleException; ModeledConnectionGroup connectionGroup) throws GuacamoleException;
/** /**
@@ -186,7 +198,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
* @param connectionGroup * @param connectionGroup
* The connection group being released. * The connection group being released.
*/ */
protected abstract void release(AuthenticatedUser user, protected abstract void release(RemoteAuthenticatedUser user,
ModeledConnectionGroup connectionGroup); ModeledConnectionGroup connectionGroup);
/** /**
@@ -206,7 +218,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
* A GuacamoleConfiguration containing the protocol and parameters from * A GuacamoleConfiguration containing the protocol and parameters from
* the given connection. * the given connection.
*/ */
private GuacamoleConfiguration getGuacamoleConfiguration(AuthenticatedUser user, private GuacamoleConfiguration getGuacamoleConfiguration(RemoteAuthenticatedUser user,
ModeledConnection connection) { ModeledConnection connection) {
// Generate configuration from available data // Generate configuration from available data
@@ -217,7 +229,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
config.setProtocol(model.getProtocol()); config.setProtocol(model.getProtocol());
// Set parameters from associated data // Set parameters from associated data
Collection<ConnectionParameterModel> parameters = parameterMapper.select(connection.getIdentifier()); Collection<ConnectionParameterModel> parameters = connectionParameterMapper.select(connection.getIdentifier());
for (ConnectionParameterModel parameter : parameters) for (ConnectionParameterModel parameter : parameters)
config.setParameter(parameter.getName(), parameter.getValue()); config.setParameter(parameter.getName(), parameter.getValue());
@@ -232,6 +244,52 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
} }
/**
* Returns a guacamole configuration which joins the active connection
* having the given ID, using the provided sharing profile to restrict the
* access provided to the user accessing the shared connection. If tokens
* are used in the connection parameter values of the sharing profile,
* credentials from the given user will be substituted appropriately.
*
* @param user
* The user whose credentials should be used if necessary.
*
* @param sharingProfile
* The sharing profile whose associated parameters dictate the level
* of access granted to the user joining the connection.
*
* @param connectionID
* The ID of the connection being joined, as provided by guacd when the
* original connection was established, or null if a new connection
* should be created instead.
*
* @return
* A GuacamoleConfiguration containing the protocol and parameters from
* the given connection.
*/
private GuacamoleConfiguration getGuacamoleConfiguration(RemoteAuthenticatedUser user,
ModeledSharingProfile sharingProfile, String connectionID) {
// Generate configuration from available data
GuacamoleConfiguration config = new GuacamoleConfiguration();
config.setConnectionID(connectionID);
// Set parameters from associated data
Collection<SharingProfileParameterModel> parameters = sharingProfileParameterMapper.select(sharingProfile.getIdentifier());
for (SharingProfileParameterModel parameter : parameters)
config.setParameter(parameter.getName(), parameter.getValue());
// Build token filter containing credential tokens
TokenFilter tokenFilter = new TokenFilter();
StandardTokens.addStandardTokens(tokenFilter, user.getCredentials());
// Filter the configuration
tokenFilter.filterValues(config.getParameters());
return config;
}
/** /**
* Saves the given ActiveConnectionRecord to the database. The end date of * Saves the given ActiveConnectionRecord to the database. The end date of
* the saved record will be populated with the current time. * the saved record will be populated with the current time.
@@ -241,17 +299,15 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
*/ */
private void saveConnectionRecord(ActiveConnectionRecord record) { private void saveConnectionRecord(ActiveConnectionRecord record) {
// Get associated connection
ModeledConnection connection = record.getConnection();
// Get associated models // Get associated models
AuthenticatedUser user = record.getUser();
ConnectionRecordModel recordModel = new ConnectionRecordModel(); ConnectionRecordModel recordModel = new ConnectionRecordModel();
// Copy user information and timestamps into new record // Copy user information and timestamps into new record
recordModel.setUsername(user.getIdentifier()); recordModel.setUsername(record.getUsername());
recordModel.setConnectionIdentifier(connection.getIdentifier()); recordModel.setConnectionIdentifier(record.getConnectionIdentifier());
recordModel.setConnectionName(connection.getName()); recordModel.setConnectionName(record.getConnectionName());
recordModel.setSharingProfileIdentifier(record.getSharingProfileIdentifier());
recordModel.setSharingProfileName(record.getSharingProfileName());
recordModel.setStartDate(record.getStartDate()); recordModel.setStartDate(record.getStartDate());
recordModel.setEndDate(new Date()); recordModel.setEndDate(new Date());
@@ -329,19 +385,26 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
if (!hasRun.compareAndSet(false, true)) if (!hasRun.compareAndSet(false, true))
return; return;
// Get original user and connection // Remove underlying tunnel from list of active tunnels
AuthenticatedUser user = activeConnection.getUser();
ModeledConnection connection = activeConnection.getConnection();
// Get associated identifiers
String identifier = connection.getIdentifier();
String parentIdentifier = connection.getParentIdentifier();
// Release connection
activeTunnels.remove(activeConnection.getUUID().toString()); activeTunnels.remove(activeConnection.getUUID().toString());
activeConnections.remove(identifier, activeConnection);
activeConnectionGroups.remove(parentIdentifier, activeConnection); // Get original user
release(user, connection); RemoteAuthenticatedUser user = activeConnection.getUser();
// Release the associated connection if this is the primary connection
if (activeConnection.isPrimaryConnection()) {
// Get connection and associated identifiers
ModeledConnection connection = activeConnection.getConnection();
String identifier = connection.getIdentifier();
String parentIdentifier = connection.getParentIdentifier();
// Release connection
activeConnections.remove(identifier, activeConnection);
activeConnectionGroups.remove(parentIdentifier, activeConnection);
release(user, connection);
}
// Release any associated group // Release any associated group
if (activeConnection.hasBalancingGroup()) if (activeConnection.hasBalancingGroup())
@@ -379,25 +442,44 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
* while connection configuration information is being retrieved. * while connection configuration information is being retrieved.
*/ */
private GuacamoleTunnel assignGuacamoleTunnel(ActiveConnectionRecord activeConnection, private GuacamoleTunnel assignGuacamoleTunnel(ActiveConnectionRecord activeConnection,
GuacamoleClientInformation info) GuacamoleClientInformation info) throws GuacamoleException {
throws GuacamoleException {
ModeledConnection connection = activeConnection.getConnection();
// Record new active connection // Record new active connection
Runnable cleanupTask = new ConnectionCleanupTask(activeConnection); Runnable cleanupTask = new ConnectionCleanupTask(activeConnection);
activeTunnels.put(activeConnection.getUUID().toString(), activeConnection); activeTunnels.put(activeConnection.getUUID().toString(), activeConnection);
activeConnections.put(connection.getIdentifier(), activeConnection);
activeConnectionGroups.put(connection.getParentIdentifier(), activeConnection);
try { try {
GuacamoleConfiguration config;
// Pull configuration directly from the connection if we are not
// joining an active connection
if (activeConnection.isPrimaryConnection()) {
ModeledConnection connection = activeConnection.getConnection();
activeConnections.put(connection.getIdentifier(), activeConnection);
activeConnectionGroups.put(connection.getParentIdentifier(), activeConnection);
config = getGuacamoleConfiguration(activeConnection.getUser(), connection);
}
// If we ARE joining an active connection, generate a configuration
// which does so
else {
// Verify that the connection ID is known
String connectionID = activeConnection.getConnectionID();
if (connectionID == null)
throw new GuacamoleResourceNotFoundException("No existing connection to be joined.");
// Build configuration from the sharing profile and the ID of
// the connection being joined
config = getGuacamoleConfiguration(activeConnection.getUser(),
activeConnection.getSharingProfile(), connectionID);
}
// Obtain socket which will automatically run the cleanup task // Obtain socket which will automatically run the cleanup task
GuacamoleSocket socket = new ConfiguredGuacamoleSocket( ConfiguredGuacamoleSocket socket = new ConfiguredGuacamoleSocket(
getUnconfiguredGuacamoleSocket(cleanupTask), getUnconfiguredGuacamoleSocket(cleanupTask), config, info);
getGuacamoleConfiguration(activeConnection.getUser(), connection),
info
);
// Assign and return new tunnel // Assign and return new tunnel
return activeConnection.assignGuacamoleTunnel(socket); return activeConnection.assignGuacamoleTunnel(socket);
@@ -596,4 +678,17 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
} }
@Override
@Transactional
public GuacamoleTunnel getGuacamoleTunnel(SharedConnectionUser user,
TrackedActiveConnection activeConnection,
ModeledSharingProfile sharingProfile,
GuacamoleClientInformation info)
throws GuacamoleException {
// Connect to shared connection
return assignGuacamoleTunnel(new ActiveConnectionRecord(user, activeConnection, sharingProfile), info);
}
} }

View File

@@ -21,13 +21,16 @@ package org.apache.guacamole.auth.jdbc.tunnel;
import java.util.Date; import java.util.Date;
import java.util.UUID; import java.util.UUID;
import org.apache.guacamole.auth.jdbc.activeconnection.TrackedActiveConnection;
import org.apache.guacamole.auth.jdbc.connection.ModeledConnection; import org.apache.guacamole.auth.jdbc.connection.ModeledConnection;
import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
import org.apache.guacamole.auth.jdbc.user.AuthenticatedUser; import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
import org.apache.guacamole.net.AbstractGuacamoleTunnel; import org.apache.guacamole.net.AbstractGuacamoleTunnel;
import org.apache.guacamole.net.GuacamoleSocket; import org.apache.guacamole.net.GuacamoleSocket;
import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.ConnectionRecord; import org.apache.guacamole.net.auth.ConnectionRecord;
import org.apache.guacamole.protocol.ConfiguredGuacamoleSocket;
/** /**
@@ -44,7 +47,7 @@ public class ActiveConnectionRecord implements ConnectionRecord {
* The user that connected to the connection associated with this connection * The user that connected to the connection associated with this connection
* record. * record.
*/ */
private final AuthenticatedUser user; private final RemoteAuthenticatedUser user;
/** /**
* The balancing group from which the associated connection was chosen, if * The balancing group from which the associated connection was chosen, if
@@ -57,6 +60,13 @@ public class ActiveConnectionRecord implements ConnectionRecord {
*/ */
private final ModeledConnection connection; private final ModeledConnection connection;
/**
* The sharing profile that was used to access the connection associated
* with this connection record. If the connection was accessed directly
* (without involving a sharing profile), this will be null.
*/
private final ModeledSharingProfile sharingProfile;
/** /**
* The time this connection record was created. * The time this connection record was created.
*/ */
@@ -66,13 +76,54 @@ public class ActiveConnectionRecord implements ConnectionRecord {
* The UUID that will be assigned to the underlying tunnel. * The UUID that will be assigned to the underlying tunnel.
*/ */
private final UUID uuid = UUID.randomUUID(); private final UUID uuid = UUID.randomUUID();
/**
* The connection ID of the connection as determined by guacd, not to be
* confused with the connection identifier determined by the database. This
* is the ID that must be supplied to guacd if joining this connection.
*/
private String connectionID;
/** /**
* The GuacamoleTunnel used by the connection associated with this * The GuacamoleTunnel used by the connection associated with this
* connection record. * connection record.
*/ */
private GuacamoleTunnel tunnel; private GuacamoleTunnel tunnel;
/**
* Creates a new connection record associated with the given user,
* connection, balancing connection group, and sharing profile. The given
* balancing connection group MUST be the connection group from which the
* given connection was chosen, and the given sharing profile MUST be the
* sharing profile that was used to share access to the given connection.
* The start date of this connection record will be the time of its
* creation.
*
* @param user
* The user that connected to the connection associated with this
* connection record.
*
* @param balancingGroup
* The balancing group from which the given connection was chosen, or
* null if no balancing group is being used.
*
* @param connection
* The connection to associate with this connection record.
*
* @param sharingProfile
* The sharing profile that was used to share access to the given
* connection, or null if no sharing profile was used.
*/
private ActiveConnectionRecord(RemoteAuthenticatedUser user,
ModeledConnectionGroup balancingGroup,
ModeledConnection connection,
ModeledSharingProfile sharingProfile) {
this.user = user;
this.balancingGroup = balancingGroup;
this.connection = connection;
this.sharingProfile = sharingProfile;
}
/** /**
* Creates a new connection record associated with the given user, * Creates a new connection record associated with the given user,
* connection, and balancing connection group. The given balancing * connection, and balancing connection group. The given balancing
@@ -90,12 +141,10 @@ public class ActiveConnectionRecord implements ConnectionRecord {
* @param connection * @param connection
* The connection to associate with this connection record. * The connection to associate with this connection record.
*/ */
public ActiveConnectionRecord(AuthenticatedUser user, public ActiveConnectionRecord(RemoteAuthenticatedUser user,
ModeledConnectionGroup balancingGroup, ModeledConnectionGroup balancingGroup,
ModeledConnection connection) { ModeledConnection connection) {
this.user = user; this(user, balancingGroup, connection, null);
this.balancingGroup = balancingGroup;
this.connection = connection;
} }
/** /**
@@ -110,11 +159,38 @@ public class ActiveConnectionRecord implements ConnectionRecord {
* @param connection * @param connection
* The connection to associate with this connection record. * The connection to associate with this connection record.
*/ */
public ActiveConnectionRecord(AuthenticatedUser user, public ActiveConnectionRecord(RemoteAuthenticatedUser user,
ModeledConnection connection) { ModeledConnection connection) {
this(user, null, connection); this(user, null, connection);
} }
/**
* Creates a new connection record associated with the given user, active
* connection, and sharing profile. The given sharing profile MUST be the
* sharing profile that was used to share access to the given connection.
* The start date of this connection record will be the time of its
* creation.
*
* @param user
* The user that connected to the connection associated with this
* connection record.
*
* @param activeConnection
* The active connection which is being shared to the given user via
* the given sharing profile.
*
* @param sharingProfile
* The sharing profile that was used to share access to the given
* connection. As a record created in this way always refers to a
* shared connection, this value may NOT be null.
*/
public ActiveConnectionRecord(RemoteAuthenticatedUser user,
TrackedActiveConnection activeConnection,
ModeledSharingProfile sharingProfile) {
this(user, null, activeConnection.getConnection(), sharingProfile);
this.connectionID = activeConnection.getConnectionID();
}
/** /**
* Returns the user that connected to the connection associated with this * Returns the user that connected to the connection associated with this
* connection record. * connection record.
@@ -123,7 +199,7 @@ public class ActiveConnectionRecord implements ConnectionRecord {
* The user that connected to the connection associated with this * The user that connected to the connection associated with this
* connection record. * connection record.
*/ */
public AuthenticatedUser getUser() { public RemoteAuthenticatedUser getUser() {
return user; return user;
} }
@@ -149,6 +225,20 @@ public class ActiveConnectionRecord implements ConnectionRecord {
return connection; return connection;
} }
/**
* Returns the sharing profile that was used to access the connection
* associated with this connection record. If the connection was accessed
* directly (without involving a sharing profile), this will be null.
*
* @return
* The sharing profile that was used to access the connection
* associated with this connection record, or null if the connection
* was accessed directly.
*/
public ModeledSharingProfile getSharingProfile() {
return sharingProfile;
}
/** /**
* Returns whether the connection associated with this connection record * Returns whether the connection associated with this connection record
* was chosen from a balancing group. * was chosen from a balancing group.
@@ -161,6 +251,21 @@ public class ActiveConnectionRecord implements ConnectionRecord {
return balancingGroup != null; return balancingGroup != null;
} }
/**
* Returns whether this connection record is associated with a connection
* being used directly, in the absence of a sharing profile. If a connection
* is shared, this will continue to return false for the connection being
* shared, but will return true for the connections which join that
* connection.
*
* @return
* true if the connection associated with this connection record is
* being used directly, false otherwise.
*/
public boolean isPrimaryConnection() {
return sharingProfile == null;
}
@Override @Override
public String getConnectionIdentifier() { public String getConnectionIdentifier() {
return connection.getIdentifier(); return connection.getIdentifier();
@@ -173,12 +278,26 @@ public class ActiveConnectionRecord implements ConnectionRecord {
@Override @Override
public String getSharingProfileIdentifier() { public String getSharingProfileIdentifier() {
// Return sharing profile identifier if known
if (sharingProfile != null)
return sharingProfile.getIdentifier();
// No associated sharing profile
return null; return null;
} }
@Override @Override
public String getSharingProfileName() { public String getSharingProfileName() {
// Return sharing profile name if known
if (sharingProfile != null)
return sharingProfile.getName();
// No associated sharing profile
return null; return null;
} }
@Override @Override
@@ -201,7 +320,7 @@ public class ActiveConnectionRecord implements ConnectionRecord {
@Override @Override
public String getUsername() { public String getUsername() {
return user.getUser().getIdentifier(); return user.getIdentifier();
} }
@Override @Override
@@ -229,13 +348,13 @@ public class ActiveConnectionRecord implements ConnectionRecord {
* given socket. * given socket.
* *
* @param socket * @param socket
* The GuacamoleSocket to use to create the tunnel associated with this * The ConfiguredGuacamoleSocket to use to create the tunnel associated
* connection record. * with this connection record.
* *
* @return * @return
* The newly-created tunnel associated with this connection record. * The newly-created tunnel associated with this connection record.
*/ */
public GuacamoleTunnel assignGuacamoleTunnel(final GuacamoleSocket socket) { public GuacamoleTunnel assignGuacamoleTunnel(final ConfiguredGuacamoleSocket socket) {
// Create tunnel with given socket // Create tunnel with given socket
this.tunnel = new AbstractGuacamoleTunnel() { this.tunnel = new AbstractGuacamoleTunnel() {
@@ -252,6 +371,10 @@ public class ActiveConnectionRecord implements ConnectionRecord {
}; };
// Store connection ID of the primary connection only
if (isPrimaryConnection())
this.connectionID = socket.getConnectionID();
// Return newly-created tunnel // Return newly-created tunnel
return this.tunnel; return this.tunnel;
@@ -268,5 +391,20 @@ public class ActiveConnectionRecord implements ConnectionRecord {
public UUID getUUID() { public UUID getUUID() {
return uuid; return uuid;
} }
/**
* Returns the connection ID of the in-progress connection as determined by
* guacd, not to be confused with the connection identifier determined by
* the database. This is the ID that must be supplied to guacd if joining
* this connection. If the in-progress connection is joining another
* connection, this will be the ID of the connection being joined, NOT the
* ID of the connection directly represented by this record.
*
* @return
* The ID of the in-progress connection, as determined by guacd.
*/
public String getConnectionID() {
return connectionID;
}
} }

View File

@@ -24,6 +24,9 @@ import org.apache.guacamole.auth.jdbc.user.AuthenticatedUser;
import org.apache.guacamole.auth.jdbc.connection.ModeledConnection; import org.apache.guacamole.auth.jdbc.connection.ModeledConnection;
import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.auth.jdbc.activeconnection.TrackedActiveConnection;
import org.apache.guacamole.auth.jdbc.sharing.SharedConnectionUser;
import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
import org.apache.guacamole.net.GuacamoleTunnel; import org.apache.guacamole.net.GuacamoleTunnel;
import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.Connection;
import org.apache.guacamole.net.auth.ConnectionGroup; import org.apache.guacamole.net.auth.ConnectionGroup;
@@ -145,4 +148,39 @@ public interface GuacamoleTunnelService {
*/ */
public Collection<ActiveConnectionRecord> getActiveConnections(ConnectionGroup connectionGroup); public Collection<ActiveConnectionRecord> getActiveConnections(ConnectionGroup connectionGroup);
/**
* Creates a socket for the given user which joins the given active
* connection. The given client information will be passed to guacd when
* the connection is established. This function will apply any concurrent
* usage rules in effect, but will NOT test object- or system-level
* permissions.
*
* @param user
* The user for whom the connection is being established.
*
* @param activeConnection
* The active connection the user is joining.
*
* @param sharingProfile
* The sharing profile whose associated parameters dictate the level
* of access granted to the user joining the connection.
*
* @param info
* Information describing the Guacamole client connecting to the given
* connection.
*
* @return
* A new GuacamoleTunnel which is configured and connected to the given
* active connection.
*
* @throws GuacamoleException
* If the connection cannot be established due to concurrent usage
* rules.
*/
GuacamoleTunnel getGuacamoleTunnel(SharedConnectionUser user,
TrackedActiveConnection activeConnection,
ModeledSharingProfile sharingProfile,
GuacamoleClientInformation info)
throws GuacamoleException;
} }

View File

@@ -27,12 +27,12 @@ import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.apache.guacamole.GuacamoleClientTooManyException; import org.apache.guacamole.GuacamoleClientTooManyException;
import org.apache.guacamole.auth.jdbc.user.AuthenticatedUser;
import org.apache.guacamole.auth.jdbc.connection.ModeledConnection; import org.apache.guacamole.auth.jdbc.connection.ModeledConnection;
import org.apache.guacamole.GuacamoleException; import org.apache.guacamole.GuacamoleException;
import org.apache.guacamole.GuacamoleResourceConflictException; import org.apache.guacamole.GuacamoleResourceConflictException;
import org.apache.guacamole.auth.jdbc.JDBCEnvironment; import org.apache.guacamole.auth.jdbc.JDBCEnvironment;
import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup;
import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
/** /**
@@ -166,7 +166,7 @@ public class RestrictedGuacamoleTunnelService
} }
@Override @Override
protected ModeledConnection acquire(AuthenticatedUser user, protected ModeledConnection acquire(RemoteAuthenticatedUser user,
List<ModeledConnection> connections) throws GuacamoleException { List<ModeledConnection> connections) throws GuacamoleException {
// Do not acquire connection unless within overall limits // Do not acquire connection unless within overall limits
@@ -174,7 +174,7 @@ public class RestrictedGuacamoleTunnelService
throw new GuacamoleResourceConflictException("Cannot connect. Overall maximum connections reached."); throw new GuacamoleResourceConflictException("Cannot connect. Overall maximum connections reached.");
// Get username // Get username
String username = user.getUser().getIdentifier(); String username = user.getIdentifier();
// Sort connections in ascending order of usage // Sort connections in ascending order of usage
ModeledConnection[] sortedConnections = connections.toArray(new ModeledConnection[connections.size()]); ModeledConnection[] sortedConnections = connections.toArray(new ModeledConnection[connections.size()]);
@@ -230,18 +230,18 @@ public class RestrictedGuacamoleTunnelService
} }
@Override @Override
protected void release(AuthenticatedUser user, ModeledConnection connection) { protected void release(RemoteAuthenticatedUser user, ModeledConnection connection) {
activeSeats.remove(new Seat(user.getUser().getIdentifier(), connection.getIdentifier())); activeSeats.remove(new Seat(user.getIdentifier(), connection.getIdentifier()));
activeConnections.remove(connection.getIdentifier()); activeConnections.remove(connection.getIdentifier());
totalActiveConnections.decrementAndGet(); totalActiveConnections.decrementAndGet();
} }
@Override @Override
protected void acquire(AuthenticatedUser user, protected void acquire(RemoteAuthenticatedUser user,
ModeledConnectionGroup connectionGroup) throws GuacamoleException { ModeledConnectionGroup connectionGroup) throws GuacamoleException {
// Get username // Get username
String username = user.getUser().getIdentifier(); String username = user.getIdentifier();
// Attempt to aquire connection group according to per-user limits // Attempt to aquire connection group according to per-user limits
Seat seat = new Seat(username, connectionGroup.getIdentifier()); Seat seat = new Seat(username, connectionGroup.getIdentifier());
@@ -267,9 +267,9 @@ public class RestrictedGuacamoleTunnelService
} }
@Override @Override
protected void release(AuthenticatedUser user, protected void release(RemoteAuthenticatedUser user,
ModeledConnectionGroup connectionGroup) { ModeledConnectionGroup connectionGroup) {
activeGroupSeats.remove(new Seat(user.getUser().getIdentifier(), connectionGroup.getIdentifier())); activeGroupSeats.remove(new Seat(user.getIdentifier(), connectionGroup.getIdentifier()));
activeGroups.remove(connectionGroup.getIdentifier()); activeGroups.remove(connectionGroup.getIdentifier());
} }