Skip to content
This repository has been archived by the owner on Jul 17, 2023. It is now read-only.

Commit

Permalink
GUACAMOLE-360: Add support for joining active connections to the data…
Browse files Browse the repository at this point in the history
…base auth.
  • Loading branch information
mike-jumper committed Aug 11, 2019
1 parent a59e20e commit 403431b
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,16 @@ public Collection<TrackedActiveConnection> retrieveObjects(ModeledAuthenticatedU
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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -43,6 +48,12 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC
@Inject
private ConnectionSharingService sharingService;

/**
* Service for creating and tracking tunnels.
*/
@Inject
private GuacamoleTunnelService tunnelService;

/**
* The identifier of this active connection.
*/
Expand Down Expand Up @@ -84,6 +95,11 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC
*/
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
Expand All @@ -102,13 +118,19 @@ public class TrackedActiveConnection extends RestrictedObject implements ActiveC
* 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();
Expand Down Expand Up @@ -169,11 +191,32 @@ public void setSharingProfileIdentifier(String sharingProfileIdentifier) {
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
Expand Down Expand Up @@ -216,4 +259,26 @@ public void setTunnel(GuacamoleTunnel tunnel) {
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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -75,10 +79,16 @@ public class ConnectionSharingService {
));

/**
* 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.
Expand All @@ -88,42 +98,67 @@ public class ConnectionSharingService {
*
* @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) {

// 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.");
// 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()));

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -88,7 +90,8 @@ protected void cleanup(GuacamoleTunnel tunnel) {
*
* @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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,10 @@ protected abstract void release(RemoteAuthenticatedUser user,

/**
* 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
Expand All @@ -213,19 +215,29 @@ protected abstract void release(RemoteAuthenticatedUser user,
* 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());
Expand Down Expand Up @@ -470,16 +482,17 @@ private GuacamoleTunnel assignGuacamoleTunnel(ActiveConnectionRecord activeConne
// 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;


/**
Expand Down Expand Up @@ -202,8 +201,8 @@ public void init(RemoteAuthenticatedUser user,
*
* @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,
Expand Down

0 comments on commit 403431b

Please sign in to comment.