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,