mirror of
				https://github.com/gyurix1968/guacamole-client.git
				synced 2025-10-31 00:53:21 +00:00 
			
		
		
		
	GUAC-1132: Handle active connections via permissions. Expose via dedicated object - do not rely on calling them tunnels.
This commit is contained in:
		| @@ -31,7 +31,7 @@ import org.glyptodon.guacamole.net.basic.rest.clipboard.ClipboardRESTService; | ||||
| import org.glyptodon.guacamole.net.basic.rest.connection.ConnectionRESTService; | ||||
| import org.glyptodon.guacamole.net.basic.rest.connectiongroup.ConnectionGroupRESTService; | ||||
| import org.glyptodon.guacamole.net.basic.rest.protocol.ProtocolRESTService; | ||||
| import org.glyptodon.guacamole.net.basic.rest.tunnel.TunnelRESTService; | ||||
| import org.glyptodon.guacamole.net.basic.rest.activeconnection.ActiveConnectionRESTService; | ||||
| import org.glyptodon.guacamole.net.basic.rest.user.UserRESTService; | ||||
|  | ||||
| /** | ||||
| @@ -51,7 +51,7 @@ public class RESTServletModule extends ServletModule { | ||||
|         bind(ProtocolRESTService.class); | ||||
|         bind(UserRESTService.class); | ||||
|         bind(TokenRESTService.class); | ||||
|         bind(TunnelRESTService.class); | ||||
|         bind(ActiveConnectionRESTService.class); | ||||
|          | ||||
|         // Set up the servlet and JSON mappings | ||||
|         bind(GuiceContainer.class); | ||||
|   | ||||
| @@ -20,22 +20,29 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package org.glyptodon.guacamole.net.basic.rest.tunnel; | ||||
| package org.glyptodon.guacamole.net.basic.rest.activeconnection; | ||||
| 
 | ||||
| import java.util.Date; | ||||
| import org.glyptodon.guacamole.net.auth.ConnectionRecord; | ||||
| import org.glyptodon.guacamole.net.auth.ActiveConnection; | ||||
| 
 | ||||
| /** | ||||
|  * Tunnel-related information which may be exposed through the REST endpoints. | ||||
|  * Information related to active connections which may be exposed through the | ||||
|  * REST endpoints. | ||||
|  *  | ||||
|  * @author Michael Jumper | ||||
|  */ | ||||
| public class APITunnel { | ||||
| public class APIActiveConnection { | ||||
| 
 | ||||
|     /** | ||||
|      * The identifier of the connection associated with this tunnel. | ||||
|      * The identifier of the active connection itself. | ||||
|      */ | ||||
|     private final String identifier; | ||||
| 
 | ||||
|     /** | ||||
|      * The identifier of the connection associated with this | ||||
|      * active connection. | ||||
|      */ | ||||
|     private final String connectionIdentifier; | ||||
|      | ||||
|     /** | ||||
|      * The date and time the connection began. | ||||
| @@ -53,26 +60,18 @@ public class APITunnel { | ||||
|     private final String username; | ||||
| 
 | ||||
|     /** | ||||
|      * The UUID of the tunnel. | ||||
|      */ | ||||
|     private final String uuid; | ||||
|      | ||||
|     /** | ||||
|      * Creates a new APITunnel, copying the information from the given | ||||
|      * connection record. | ||||
|      * Creates a new APIActiveConnection, copying the information from the given | ||||
|      * active connection. | ||||
|      * | ||||
|      * @param record | ||||
|      *     The record to copy data from. | ||||
|      *  | ||||
|      * @param uuid | ||||
|      *     The UUID of the associated GuacamoleTunnel. | ||||
|      * @param connection | ||||
|      *     The active connection to copy data from. | ||||
|      */ | ||||
|     public APITunnel(ConnectionRecord record, String uuid) { | ||||
|         this.identifier = record.getIdentifier(); | ||||
|         this.startDate  = record.getStartDate(); | ||||
|         this.remoteHost = record.getRemoteHost(); | ||||
|         this.username   = record.getUsername(); | ||||
|         this.uuid       = uuid; | ||||
|     public APIActiveConnection(ActiveConnection connection) { | ||||
|         this.identifier           = connection.getIdentifier(); | ||||
|         this.connectionIdentifier = connection.getConnectionIdentifier(); | ||||
|         this.startDate            = connection.getStartDate(); | ||||
|         this.remoteHost           = connection.getRemoteHost(); | ||||
|         this.username             = connection.getUsername(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @@ -81,8 +80,8 @@ public class APITunnel { | ||||
|      * @return | ||||
|      *     The identifier of the connection associated with this tunnel. | ||||
|      */ | ||||
|     public String getIdentifier() { | ||||
|         return identifier; | ||||
|     public String getConnectionIdentifier() { | ||||
|         return connectionIdentifier; | ||||
|     } | ||||
|      | ||||
|     /** | ||||
| @@ -117,14 +116,15 @@ public class APITunnel { | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns the UUID of the underlying Guacamole tunnel. Absolutely every | ||||
|      * Guacamole tunnel has an associated UUID. | ||||
|      * Returns the identifier of the active connection itself. This is | ||||
|      * distinct from the connection identifier, and uniquely identifies a | ||||
|      * specific use of a connection. | ||||
|      * | ||||
|      * @return | ||||
|      *     The UUID of the underlying Guacamole tunnel. | ||||
|      *     The identifier of the active connection. | ||||
|      */ | ||||
|     public String getUUID() { | ||||
|         return uuid; | ||||
|     public String getIdentifier() { | ||||
|         return identifier; | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -20,10 +20,9 @@ | ||||
|  * THE SOFTWARE. | ||||
|  */ | ||||
| 
 | ||||
| package org.glyptodon.guacamole.net.basic.rest.tunnel; | ||||
| package org.glyptodon.guacamole.net.basic.rest.activeconnection; | ||||
| 
 | ||||
| import com.google.inject.Inject; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collection; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| @@ -37,9 +36,14 @@ import javax.ws.rs.core.MediaType; | ||||
| import org.glyptodon.guacamole.GuacamoleClientException; | ||||
| import org.glyptodon.guacamole.GuacamoleException; | ||||
| import org.glyptodon.guacamole.GuacamoleUnsupportedException; | ||||
| import org.glyptodon.guacamole.net.GuacamoleTunnel; | ||||
| import org.glyptodon.guacamole.net.auth.ConnectionRecord; | ||||
| import org.glyptodon.guacamole.net.auth.ActiveConnection; | ||||
| import org.glyptodon.guacamole.net.auth.Directory; | ||||
| import org.glyptodon.guacamole.net.auth.User; | ||||
| import org.glyptodon.guacamole.net.auth.UserContext; | ||||
| import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; | ||||
| import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; | ||||
| import org.glyptodon.guacamole.net.auth.permission.SystemPermission; | ||||
| import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; | ||||
| import org.glyptodon.guacamole.net.basic.rest.APIPatch; | ||||
| import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; | ||||
| import org.glyptodon.guacamole.net.basic.rest.PATCH; | ||||
| @@ -52,15 +56,15 @@ import org.slf4j.LoggerFactory; | ||||
|  *  | ||||
|  * @author Michael Jumper | ||||
|  */ | ||||
| @Path("/tunnels") | ||||
| @Path("/activeConnections") | ||||
| @Produces(MediaType.APPLICATION_JSON) | ||||
| @Consumes(MediaType.APPLICATION_JSON) | ||||
| public class TunnelRESTService { | ||||
| public class ActiveConnectionRESTService { | ||||
| 
 | ||||
|     /** | ||||
|      * Logger for this class. | ||||
|      */ | ||||
|     private static final Logger logger = LoggerFactory.getLogger(TunnelRESTService.class); | ||||
|     private static final Logger logger = LoggerFactory.getLogger(ActiveConnectionRESTService.class); | ||||
| 
 | ||||
|     /** | ||||
|      * A service for authenticating users from auth tokens. | ||||
| @@ -69,61 +73,78 @@ public class TunnelRESTService { | ||||
|     private AuthenticationService authenticationService; | ||||
| 
 | ||||
|     /** | ||||
|      * Retrieves the tunnels of all active connections visible to the current | ||||
|      * user. | ||||
|      * Gets a list of active connections in the system, filtering the returned | ||||
|      * list by the given permissions, if specified. | ||||
|      *  | ||||
|      * @param authToken | ||||
|      *     The authentication token that is used to authenticate the user | ||||
|      *     performing the operation. | ||||
|      * | ||||
|      * @param permissions | ||||
|      *     The set of permissions to filter with. A user must have one or more | ||||
|      *     of these permissions for a user to appear in the result.  | ||||
|      *     If null, no filtering will be performed. | ||||
|      *  | ||||
|      * @return | ||||
|      *     A map of the tunnels of all active connections visible to the | ||||
|      *     current user, where the key of each entry is the tunnel's UUID. | ||||
|      * | ||||
|      *     A list of all active connections. If a permission was specified, | ||||
|      *     this list will contain only those active connections for which the | ||||
|      *     current user has that permission. | ||||
|      *  | ||||
|      * @throws GuacamoleException | ||||
|      *     If an error occurs while retrieving the tunnels. | ||||
|      *     If an error is encountered while retrieving active connections. | ||||
|      */ | ||||
|     @GET | ||||
|     @Path("/") | ||||
|     @AuthProviderRESTExposure | ||||
|     public Map<String, APITunnel> getTunnels(@QueryParam("token") String authToken) | ||||
|     public Map<String, APIActiveConnection> getActiveConnections(@QueryParam("token") String authToken, | ||||
|             @QueryParam("permission") List<ObjectPermission.Type> permissions) | ||||
|             throws GuacamoleException { | ||||
| 
 | ||||
|         UserContext userContext = authenticationService.getUserContext(authToken); | ||||
|         User self = userContext.self(); | ||||
|          | ||||
|         // Do not filter on permissions if no permissions are specified | ||||
|         if (permissions != null && permissions.isEmpty()) | ||||
|             permissions = null; | ||||
| 
 | ||||
|         // Retrieve all active tunnels | ||||
|         Map<String, APITunnel> apiTunnels = new HashMap<String, APITunnel>(); | ||||
|         for (ConnectionRecord record : userContext.getActiveConnections()) { | ||||
|         // An admin user has access to any user | ||||
|         SystemPermissionSet systemPermissions = self.getSystemPermissions(); | ||||
|         boolean isAdmin = systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER); | ||||
| 
 | ||||
|             // Locate associated tunnel and UUID | ||||
|             GuacamoleTunnel tunnel = record.getTunnel(); | ||||
|             if (tunnel != null) { | ||||
|                 APITunnel apiTunnel = new APITunnel(record, tunnel.getUUID().toString()); | ||||
|                 apiTunnels.put(apiTunnel.getUUID(), apiTunnel); | ||||
|             } | ||||
|         // Get the directory | ||||
|         Directory<ActiveConnection> activeConnectionDirectory = userContext.getActiveConnectionDirectory(); | ||||
| 
 | ||||
|         // Filter users, if requested | ||||
|         Collection<String> activeConnectionIdentifiers = activeConnectionDirectory.getIdentifiers(); | ||||
|         if (!isAdmin && permissions != null) { | ||||
|             ObjectPermissionSet userPermissions = self.getUserPermissions(); | ||||
|             activeConnectionIdentifiers = userPermissions.getAccessibleObjects(permissions, activeConnectionIdentifiers); | ||||
|         } | ||||
|              | ||||
|         // Retrieve all active connections , converting to API active connections | ||||
|         Map<String, APIActiveConnection> apiActiveConnections = new HashMap<String, APIActiveConnection>(); | ||||
|         for (ActiveConnection activeConnection : activeConnectionDirectory.getAll(activeConnectionIdentifiers)) | ||||
|             apiActiveConnections.put(activeConnection.getIdentifier(), new APIActiveConnection(activeConnection)); | ||||
| 
 | ||||
|         return apiTunnels; | ||||
|         return apiActiveConnections; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Applies the given tunnel patches. This operation currently only supports | ||||
|      * deletion of tunnels through the "remove" patch operation. Deleting a | ||||
|      * tunnel effectively closing the tunnel and kills the associated | ||||
|      * connection. The path of each patch operation is of the form "/UUID" | ||||
|      * where UUID is the UUID of the tunnel being modified. | ||||
|      * Applies the given active connection patches. This operation currently | ||||
|      * only supports deletion of active connections through the "remove" patch | ||||
|      * operation. Deleting an active connection effectively kills the | ||||
|      * connection. The path of each patch operation is of the form "/ID" | ||||
|      * where ID is the identifier of the active connection being modified. | ||||
|      *  | ||||
|      * @param authToken | ||||
|      *     The authentication token that is used to authenticate the user | ||||
|      *     performing the operation. | ||||
|      * | ||||
|      * @param patches | ||||
|      *     The tunnel patches to apply for this request. | ||||
|      *     The active connection patches to apply for this request. | ||||
|      * | ||||
|      * @throws GuacamoleException | ||||
|      *     If an error occurs while deleting the tunnels. | ||||
|      *     If an error occurs while deleting the active connections. | ||||
|      */ | ||||
|     @PATCH | ||||
|     @Path("/") | ||||
| @@ -131,37 +152,28 @@ public class TunnelRESTService { | ||||
|     public void patchTunnels(@QueryParam("token") String authToken, | ||||
|             List<APIPatch<String>> patches) throws GuacamoleException { | ||||
| 
 | ||||
|         // Attempt to get all requested tunnels | ||||
|         UserContext userContext = authenticationService.getUserContext(authToken); | ||||
| 
 | ||||
|         // Build list of tunnels to delete | ||||
|         Collection<String> tunnelUUIDs = new ArrayList<String>(patches.size()); | ||||
|         // Get the directory | ||||
|         Directory<ActiveConnection> activeConnectionDirectory = userContext.getActiveConnectionDirectory(); | ||||
| 
 | ||||
|         // Close each connection listed for removal | ||||
|         for (APIPatch<String> patch : patches) { | ||||
| 
 | ||||
|             // Only remove is supported | ||||
|             if (patch.getOp() != APIPatch.Operation.remove) | ||||
|                 throw new GuacamoleUnsupportedException("Only the \"remove\" operation is supported when patching tunnels."); | ||||
|                 throw new GuacamoleUnsupportedException("Only the \"remove\" operation is supported when patching active connections."); | ||||
| 
 | ||||
|             // Retrieve and validate path | ||||
|             String path = patch.getPath(); | ||||
|             if (!path.startsWith("/")) | ||||
|                 throw new GuacamoleClientException("Patch paths must start with \"/\"."); | ||||
|              | ||||
|             // Add UUID | ||||
|             tunnelUUIDs.add(path.substring(1)); | ||||
| 
 | ||||
|             // Close connection  | ||||
|             activeConnectionDirectory.remove(path.substring(1)); | ||||
|              | ||||
|         } | ||||
|          | ||||
|         // Close each tunnel, if not already closed | ||||
|         Collection<ConnectionRecord> records = userContext.getActiveConnections(tunnelUUIDs); | ||||
|         for (ConnectionRecord record : records) { | ||||
| 
 | ||||
|             GuacamoleTunnel tunnel = record.getTunnel(); | ||||
|             if (tunnel != null && tunnel.isOpen()) | ||||
|                 tunnel.close(); | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -56,6 +56,12 @@ public class APIPermissionSet { | ||||
|     private Map<String, Set<ObjectPermission.Type>> connectionGroupPermissions = | ||||
|             new HashMap<String, Set<ObjectPermission.Type>>(); | ||||
|  | ||||
|     /** | ||||
|      * Map of active connection ID to the set of granted permissions. | ||||
|      */ | ||||
|     private Map<String, Set<ObjectPermission.Type>> activeConnectionPermissions = | ||||
|             new HashMap<String, Set<ObjectPermission.Type>>(); | ||||
|  | ||||
|     /** | ||||
|      * Map of user ID to the set of granted permissions. | ||||
|      */ | ||||
| @@ -149,10 +155,11 @@ public class APIPermissionSet { | ||||
|     public APIPermissionSet(User user) throws GuacamoleException { | ||||
|  | ||||
|         // Add all permissions from the provided user | ||||
|         addSystemPermissions(systemPermissions,          user.getSystemPermissions()); | ||||
|         addObjectPermissions(connectionPermissions,      user.getConnectionPermissions()); | ||||
|         addObjectPermissions(connectionGroupPermissions, user.getConnectionGroupPermissions()); | ||||
|         addObjectPermissions(userPermissions,            user.getUserPermissions()); | ||||
|         addSystemPermissions(systemPermissions,           user.getSystemPermissions()); | ||||
|         addObjectPermissions(connectionPermissions,       user.getConnectionPermissions()); | ||||
|         addObjectPermissions(connectionGroupPermissions,  user.getConnectionGroupPermissions()); | ||||
|         addObjectPermissions(activeConnectionPermissions, user.getActiveConnectionPermissions()); | ||||
|         addObjectPermissions(userPermissions,             user.getUserPermissions()); | ||||
|          | ||||
|     } | ||||
|  | ||||
| @@ -186,6 +193,21 @@ public class APIPermissionSet { | ||||
|         return connectionGroupPermissions; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns a map of active connection IDs to the set of permissions granted | ||||
|      * for that active connection. If no permissions are granted to a particular | ||||
|      * active connection, its ID will not be present as a key in the map. This | ||||
|      * map is mutable, and changes to this map will affect the permission set | ||||
|      * directly. | ||||
|      * | ||||
|      * @return | ||||
|      *     A map of active connection IDs to the set of permissions granted for | ||||
|      *     that active connection. | ||||
|      */ | ||||
|     public Map<String, Set<ObjectPermission.Type>> getActiveConnectionPermissions() { | ||||
|         return activeConnectionPermissions; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns a map of user IDs to the set of permissions granted for that | ||||
|      * user. If no permissions are granted to a particular user, its ID will | ||||
| @@ -238,6 +260,19 @@ public class APIPermissionSet { | ||||
|         this.connectionGroupPermissions = connectionGroupPermissions; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Replaces the current map of active connection permissions with the give | ||||
|      * map, which must map active connection ID to its corresponding set of | ||||
|      * granted permissions. If an active connection has no permissions, its ID | ||||
|      * must not be present as a key in the map. | ||||
|      * | ||||
|      * @param activeConnectionPermissions | ||||
|      *     The map which must replace the currently-stored map of permissions. | ||||
|      */ | ||||
|     public void setActiveConnectionPermissions(Map<String, Set<ObjectPermission.Type>> activeConnectionPermissions) { | ||||
|         this.activeConnectionPermissions = activeConnectionPermissions; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Replaces the current map of user permissions with the given map, which | ||||
|      * must map user ID to its corresponding set of granted permissions. If a | ||||
|   | ||||
| @@ -95,4 +95,10 @@ public class APIUserWrapper implements User { | ||||
|         throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access."); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public ObjectPermissionSet getActiveConnectionPermissions() | ||||
|             throws GuacamoleException { | ||||
|         throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access."); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -92,6 +92,12 @@ public class UserRESTService { | ||||
|      */ | ||||
|     private static final String CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX = "/connectionGroupPermissions/"; | ||||
|  | ||||
|     /** | ||||
|      * The prefix of any path within an operation of a JSON patch which | ||||
|      * modifies the permissions of a user regarding a specific active connection. | ||||
|      */ | ||||
|     private static final String ACTIVE_CONNECTION_PERMISSION_PATCH_PATH_PREFIX = "/activeConnectionPermissions/"; | ||||
|  | ||||
|     /** | ||||
|      * The prefix of any path within an operation of a JSON patch which | ||||
|      * modifies the permissions of a user regarding another, specific user. | ||||
| @@ -503,10 +509,11 @@ public class UserRESTService { | ||||
|             throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\""); | ||||
|  | ||||
|         // Permission patches for all types of permissions | ||||
|         PermissionSetPatch<ObjectPermission> connectionPermissionPatch      = new PermissionSetPatch<ObjectPermission>(); | ||||
|         PermissionSetPatch<ObjectPermission> connectionGroupPermissionPatch = new PermissionSetPatch<ObjectPermission>(); | ||||
|         PermissionSetPatch<ObjectPermission> userPermissionPatch            = new PermissionSetPatch<ObjectPermission>(); | ||||
|         PermissionSetPatch<SystemPermission> systemPermissionPatch          = new PermissionSetPatch<SystemPermission>(); | ||||
|         PermissionSetPatch<ObjectPermission> connectionPermissionPatch       = new PermissionSetPatch<ObjectPermission>(); | ||||
|         PermissionSetPatch<ObjectPermission> connectionGroupPermissionPatch  = new PermissionSetPatch<ObjectPermission>(); | ||||
|         PermissionSetPatch<ObjectPermission> activeConnectionPermissionPatch = new PermissionSetPatch<ObjectPermission>(); | ||||
|         PermissionSetPatch<ObjectPermission> userPermissionPatch             = new PermissionSetPatch<ObjectPermission>(); | ||||
|         PermissionSetPatch<SystemPermission> systemPermissionPatch           = new PermissionSetPatch<SystemPermission>(); | ||||
|          | ||||
|         // Apply all patch operations individually | ||||
|         for (APIPatch<String> patch : patches) { | ||||
| @@ -539,6 +546,19 @@ public class UserRESTService { | ||||
|                  | ||||
|             } | ||||
|  | ||||
|             // Create active connection permission if path has active connection prefix | ||||
|             else if (path.startsWith(ACTIVE_CONNECTION_PERMISSION_PATCH_PATH_PREFIX)) { | ||||
|  | ||||
|                 // Get identifier and type from patch operation | ||||
|                 String identifier = path.substring(ACTIVE_CONNECTION_PERMISSION_PATCH_PATH_PREFIX.length()); | ||||
|                 ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue()); | ||||
|  | ||||
|                 // Create and update corresponding permission | ||||
|                 ObjectPermission permission = new ObjectPermission(type, identifier); | ||||
|                 updatePermissionSet(patch.getOp(), activeConnectionPermissionPatch, permission); | ||||
|                  | ||||
|             } | ||||
|  | ||||
|             // Create user permission if path has user prefix | ||||
|             else if (path.startsWith(USER_PERMISSION_PATCH_PATH_PREFIX)) { | ||||
|  | ||||
| @@ -573,6 +593,7 @@ public class UserRESTService { | ||||
|         // Save the permission changes | ||||
|         connectionPermissionPatch.apply(user.getConnectionPermissions()); | ||||
|         connectionGroupPermissionPatch.apply(user.getConnectionGroupPermissions()); | ||||
|         activeConnectionPermissionPatch.apply(user.getActiveConnectionPermissions()); | ||||
|         userPermissionPatch.apply(user.getUserPermissions()); | ||||
|         systemPermissionPatch.apply(user.getSystemPermissions()); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user