GUACAMOLE-360: Add support for joining active connections to the database auth.
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionService.java
index 1d3344d..c21e9c3 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/activeconnection/ActiveConnectionService.java
@@ -91,15 +91,16 @@
         Collection<TrackedActiveConnection> activeConnections = new ArrayList<TrackedActiveConnection>(identifiers.size());
         for (ActiveConnectionRecord record : records) {
 
-            // Sensitive information should be included if the connection was
-            // started by the current user OR the user is an admin
-            boolean includeSensitiveInformation =
+            // The current user should have access to sensitive information and
+            // be able to connect to (join) the active connection if they are
+            // the user that started the connection OR the user is an admin
+            boolean hasPrivilegedAccess =
                     isAdmin || username.equals(record.getUsername());
 
             // Add connection if within requested identifiers
             if (identifierSet.contains(record.getUUID().toString())) {
                 TrackedActiveConnection activeConnection = trackedActiveConnectionProvider.get();
-                activeConnection.init(user, record, includeSensitiveInformation);
+                activeConnection.init(user, record, hasPrivilegedAccess, hasPrivilegedAccess);
                 activeConnections.add(activeConnection);
             }
 
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 5424550..cdbcc07 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
@@ -21,15 +21,20 @@
 
 import com.google.inject.Inject;
 import java.util.Date;
+import java.util.Map;
 import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleSecurityException;
 import org.apache.guacamole.auth.jdbc.base.RestrictedObject;
 import org.apache.guacamole.auth.jdbc.connection.ModeledConnection;
 import org.apache.guacamole.auth.jdbc.sharing.ConnectionSharingService;
+import org.apache.guacamole.auth.jdbc.sharing.connection.SharedConnectionDefinition;
 import org.apache.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord;
+import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
 import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
 import org.apache.guacamole.net.GuacamoleTunnel;
 import org.apache.guacamole.net.auth.ActiveConnection;
 import org.apache.guacamole.net.auth.credentials.UserCredentials;
+import org.apache.guacamole.protocol.GuacamoleClientInformation;
 
 /**
  * An implementation of the ActiveConnection object which has an associated
@@ -44,6 +49,12 @@
     private ConnectionSharingService sharingService;
 
     /**
+     * Service for creating and tracking tunnels.
+     */
+    @Inject
+    private GuacamoleTunnelService tunnelService;
+
+    /**
      * The identifier of this active connection.
      */
     private String identifier;
@@ -85,6 +96,11 @@
     private GuacamoleTunnel tunnel;
 
     /**
+     * Whether connections to this TrackedActiveConnection are allowed.
+     */
+    private boolean connectable;
+
+    /**
      * Initializes this TrackedActiveConnection, copying the data associated
      * with the given active connection record. At a minimum, the identifier
      * of this active connection will be set, the start date, and the
@@ -102,13 +118,19 @@
      *     Whether sensitive data should be copied from the connection record
      *     as well. This includes the remote host, associated tunnel, and
      *     username.
+     *
+     * @param connectable
+     *     Whether the user that retrieved this object should be allowed to
+     *     join the active connection.
      */
     public void init(ModeledAuthenticatedUser currentUser,
             ActiveConnectionRecord activeConnectionRecord,
-            boolean includeSensitiveInformation) {
+            boolean includeSensitiveInformation,
+            boolean connectable) {
 
         super.init(currentUser);
         this.connectionRecord = activeConnectionRecord;
+        this.connectable      = connectable;
         
         // Copy all non-sensitive data from given record
         this.connection               = activeConnectionRecord.getConnection();
@@ -169,11 +191,32 @@
         this.sharingProfileIdentifier = sharingProfileIdentifier;
     }
 
+    /**
+     * Shares this active connection with the user that retrieved it, returning
+     * a SharedConnectionDefinition that can be used to establish a tunnel to
+     * the shared connection. If provided, access within the shared connection
+     * will be restricted by the sharing profile with the given identifier.
+     *
+     * @param identifier
+     *     The identifier of the sharing profile that defines the restrictions
+     *     applying to the shared connection, or null if no such restrictions
+     *     apply.
+     *
+     * @return
+     *     A new SharedConnectionDefinition which can be used to establish a
+     *     tunnel to the shared connection.
+     *
+     * @throws GuacamoleException
+     *     If permission to share this active connection is denied.
+     */
+    private SharedConnectionDefinition share(String identifier) throws GuacamoleException {
+        return sharingService.shareConnection(getCurrentUser(), connectionRecord, identifier);
+    }
+
     @Override
     public UserCredentials getSharingCredentials(String identifier)
             throws GuacamoleException {
-        return sharingService.generateTemporaryCredentials(getCurrentUser(),
-                connectionRecord, identifier);
+        return sharingService.getSharingCredentials(share(identifier));
     }
 
     @Override
@@ -216,4 +259,26 @@
         this.tunnel = tunnel;
     }
 
+    @Override
+    public boolean isConnectable() {
+        return connectable;
+    }
+
+    @Override
+    public GuacamoleTunnel connect(GuacamoleClientInformation info,
+            Map<String, String> tokens) throws GuacamoleException {
+
+        // Establish connection only if connecting is allowed
+        if (isConnectable())
+            return tunnelService.getGuacamoleTunnel(getCurrentUser(), share(null), info, tokens);
+
+        throw new GuacamoleSecurityException("Permission denied.");
+
+    }
+
+    @Override
+    public int getActiveConnections() {
+        return 0;
+    }
+
 }
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/ConnectionSharingService.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/ConnectionSharingService.java
index efdecf0..fe038b6 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/ConnectionSharingService.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/ConnectionSharingService.java
@@ -21,6 +21,7 @@
 
 import com.google.inject.Inject;
 import java.util.Collections;
+import java.util.Map;
 import javax.servlet.http.HttpServletRequest;
 import org.apache.guacamole.auth.jdbc.user.ModeledAuthenticatedUser;
 import org.apache.guacamole.GuacamoleException;
@@ -30,11 +31,14 @@
 import org.apache.guacamole.auth.jdbc.sharingprofile.ModeledSharingProfile;
 import org.apache.guacamole.auth.jdbc.sharingprofile.SharingProfileService;
 import org.apache.guacamole.auth.jdbc.tunnel.ActiveConnectionRecord;
+import org.apache.guacamole.auth.jdbc.tunnel.GuacamoleTunnelService;
+import org.apache.guacamole.auth.jdbc.user.RemoteAuthenticatedUser;
 import org.apache.guacamole.form.Field;
 import org.apache.guacamole.net.auth.AuthenticationProvider;
 import org.apache.guacamole.net.auth.Credentials;
 import org.apache.guacamole.net.auth.credentials.CredentialsInfo;
 import org.apache.guacamole.net.auth.credentials.UserCredentials;
+import org.apache.guacamole.protocol.GuacamoleClientInformation;
 
 /**
  * Service which provides convenience methods for sharing active connections.
@@ -75,10 +79,16 @@
             ));
 
     /**
-     * Generates a set of temporary credentials which can be used to connect to
-     * the given connection using the given sharing profile. If the user does
-     * not have permission to share the connection via the given sharing
-     * profile, permission will be denied.
+     * Creates a new SharedConnectionDefinition which can be used to connect to
+     * the given connection, optionally restricting access to the shared
+     * connection using the given sharing profile. If the user does not have
+     * permission to share the connection via the given sharing profile,
+     * permission will be denied.
+     *
+     * @see GuacamoleTunnelService#getGuacamoleTunnel(RemoteAuthenticatedUser,
+     *          SharedConnectionDefinition, GuacamoleClientInformation, Map)
+     *
+     * @see #getSharingCredentials(SharedConnectionDefinition)
      *
      * @param user
      *     The user sharing the connection.
@@ -88,42 +98,67 @@
      *
      * @param sharingProfileIdentifier
      *     The identifier of the sharing profile dictating the semantics or
-     *     restrictions applying to the shared session.
+     *     restrictions applying to the shared session, or null if no such
+     *     restrictions should apply.
      *
      * @return
-     *     A newly-generated set of temporary credentials which can be used to
-     *     connect to the given connection.
+     *     A new SharedConnectionDefinition which can be used to connect to the
+     *     given connection.
      *
      * @throws GuacamoleException
      *     If permission to share the given connection is denied.
      */
-    public UserCredentials generateTemporaryCredentials(ModeledAuthenticatedUser user,
+    public SharedConnectionDefinition shareConnection(ModeledAuthenticatedUser user,
             ActiveConnectionRecord activeConnection,
             String sharingProfileIdentifier) throws GuacamoleException {
 
-        // Pull sharing profile (verifying access)
-        ModeledSharingProfile sharingProfile =
-                sharingProfileService.retrieveObject(user,
-                        sharingProfileIdentifier);
+        // If a sharing profile is provided, verify that permission to use that
+        // profile to share the given connection is actually granted
+        ModeledSharingProfile sharingProfile = null;
+        if (sharingProfileIdentifier != null) {
 
-        // Verify that this profile is indeed a sharing profile for the
-        // requested connection
-        String connectionIdentifier = activeConnection.getConnectionIdentifier();
-        if (sharingProfile == null || !sharingProfile.getPrimaryConnectionIdentifier().equals(connectionIdentifier))
-            throw new GuacamoleSecurityException("Permission denied.");
+            // Pull sharing profile (verifying access)
+            sharingProfile = sharingProfileService.retrieveObject(user, sharingProfileIdentifier);
+
+            // Verify that this profile is indeed a sharing profile for the
+            // requested connection
+            String connectionIdentifier = activeConnection.getConnectionIdentifier();
+            if (sharingProfile == null || !sharingProfile.getPrimaryConnectionIdentifier().equals(connectionIdentifier))
+                throw new GuacamoleSecurityException("Permission denied.");
+
+        }
 
         // Generate a share key for the requested connection
         String key = keyGenerator.getShareKey();
-        connectionMap.add(new SharedConnectionDefinition(activeConnection,
-                sharingProfile, key));
+        SharedConnectionDefinition definition = new SharedConnectionDefinition(activeConnection, sharingProfile, key);
+        connectionMap.add(definition);
 
         // Ensure the share key is properly invalidated when the original
         // connection is closed
         activeConnection.registerShareKey(key);
 
+        return definition;
+
+    }
+
+    /**
+     * Generates a set of temporary credentials which can be used to connect to
+     * the given connection shared by the SharedConnectionDefinition.
+     *
+     * @param definition
+     *     The SharedConnectionDefinition which defines the connection being
+     *     shared and any applicable restrictions.
+     *
+     * @return
+     *     A newly-generated set of temporary credentials which can be used to
+     *     connect to the connection shared by the given
+     *     SharedConnectionDefinition.
+     */
+    public UserCredentials getSharingCredentials(SharedConnectionDefinition definition) {
+
         // Return credentials defining a single expected parameter
         return new UserCredentials(SHARE_KEY,
-                Collections.singletonMap(SHARE_KEY_NAME, key));
+                Collections.singletonMap(SHARE_KEY_NAME, definition.getShareKey()));
 
     }
 
diff --git a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnectionDefinition.java b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnectionDefinition.java
index cb48013..4e7c3d5 100644
--- a/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnectionDefinition.java
+++ b/extensions/guacamole-auth-jdbc/modules/guacamole-auth-jdbc-base/src/main/java/org/apache/guacamole/auth/jdbc/sharing/connection/SharedConnectionDefinition.java
@@ -29,9 +29,11 @@
 
 /**
  * Defines the semantics/restrictions of a shared connection by associating an
- * active connection with a sharing profile. The sharing profile defines the
- * access provided to users of the shared active connection through its
- * connection parameters.
+ * active connection with an optional sharing profile. The sharing profile, if
+ * present, defines the access provided to users of the shared active
+ * connection through its connection parameters. If no sharing profile is
+ * present, the shared connection has the same level of access as the original
+ * connection.
  */
 public class SharedConnectionDefinition {
 
@@ -88,7 +90,8 @@
      *
      * @param sharingProfile
      *     A sharing profile whose associated parameters dictate the level of
-     *     access provided to the shared connection.
+     *     access provided to the shared connection, or null if the connection
+     *     should be given full access.
      *
      * @param shareKey
      *     The unique key with which a user may access the shared connection.
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 20ac299..abecf32 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
@@ -202,8 +202,10 @@
 
     /**
      * Returns a guacamole configuration containing the protocol and parameters
-     * from the given connection. If tokens are used in the connection
-     * parameter values, credentials from the given user will be substituted
+     * from the given connection. If the ID of an active connection is
+     * provided, that connection will be joined instead of starting a new
+     * primary connection. If tokens are used in the connection parameter
+     * values, credentials from the given user will be substituted
      * appropriately.
      *
      * @param user
@@ -213,19 +215,29 @@
      *     The connection whose protocol and parameters should be added to the
      *     returned configuration.
      *
+     * @param connectionID
+     *     The ID of the active connection to be joined, as returned by guacd,
+     *     or null if a new primary connection should be established.
+     *
      * @return
      *     A GuacamoleConfiguration containing the protocol and parameters from
      *     the given connection.
      */
     private GuacamoleConfiguration getGuacamoleConfiguration(RemoteAuthenticatedUser user,
-            ModeledConnection connection) {
+            ModeledConnection connection, String connectionID) {
 
         // Generate configuration from available data
         GuacamoleConfiguration config = new GuacamoleConfiguration();
 
-        // Set protocol from connection
-        ConnectionModel model = connection.getModel();
-        config.setProtocol(model.getProtocol());
+        // Join existing active connection, if any
+        if (connectionID != null)
+            config.setConnectionID(connectionID);
+
+        // Set protocol from connection if not joining an active connection
+        else {
+            ConnectionModel model = connection.getModel();
+            config.setProtocol(model.getProtocol());
+        }
 
         // Set parameters from associated data
         Collection<ConnectionParameterModel> parameters = connectionParameterMapper.select(connection.getIdentifier());
@@ -470,16 +482,17 @@
             // Retrieve connection information associated with given connection record
             ModeledConnection connection = activeConnection.getConnection();
 
-            // Pull configuration directly from the connection if we are not
-            // joining an active connection
+            // Pull configuration directly from the connection, additionally
+            // joining the existing active connection (without sharing profile
+            // restrictions) if such a connection exists
             if (activeConnection.isPrimaryConnection()) {
                 activeConnections.put(connection.getIdentifier(), activeConnection);
                 activeConnectionGroups.put(connection.getParentIdentifier(), activeConnection);
-                config = getGuacamoleConfiguration(activeConnection.getUser(), connection);
+                config = getGuacamoleConfiguration(activeConnection.getUser(), connection, activeConnection.getConnectionID());
             }
 
-            // If we ARE joining an active connection, generate a configuration
-            // which does so
+            // If we ARE joining an active connection under the restrictions of
+            // a sharing profile, generate a configuration which does so
             else {
 
                 // Verify that the connection ID is known
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 3a4b148..a150212 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
@@ -32,7 +32,6 @@
 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;
 
 
 /**
@@ -202,8 +201,8 @@
      *
      * @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.
+     *     connection, or null if no sharing profile should be used (access to
+     *     the connection is unrestricted).
      */
     public void init(RemoteAuthenticatedUser user,
             ActiveConnectionRecord activeConnection,