diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java index 60d094c78..bbbfc9d2b 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java @@ -25,6 +25,7 @@ package org.glyptodon.guacamole.net.basic.rest.connectiongroup; import com.google.inject.Inject; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; @@ -45,6 +46,7 @@ import org.glyptodon.guacamole.net.auth.User; import org.glyptodon.guacamole.net.auth.UserContext; import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission; import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; +import org.glyptodon.guacamole.net.auth.permission.SystemPermission; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; import org.glyptodon.guacamole.net.basic.rest.ObjectRetrievalService; import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; @@ -79,6 +81,40 @@ public class ConnectionGroupRESTService { @Inject private ObjectRetrievalService retrievalService; + /** + * Determines whether the given user has at least one of the given + * permissions for the connection having the given identifier. + * + * @param user + * The user to check permissions for. + * + * @param identifier + * The identifier of the connection to check permissions for. + * + * @param permissions + * The permissions to check. The given user must have one or more of + * these permissions for this function to return true. + * + * @return + * true if the user has at least one of the given permissions. + */ + private boolean hasConnectionPermission(User user, String identifier, + List permissions) throws GuacamoleException { + + // Determine whether user has at least one of the given permissions + for (ObjectPermission.Type permission : permissions) { + + ConnectionPermission connectionPermission = new ConnectionPermission(permission, identifier); + if (user.hasPermission(connectionPermission)) + return true; + + } + + // None of the given permissions were present + return false; + + } + /** * Retrieves the given connection group from the user context, including * all descendant connections and groups if requested. @@ -93,10 +129,10 @@ public class ConnectionGroupRESTService { * Whether the descendant connections and groups of the given * connection group should also be retrieved. * - * @param permission - * The permission the current user must have for a connection or - * connection group to be returned in the results, if any. If null - * is specified, no filtering by permission will be performed. + * @param permissions + * The set of permissions to filter with. A user must have one or more + * of these permissions for a connection to appear in the result. + * If null, no filtering will be performed. * * @return * The requested connection group, or null if no such connection group @@ -107,10 +143,13 @@ public class ConnectionGroupRESTService { * or any of its descendants. */ private APIConnectionGroup retrieveConnectionGroup(UserContext userContext, - String identifier, boolean includeDescendants, ObjectPermission.Type permission) + String identifier, boolean includeDescendants, List permissions) throws GuacamoleException { User self = userContext.self(); + + // An admin user has access to any connection or connection group + boolean isAdmin = self.hasPermission(new SystemPermission(SystemPermission.Type.ADMINISTER)); // Retrieve specified connection group ConnectionGroup connectionGroup; @@ -139,7 +178,7 @@ public class ConnectionGroupRESTService { continue; // Filter based on permission, if requested - if (permission == null || self.hasPermission(new ConnectionPermission(permission, childIdentifier))) + if (isAdmin || permissions == null || hasConnectionPermission(self, childIdentifier, permissions)) apiConnections.add(new APIConnection(childConnection)); } @@ -154,7 +193,7 @@ public class ConnectionGroupRESTService { for (String childIdentifier : groupDirectory.getIdentifiers()) { // Pull current connection group - silently ignore if connection group was removed prior to read - APIConnectionGroup childConnectionGroup = retrieveConnectionGroup(userContext, childIdentifier, true, permission); + APIConnectionGroup childConnectionGroup = retrieveConnectionGroup(userContext, childIdentifier, true, permissions); if (childConnectionGroup == null) continue; @@ -215,11 +254,11 @@ public class ConnectionGroupRESTService { * @param connectionGroupID * The ID of the connection group to retrieve. * - * @param permission + * @param permissions * If specified, limit the returned list to only those connections for - * which the current user has the given permission. Otherwise, all - * visible connections are returned. Connection groups are unaffected - * by this parameter. + * which the current user has any of the given permissions. Otherwise, + * all visible connections are returned. Connection groups are + * unaffected by this parameter. * * @return * The requested connection group, including all descendants. @@ -233,13 +272,13 @@ public class ConnectionGroupRESTService { @AuthProviderRESTExposure public APIConnectionGroup getConnectionGroupTree(@QueryParam("token") String authToken, @PathParam("connectionGroupID") String connectionGroupID, - @QueryParam("permission") ObjectPermission.Type permission) + @QueryParam("permission") List permissions) throws GuacamoleException { UserContext userContext = authenticationService.getUserContext(authToken); // Retrieve requested connection group and all descendants - APIConnectionGroup connectionGroup = retrieveConnectionGroup(userContext, connectionGroupID, true, permission); + APIConnectionGroup connectionGroup = retrieveConnectionGroup(userContext, connectionGroupID, true, permissions); if (connectionGroup == null) throw new GuacamoleResourceNotFoundException("No such connection group: \"" + connectionGroupID + "\""); diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js index 0f57ff032..0e9bb5a72 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js @@ -35,8 +35,10 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i // Required services var $location = $injector.get('$location'); var $routeParams = $injector.get('$routeParams'); + var authenticationService = $injector.get('authenticationService'); var connectionService = $injector.get('connectionService'); var connectionGroupService = $injector.get('connectionGroupService'); + var permissionService = $injector.get('permissionService'); var protocolService = $injector.get('protocolService'); var translationStringService = $injector.get('translationStringService'); @@ -95,6 +97,20 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i * @type HistoryEntryWrapper[] */ $scope.historyEntryWrappers = null; + + /** + * Whether the user has UPDATE permission for the current connection. + * + * @type boolean + */ + $scope.hasUpdatePermission = null; + + /** + * Whether the user has DELETE permission for the current connection. + * + * @type boolean + */ + $scope.hasDeletePermission = null; /** * Returns whether critical data has completed being loaded. @@ -109,15 +125,34 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i && $scope.rootGroup !== null && $scope.connection !== null && $scope.parameters !== null - && $scope.historyEntryWrappers !== null; + && $scope.historyEntryWrappers !== null + && $scope.hasUpdatePermission !== null + && $scope.hasDeletePermission !== null; }; // Pull connection group hierarchy - connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER, PermissionSet.ObjectPermissionType.UPDATE) + connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER, + [PermissionSet.ObjectPermissionType.ADMINISTER]) .success(function connectionGroupReceived(rootGroup) { $scope.rootGroup = rootGroup; }); + + // Query the user's permissions for the current connection + permissionService.getPermissions(authenticationService.getCurrentUserID()) + .success(function permissionsReceived(permissions) { + + // Check if the user has UPDATE permission + $scope.hasUpdatePermission = + PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) + || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier); + + // Check if the user has DELETE permission + $scope.hasDeletePermission = + PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) + || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier); + + }); // Get protocol metadata protocolService.getProtocols().success(function protocolsReceived(protocols) { diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js index 31f751ea0..d8f0ea902 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js @@ -33,7 +33,9 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope' // Required services var $location = $injector.get('$location'); var $routeParams = $injector.get('$routeParams'); + var authenticationService = $injector.get('authenticationService'); var connectionGroupService = $injector.get('connectionGroupService'); + var permissionService = $injector.get('permissionService'); /** * An action to be provided along with the object sent to showStatus which @@ -68,6 +70,20 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope' * @type ConnectionGroup */ $scope.connectionGroup = null; + + /** + * Whether the user has UPDATE permission for the current connection group. + * + * @type boolean + */ + $scope.hasUpdatePermission = null; + + /** + * Whether the user has DELETE permission for the current connection group. + * + * @type boolean + */ + $scope.hasDeletePermission = null; /** * Returns whether critical data has completed being loaded. @@ -78,14 +94,32 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope' */ $scope.isLoaded = function isLoaded() { - return $scope.rootGroup !== null - && $scope.connectionGroup !== null; + return $scope.rootGroup !== null + && $scope.connectionGroup !== null + && $scope.hasUpdatePermission !== null + && $scope.hasDeletePermission !== null; }; + + // Query the user's permissions for the current connection group + permissionService.getPermissions(authenticationService.getCurrentUserID()) + .success(function permissionsReceived(permissions) { + + // Check if the user has UPDATE permission + $scope.hasUpdatePermission = + PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) + || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, identifier); + + // Check if the user has DELETE permission + $scope.hasDeletePermission = + PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) + || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE, identifier); + + }); // Pull connection group hierarchy - connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER, PermissionSet.ObjectPermissionType.UPDATE) + connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER, [PermissionSet.ObjectPermissionType.ADMINISTER]) .success(function connectionGroupReceived(rootGroup) { $scope.rootGroup = rootGroup; }); diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageController.js b/guacamole/src/main/webapp/app/manage/controllers/manageController.js index e1a992191..391efcff2 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageController.js @@ -178,8 +178,9 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', }); - // Retrieve all connections for which we have UPDATE permission - connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER, PermissionSet.ObjectPermissionType.UPDATE) + // Retrieve all connections for which we have UPDATE or DELETE permission + connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER, + [PermissionSet.ObjectPermissionType.UPDATE, PermissionSet.ObjectPermissionType.DELETE]) .success(function connectionGroupReceived(rootGroup) { $scope.rootGroup = rootGroup; }); diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js index e7642149f..dd769a933 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js @@ -103,8 +103,8 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto $scope.permissionFlags = PermissionFlagSet.fromPermissionSet(permissions); }); - // Retrieve all connections for which we have UPDATE permission - connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER, PermissionSet.ObjectPermissionType.ADMINISTER) + // Retrieve all connections for which we have ADMINISTER permission + connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER, [PermissionSet.ObjectPermissionType.ADMINISTER]) .success(function connectionGroupReceived(rootGroup) { $scope.rootGroup = rootGroup; }); diff --git a/guacamole/src/main/webapp/app/manage/templates/manageConnection.html b/guacamole/src/main/webapp/app/manage/templates/manageConnection.html index 4ffe4fe08..85dca606a 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageConnection.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageConnection.html @@ -76,9 +76,9 @@ THE SOFTWARE.
- + - +
diff --git a/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html b/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html index 169717dfb..0fca9ea5d 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageConnectionGroup.html @@ -61,9 +61,9 @@ THE SOFTWARE.
- + - +
diff --git a/guacamole/src/main/webapp/app/rest/services/connectionGroupService.js b/guacamole/src/main/webapp/app/rest/services/connectionGroupService.js index b2322dc80..978fd43c4 100644 --- a/guacamole/src/main/webapp/app/rest/services/connectionGroupService.js +++ b/guacamole/src/main/webapp/app/rest/services/connectionGroupService.js @@ -39,17 +39,17 @@ angular.module('rest').factory('connectionGroupService', ['$http', 'authenticati * The ID of the connection group to retrieve. If not provided, the * root connection group will be retrieved by default. * - * @param {String} [permissionType] - * The permission type string of the permission that the current user - * must have for a given connection or connection group to appear - * within the result. Valid values are listed within - * PermissionSet.ObjectType. + * @param {String[]} [permissionType] + * The set of permissions to filter with. A user must have one or more + * of these permissions for a connection to appear in the result. + * If null, no filtering will be performed. Valid values are listed + * within PermissionSet.ObjectType. * * @returns {Promise.ConnectionGroup} * A promise which will resolve with a @link{ConnectionGroup} upon * success. */ - service.getConnectionGroupTree = function getConnectionGroupTree(connectionGroupID, permissionType) { + service.getConnectionGroupTree = function getConnectionGroupTree(connectionGroupID, permissionTypes) { // Use the root connection group ID if no ID is passed in connectionGroupID = connectionGroupID || ConnectionGroup.ROOT_IDENTIFIER; @@ -60,8 +60,8 @@ angular.module('rest').factory('connectionGroupService', ['$http', 'authenticati }; // Add permission filter if specified - if (permissionType) - httpParameters.permission = permissionType; + if (permissionTypes) + httpParameters.permission = permissionTypes; // Retrieve connection group return $http({