diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java index 9b39f2fbc..6c2e4d5a8 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java @@ -75,6 +75,13 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC */ 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. */ @@ -107,6 +114,7 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC // Copy all non-sensitive data from given record this.connection = activeConnectionRecord.getConnection(); + this.connectionID = activeConnectionRecord.getConnectionID(); this.sharingProfileIdentifier = activeConnectionRecord.getSharingProfileIdentifier(); this.identifier = activeConnectionRecord.getUUID().toString(); this.startDate = activeConnectionRecord.getStartDate(); @@ -142,6 +150,19 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC 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 public String getConnectionIdentifier() { return connection.getIdentifier(); diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnection.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnection.java index 984f449f7..70b894429 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnection.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnection.java @@ -19,16 +19,17 @@ package org.apache.guacamole.auth.jdbc.sharing; +import com.google.inject.Inject; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import org.apache.guacamole.GuacamoleException; -import org.apache.guacamole.GuacamoleUnsupportedException; import org.apache.guacamole.auth.jdbc.activeconnection.TrackedActiveConnection; import org.apache.guacamole.auth.jdbc.connectiongroup.RootConnectionGroup; 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.auth.Connection; import org.apache.guacamole.net.auth.ConnectionRecord; @@ -43,6 +44,12 @@ import org.apache.guacamole.protocol.GuacamoleConfiguration; */ public class SharedConnection implements Connection { + /** + * Service for establishing tunnels to Guacamole connections. + */ + @Inject + private GuacamoleTunnelService tunnelService; + /** * Randomly-generated unique identifier, guaranteeing this shared connection * does not duplicate the identifying information of the underlying @@ -130,8 +137,8 @@ public class SharedConnection implements Connection { @Override public GuacamoleTunnel connect(GuacamoleClientInformation info) throws GuacamoleException { - // STUB - throw new GuacamoleUnsupportedException("Connecting to shared connections is not yet implemented."); + return tunnelService.getGuacamoleTunnel(user, activeConnection, + sharingProfile, info); } @Override diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java index 82af02db9..2d5e1f884 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java @@ -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.ConnectionRecordModel; 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.GuacamoleResourceNotFoundException; import org.apache.guacamole.GuacamoleSecurityException; 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.environment.Environment; import org.apache.guacamole.net.GuacamoleSocket; @@ -55,6 +56,11 @@ import org.apache.guacamole.token.StandardTokens; import org.apache.guacamole.token.TokenFilter; import org.mybatis.guice.transactional.Transactional; 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. */ @Inject - private ConnectionParameterMapper parameterMapper; + private ConnectionParameterMapper connectionParameterMapper; + + /** + * Mapper for accessing sharing profile parameters. + */ + @Inject + private SharingProfileParameterMapper sharingProfileParameterMapper; /** * Mapper for accessing connection history. @@ -141,7 +153,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS * @throws GuacamoleException * If access is denied to the given user for any reason. */ - protected abstract ModeledConnection acquire(AuthenticatedUser user, + protected abstract ModeledConnection acquire(RemoteAuthenticatedUser user, List connections) throws GuacamoleException; /** @@ -155,7 +167,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS * @param connection * The connection being released. */ - protected abstract void release(AuthenticatedUser user, + protected abstract void release(RemoteAuthenticatedUser user, ModeledConnection connection); /** @@ -172,7 +184,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS * @throws GuacamoleException * 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; /** @@ -186,7 +198,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS * @param connectionGroup * The connection group being released. */ - protected abstract void release(AuthenticatedUser user, + protected abstract void release(RemoteAuthenticatedUser user, ModeledConnectionGroup connectionGroup); /** @@ -206,7 +218,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS * A GuacamoleConfiguration containing the protocol and parameters from * the given connection. */ - private GuacamoleConfiguration getGuacamoleConfiguration(AuthenticatedUser user, + private GuacamoleConfiguration getGuacamoleConfiguration(RemoteAuthenticatedUser user, ModeledConnection connection) { // Generate configuration from available data @@ -217,7 +229,7 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS config.setProtocol(model.getProtocol()); // Set parameters from associated data - Collection parameters = parameterMapper.select(connection.getIdentifier()); + Collection parameters = connectionParameterMapper.select(connection.getIdentifier()); for (ConnectionParameterModel parameter : parameters) 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 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 * 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) { - // Get associated connection - ModeledConnection connection = record.getConnection(); - // Get associated models - AuthenticatedUser user = record.getUser(); ConnectionRecordModel recordModel = new ConnectionRecordModel(); // Copy user information and timestamps into new record - recordModel.setUsername(user.getIdentifier()); - recordModel.setConnectionIdentifier(connection.getIdentifier()); - recordModel.setConnectionName(connection.getName()); + recordModel.setUsername(record.getUsername()); + recordModel.setConnectionIdentifier(record.getConnectionIdentifier()); + recordModel.setConnectionName(record.getConnectionName()); + recordModel.setSharingProfileIdentifier(record.getSharingProfileIdentifier()); + recordModel.setSharingProfileName(record.getSharingProfileName()); recordModel.setStartDate(record.getStartDate()); recordModel.setEndDate(new Date()); @@ -329,19 +385,26 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS if (!hasRun.compareAndSet(false, true)) return; - // Get original user and connection - AuthenticatedUser user = activeConnection.getUser(); - ModeledConnection connection = activeConnection.getConnection(); - - // Get associated identifiers - String identifier = connection.getIdentifier(); - String parentIdentifier = connection.getParentIdentifier(); - - // Release connection + // Remove underlying tunnel from list of active tunnels activeTunnels.remove(activeConnection.getUUID().toString()); - activeConnections.remove(identifier, activeConnection); - activeConnectionGroups.remove(parentIdentifier, activeConnection); - release(user, connection); + + // Get original user + 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 if (activeConnection.hasBalancingGroup()) @@ -379,25 +442,44 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS * while connection configuration information is being retrieved. */ private GuacamoleTunnel assignGuacamoleTunnel(ActiveConnectionRecord activeConnection, - GuacamoleClientInformation info) - throws GuacamoleException { + GuacamoleClientInformation info) throws GuacamoleException { - ModeledConnection connection = activeConnection.getConnection(); - // Record new active connection Runnable cleanupTask = new ConnectionCleanupTask(activeConnection); activeTunnels.put(activeConnection.getUUID().toString(), activeConnection); - activeConnections.put(connection.getIdentifier(), activeConnection); - activeConnectionGroups.put(connection.getParentIdentifier(), activeConnection); 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 - GuacamoleSocket socket = new ConfiguredGuacamoleSocket( - getUnconfiguredGuacamoleSocket(cleanupTask), - getGuacamoleConfiguration(activeConnection.getUser(), connection), - info - ); + ConfiguredGuacamoleSocket socket = new ConfiguredGuacamoleSocket( + getUnconfiguredGuacamoleSocket(cleanupTask), config, info); // Assign and return new tunnel 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); + + } + } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java index a34e339c3..f768f2378 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java @@ -21,13 +21,16 @@ package org.apache.guacamole.auth.jdbc.tunnel; import java.util.Date; 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.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.GuacamoleSocket; import org.apache.guacamole.net.GuacamoleTunnel; 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 * record. */ - private final AuthenticatedUser user; + private final RemoteAuthenticatedUser user; /** * The balancing group from which the associated connection was chosen, if @@ -57,6 +60,13 @@ public class ActiveConnectionRecord implements ConnectionRecord { */ 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. */ @@ -66,13 +76,54 @@ public class ActiveConnectionRecord implements ConnectionRecord { * The UUID that will be assigned to the underlying tunnel. */ 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 * connection record. */ 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, * connection, and balancing connection group. The given balancing @@ -90,12 +141,10 @@ public class ActiveConnectionRecord implements ConnectionRecord { * @param connection * The connection to associate with this connection record. */ - public ActiveConnectionRecord(AuthenticatedUser user, + public ActiveConnectionRecord(RemoteAuthenticatedUser user, ModeledConnectionGroup balancingGroup, ModeledConnection connection) { - this.user = user; - this.balancingGroup = balancingGroup; - this.connection = connection; + this(user, balancingGroup, connection, null); } /** @@ -110,11 +159,38 @@ public class ActiveConnectionRecord implements ConnectionRecord { * @param connection * The connection to associate with this connection record. */ - public ActiveConnectionRecord(AuthenticatedUser user, + public ActiveConnectionRecord(RemoteAuthenticatedUser user, ModeledConnection 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 * connection record. @@ -123,7 +199,7 @@ public class ActiveConnectionRecord implements ConnectionRecord { * The user that connected to the connection associated with this * connection record. */ - public AuthenticatedUser getUser() { + public RemoteAuthenticatedUser getUser() { return user; } @@ -149,6 +225,20 @@ public class ActiveConnectionRecord implements ConnectionRecord { 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 * was chosen from a balancing group. @@ -161,6 +251,21 @@ public class ActiveConnectionRecord implements ConnectionRecord { 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 public String getConnectionIdentifier() { return connection.getIdentifier(); @@ -173,12 +278,26 @@ public class ActiveConnectionRecord implements ConnectionRecord { @Override public String getSharingProfileIdentifier() { + + // Return sharing profile identifier if known + if (sharingProfile != null) + return sharingProfile.getIdentifier(); + + // No associated sharing profile return null; + } @Override public String getSharingProfileName() { + + // Return sharing profile name if known + if (sharingProfile != null) + return sharingProfile.getName(); + + // No associated sharing profile return null; + } @Override @@ -201,7 +320,7 @@ public class ActiveConnectionRecord implements ConnectionRecord { @Override public String getUsername() { - return user.getUser().getIdentifier(); + return user.getIdentifier(); } @Override @@ -229,13 +348,13 @@ public class ActiveConnectionRecord implements ConnectionRecord { * given socket. * * @param socket - * The GuacamoleSocket to use to create the tunnel associated with this - * connection record. + * The ConfiguredGuacamoleSocket to use to create the tunnel associated + * with this connection record. * * @return * 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 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 this.tunnel; @@ -268,5 +391,20 @@ public class ActiveConnectionRecord implements ConnectionRecord { public UUID getUUID() { 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; + } + } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java index 554967b3a..6a00b2e2c 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java @@ -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.connectiongroup.ModeledConnectionGroup; 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.auth.Connection; import org.apache.guacamole.net.auth.ConnectionGroup; @@ -145,4 +148,39 @@ public interface GuacamoleTunnelService { */ public Collection 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; + } diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/RestrictedGuacamoleTunnelService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/RestrictedGuacamoleTunnelService.java index 4163adc69..ca9234111 100644 --- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/RestrictedGuacamoleTunnelService.java +++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/RestrictedGuacamoleTunnelService.java @@ -27,12 +27,12 @@ import java.util.Comparator; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; 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.GuacamoleException; import org.apache.guacamole.GuacamoleResourceConflictException; import org.apache.guacamole.auth.jdbc.JDBCEnvironment; import org.apache.guacamole.auth.jdbc.connectiongroup.ModeledConnectionGroup; +import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser; /** @@ -166,7 +166,7 @@ public class RestrictedGuacamoleTunnelService } @Override - protected ModeledConnection acquire(AuthenticatedUser user, + protected ModeledConnection acquire(RemoteAuthenticatedUser user, List connections) throws GuacamoleException { // Do not acquire connection unless within overall limits @@ -174,7 +174,7 @@ public class RestrictedGuacamoleTunnelService throw new GuacamoleResourceConflictException("Cannot connect. Overall maximum connections reached."); // Get username - String username = user.getUser().getIdentifier(); + String username = user.getIdentifier(); // Sort connections in ascending order of usage ModeledConnection[] sortedConnections = connections.toArray(new ModeledConnection[connections.size()]); @@ -230,18 +230,18 @@ public class RestrictedGuacamoleTunnelService } @Override - protected void release(AuthenticatedUser user, ModeledConnection connection) { - activeSeats.remove(new Seat(user.getUser().getIdentifier(), connection.getIdentifier())); + protected void release(RemoteAuthenticatedUser user, ModeledConnection connection) { + activeSeats.remove(new Seat(user.getIdentifier(), connection.getIdentifier())); activeConnections.remove(connection.getIdentifier()); totalActiveConnections.decrementAndGet(); } @Override - protected void acquire(AuthenticatedUser user, + protected void acquire(RemoteAuthenticatedUser user, ModeledConnectionGroup connectionGroup) throws GuacamoleException { // Get username - String username = user.getUser().getIdentifier(); + String username = user.getIdentifier(); // Attempt to aquire connection group according to per-user limits Seat seat = new Seat(username, connectionGroup.getIdentifier()); @@ -267,9 +267,9 @@ public class RestrictedGuacamoleTunnelService } @Override - protected void release(AuthenticatedUser user, + protected void release(RemoteAuthenticatedUser user, ModeledConnectionGroup connectionGroup) { - activeGroupSeats.remove(new Seat(user.getUser().getIdentifier(), connectionGroup.getIdentifier())); + activeGroupSeats.remove(new Seat(user.getIdentifier(), connectionGroup.getIdentifier())); activeGroups.remove(connectionGroup.getIdentifier()); }