You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by jm...@apache.org on 2016/07/22 02:50:59 UTC

[5/8] incubator-guacamole-client git commit: GUACAMOLE-5: Implement joining of shared connections via temporary credentials.

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

Project: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/commit/19f80dac
Tree: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/tree/19f80dac
Diff: http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/diff/19f80dac

Branch: refs/heads/master
Commit: 19f80dac9533faa48be9f95c162d158a87965935
Parents: bfa5c38
Author: Michael Jumper <mj...@apache.org>
Authored: Thu Jul 21 14:20:28 2016 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Thu Jul 21 14:25:51 2016 -0700

----------------------------------------------------------------------
 .../TrackedActiveConnection.java                |  21 +++
 .../auth/jdbc/sharing/SharedConnection.java     |  13 +-
 .../tunnel/AbstractGuacamoleTunnelService.java  | 169 +++++++++++++++----
 .../jdbc/tunnel/ActiveConnectionRecord.java     | 166 ++++++++++++++++--
 .../jdbc/tunnel/GuacamoleTunnelService.java     |  38 +++++
 .../RestrictedGuacamoleTunnelService.java       |  18 +-
 6 files changed, 362 insertions(+), 63 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/19f80dac/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/TrackedActiveConnection.java
----------------------------------------------------------------------
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 9b39f2f..6c2e4d5 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
@@ -76,6 +76,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.
      */
     private GuacamoleTunnel tunnel;
@@ -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();

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/19f80dac/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/SharedConnection.java
----------------------------------------------------------------------
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 984f449..70b8944 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;
@@ -44,6 +45,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
      * connection being shared.
@@ -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

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/19f80dac/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/AbstractGuacamoleTunnelService.java
----------------------------------------------------------------------
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 82af02d..2d5e1f8 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<ModeledConnection> 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<ConnectionParameterModel> parameters = parameterMapper.select(connection.getIdentifier());
+        Collection<ConnectionParameterModel> parameters = connectionParameterMapper.select(connection.getIdentifier());
         for (ConnectionParameterModel parameter : parameters)
             config.setParameter(parameter.getName(), parameter.getValue());
 
@@ -233,6 +245,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
      * 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();
+            // Remove underlying tunnel from list of active tunnels
+            activeTunnels.remove(activeConnection.getUUID().toString());
+
+            // Get original user
+            RemoteAuthenticatedUser user = activeConnection.getUser();
 
-            // Get associated identifiers
-            String identifier = connection.getIdentifier();
-            String parentIdentifier = connection.getParentIdentifier();
+            // Release the associated connection if this is the primary connection
+            if (activeConnection.isPrimaryConnection()) {
 
-            // Release connection
-            activeTunnels.remove(activeConnection.getUUID().toString());
-            activeConnections.remove(identifier, activeConnection);
-            activeConnectionGroups.remove(parentIdentifier, activeConnection);
-            release(user, connection);
+                // 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);
+
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/19f80dac/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/ActiveConnectionRecord.java
----------------------------------------------------------------------
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 a34e339..f768f23 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
@@ -58,6 +61,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.
      */
     private final Date startDate = new Date();
@@ -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,12 +159,39 @@ 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;
     }
 
@@ -150,6 +226,20 @@ public class ActiveConnectionRecord implements ConnectionRecord {
     }
 
     /**
+     * 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;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/19f80dac/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/GuacamoleTunnelService.java
----------------------------------------------------------------------
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 554967b..6a00b2e 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<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;
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-guacamole-client/blob/19f80dac/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/tunnel/RestrictedGuacamoleTunnelService.java
----------------------------------------------------------------------
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 4163adc..ca92341 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<ModeledConnection> 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());
     }