GUAC-1126: Allow non-admins to see active connections if they have READ permission to those connections.

This commit is contained in:
Michael Jumper
2015-04-09 14:15:55 -07:00
parent 311d56bae5
commit f8714b735f
5 changed files with 63 additions and 62 deletions

View File

@@ -88,20 +88,22 @@ public class ActiveConnectionPermissionService
if (canReadPermissions(user, targetUser)) { if (canReadPermissions(user, targetUser)) {
// Only administrators may access active connections // Only administrators may access active connections
if (!targetUser.isAdministrator()) boolean isAdmin = targetUser.isAdministrator();
return Collections.EMPTY_SET;
// Get all active connections // Get all active connections
Collection<ActiveConnectionRecord> records = tunnelService.getActiveConnections(user); Collection<ActiveConnectionRecord> records = tunnelService.getActiveConnections(user);
// We have READ and DELETE on all active connections // We have READ, and possibly DELETE, on all active connections
Set<ObjectPermission> permissions = new HashSet<ObjectPermission>(); Set<ObjectPermission> permissions = new HashSet<ObjectPermission>();
for (ActiveConnectionRecord record : records) { for (ActiveConnectionRecord record : records) {
// Add implicit READ and DELETE // Add implicit READ
String identifier = record.getUUID().toString(); String identifier = record.getUUID().toString();
permissions.add(new ObjectPermission(ObjectPermission.Type.READ, identifier)); permissions.add(new ObjectPermission(ObjectPermission.Type.READ, identifier));
permissions.add(new ObjectPermission(ObjectPermission.Type.DELETE, identifier));
// If we're and admin, then we also have DELETE
if (isAdmin)
permissions.add(new ObjectPermission(ObjectPermission.Type.DELETE, identifier));
} }

View File

@@ -26,6 +26,7 @@ import com.google.inject.Inject;
import com.google.inject.Provider; import com.google.inject.Provider;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser;
@@ -62,35 +63,42 @@ public class ActiveConnectionService
public TrackedActiveConnection retrieveObject(AuthenticatedUser user, public TrackedActiveConnection retrieveObject(AuthenticatedUser user,
String identifier) throws GuacamoleException { String identifier) throws GuacamoleException {
// Only administrators may retrieve active connections // Pull objects having given identifier
if (!user.getUser().isAdministrator()) Collection<TrackedActiveConnection> objects = retrieveObjects(user, Collections.singleton(identifier));
throw new GuacamoleSecurityException("Permission denied.");
// Retrieve record associated with requested connection // If no such object, return null
ActiveConnectionRecord record = tunnelService.getActiveConnection(user, identifier); if (objects.isEmpty())
if (record == null)
return null; return null;
// Return tracked active connection using retrieved record // The object collection will have exactly one element unless the
TrackedActiveConnection activeConnection = trackedActiveConnectionProvider.get(); // database has seriously lost integrity
activeConnection.init(user, record); assert(objects.size() == 1);
return activeConnection;
// Return first and only object
return objects.iterator().next();
} }
@Override @Override
public Collection<TrackedActiveConnection> retrieveObjects(AuthenticatedUser user, public Collection<TrackedActiveConnection> retrieveObjects(AuthenticatedUser user,
Collection<String> identifiers) throws GuacamoleException { Collection<String> identifiers) throws GuacamoleException {
// Build list of all active connections with given identifiers Set<String> identifierSet = new HashSet<String>(identifiers);
Collection<TrackedActiveConnection> activeConnections = new ArrayList<TrackedActiveConnection>(identifiers.size());
for (String identifier : identifiers) {
// Add connection to list if it exists // Retrieve all visible connections (permissions enforced by tunnel service)
TrackedActiveConnection activeConnection = retrieveObject(user, identifier); Collection<ActiveConnectionRecord> records = tunnelService.getActiveConnections(user);
if (activeConnection != null)
// Restrict to subset of records which match given identifiers
Collection<TrackedActiveConnection> activeConnections = new ArrayList<TrackedActiveConnection>(identifiers.size());
for (ActiveConnectionRecord record : records) {
// Add connection if within requested identifiers
if (identifierSet.contains(record.getUUID().toString())) {
TrackedActiveConnection activeConnection = trackedActiveConnectionProvider.get();
activeConnection.init(user, record);
activeConnections.add(activeConnection); activeConnections.add(activeConnection);
}
} }
return activeConnections; return activeConnections;
@@ -101,6 +109,10 @@ public class ActiveConnectionService
public void deleteObject(AuthenticatedUser user, String identifier) public void deleteObject(AuthenticatedUser user, String identifier)
throws GuacamoleException { throws GuacamoleException {
// Only administrators may delete active connections
if (!user.getUser().isAdministrator())
throw new GuacamoleSecurityException("Permission denied.");
// Close connection, if it exists (and we have permission) // Close connection, if it exists (and we have permission)
ActiveConnection activeConnection = retrieveObject(user, identifier); ActiveConnection activeConnection = retrieveObject(user, identifier);
if (activeConnection != null) { if (activeConnection != null) {

View File

@@ -28,8 +28,10 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser; import org.glyptodon.guacamole.auth.jdbc.user.AuthenticatedUser;
@@ -448,24 +450,33 @@ public abstract class AbstractGuacamoleTunnelService implements GuacamoleTunnelS
public Collection<ActiveConnectionRecord> getActiveConnections(AuthenticatedUser user) public Collection<ActiveConnectionRecord> getActiveConnections(AuthenticatedUser user)
throws GuacamoleException { throws GuacamoleException {
// Only administrators may see all active connections // Simply return empty list if there are no active tunnels
if (!user.getUser().isAdministrator()) Collection<ActiveConnectionRecord> records = activeTunnels.values();
if (records.isEmpty())
return Collections.EMPTY_LIST; return Collections.EMPTY_LIST;
return Collections.unmodifiableCollection(activeTunnels.values()); // Build set of all connection identifiers associated with active tunnels
Set<String> identifiers = new HashSet<String>(records.size());
for (ActiveConnectionRecord record : records)
identifiers.add(record.getConnection().getIdentifier());
} // Produce collection of readable connection identifiers
Collection<ConnectionModel> connections = connectionMapper.selectReadable(user.getUser().getModel(), identifiers);
@Override // Ensure set contains only identifiers of readable connections
public ActiveConnectionRecord getActiveConnection(AuthenticatedUser user, identifiers.clear();
String tunnelUUID) throws GuacamoleException { for (ConnectionModel connection : connections)
identifiers.add(connection.getIdentifier());
// Only administrators may see all active connections // Produce readable subset of records
if (!user.getUser().isAdministrator()) Collection<ActiveConnectionRecord> visibleRecords = new ArrayList<ActiveConnectionRecord>(records.size());
return null; for (ActiveConnectionRecord record : records) {
if (identifiers.contains(record.getConnection().getIdentifier()))
visibleRecords.add(record);
}
return visibleRecords;
return activeTunnels.get(tunnelUUID);
} }
@Override @Override

View File

@@ -59,30 +59,6 @@ public interface GuacamoleTunnelService {
public Collection<ActiveConnectionRecord> getActiveConnections(AuthenticatedUser user) public Collection<ActiveConnectionRecord> getActiveConnections(AuthenticatedUser user)
throws GuacamoleException; throws GuacamoleException;
/**
* Returns the connection records representing the connection associated
* with the tunnel having the given UUID, if that connection is visible to
* the given user.
*
* @param user
* The user retrieving the active connection.
*
* @param tunnelUUID
* The UUID of the tunnel associated with the active connection being
* retrieved.
*
* @return
* The active connection associated with the tunnel having the given
* UUID, or null if no such connection exists.
*
* @throws GuacamoleException
* If an error occurs while retrieving all active connections, or if
* permission is denied.
*/
public ActiveConnectionRecord getActiveConnection(AuthenticatedUser user,
String tunnelUUID)
throws GuacamoleException;
/** /**
* Creates a socket for the given user which connects to the given * Creates a socket for the given user which connects to the given
* connection. The given client information will be passed to guacd when * connection. The given client information will be passed to guacd when

View File

@@ -106,14 +106,14 @@ public class ActiveConnectionRESTService {
if (permissions != null && permissions.isEmpty()) if (permissions != null && permissions.isEmpty())
permissions = null; permissions = null;
// An admin user has access to any user // An admin user has access to any connection
SystemPermissionSet systemPermissions = self.getSystemPermissions(); SystemPermissionSet systemPermissions = self.getSystemPermissions();
boolean isAdmin = systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER); boolean isAdmin = systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER);
// Get the directory // Get the directory
Directory<ActiveConnection> activeConnectionDirectory = userContext.getActiveConnectionDirectory(); Directory<ActiveConnection> activeConnectionDirectory = userContext.getActiveConnectionDirectory();
// Filter users, if requested // Filter connections, if requested
Collection<String> activeConnectionIdentifiers = activeConnectionDirectory.getIdentifiers(); Collection<String> activeConnectionIdentifiers = activeConnectionDirectory.getIdentifiers();
if (!isAdmin && permissions != null) { if (!isAdmin && permissions != null) {
ObjectPermissionSet activeConnectionPermissions = self.getActiveConnectionPermissions(); ObjectPermissionSet activeConnectionPermissions = self.getActiveConnectionPermissions();