GUACAMOLE-220: Migrate user management controller to ManagementPermissions.
Project: http://git-wip-us.apache.org/repos/asf/guacamole-client/repo Commit: http://git-wip-us.apache.org/repos/asf/guacamole-client/commit/4f43ddc4 Tree: http://git-wip-us.apache.org/repos/asf/guacamole-client/tree/4f43ddc4 Diff: http://git-wip-us.apache.org/repos/asf/guacamole-client/diff/4f43ddc4 Branch: refs/heads/master Commit: 4f43ddc4203a59c7aea6834cfd8b5961c74ba57e Parents: 507202d Author: Michael Jumper <mjum...@apache.org> Authored: Tue May 1 00:03:52 2018 -0700 Committer: Michael Jumper <mjum...@apache.org> Committed: Tue May 1 00:12:58 2018 -0700 ---------------------------------------------------------------------- .../manage/controllers/manageUserController.js | 464 ++++++------------- .../webapp/app/manage/templates/manageUser.html | 18 +- 2 files changed, 153 insertions(+), 329 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/4f43ddc4/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js ---------------------------------------------------------------------- diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js index c385065..388e717 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js @@ -24,14 +24,16 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto function manageUserController($scope, $injector) { // Required types - var PageDefinition = $injector.get('PageDefinition'); - var PermissionFlagSet = $injector.get('PermissionFlagSet'); - var PermissionSet = $injector.get('PermissionSet'); - var User = $injector.get('User'); + var ManagementPermissions = $injector.get('ManagementPermissions'); + var PageDefinition = $injector.get('PageDefinition'); + var PermissionFlagSet = $injector.get('PermissionFlagSet'); + var PermissionSet = $injector.get('PermissionSet'); + var User = $injector.get('User'); // Required services var $location = $injector.get('$location'); var $routeParams = $injector.get('$routeParams'); + var $q = $injector.get('$q'); var authenticationService = $injector.get('authenticationService'); var dataSourceService = $injector.get('dataSourceService'); var guacNotification = $injector.get('guacNotification'); @@ -128,13 +130,31 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto $scope.permissionFlags = null; /** - * A map of data source identifiers to the set of all permissions - * associated with the current user under that data source, or null if the - * user's permissions have not yet been loaded. + * The set of permissions that will be added to the user when the user is + * saved. Permissions will only be present in this set if they are + * manually added, and not later manually removed before saving. * - * @type Object.<String, PermissionSet> + * @type PermissionSet */ - $scope.permissions = null; + $scope.permissionsAdded = new PermissionSet(); + + /** + * The set of permissions that will be removed from the user when the user + * is saved. Permissions will only be present in this set if they are + * manually removed, and not later manually added before saving. + * + * @type PermissionSet + */ + $scope.permissionsRemoved = new PermissionSet(); + + /** + * The managment-related actions that the current user may perform on the + * user currently being created/modified, or null if the current user's + * permissions have not yet been loaded. + * + * @type ManagementPermissions + */ + $scope.managementPermissions = null; /** * All available user attributes. This is only the set of attribute @@ -162,11 +182,10 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto */ $scope.isLoaded = function isLoaded() { - return $scope.users !== null - && $scope.permissionFlags !== null - && $scope.rootGroups !== null - && $scope.permissions !== null - && $scope.attributes !== null; + return $scope.users !== null + && $scope.permissionFlags !== null + && $scope.managementPermissions !== null + && $scope.attributes !== null; }; @@ -196,97 +215,6 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto }; /** - * Returns whether the current user can change attributes explicitly - * associated with the user being edited within the given data source. - * - * @param {String} [dataSource] - * The identifier of the data source to check. If omitted, this will - * default to the currently-selected data source. - * - * @returns {Boolean} - * true if the current user can change attributes associated with the - * user being edited, false otherwise. - */ - $scope.canChangeAttributes = function canChangeAttributes(dataSource) { - - // Do not check if permissions are not yet loaded - if (!$scope.permissions) - return false; - - // Use currently-selected data source if unspecified - dataSource = dataSource || $scope.dataSource; - - // Attributes can always be set if we are creating the user - if (!$scope.userExists(dataSource)) - return true; - - // The administrator can always change attributes - if (PermissionSet.hasSystemPermission($scope.permissions[dataSource], - PermissionSet.SystemPermissionType.ADMINISTER)) - return true; - - // Otherwise, can change attributes if we have permission to update this user - return PermissionSet.hasUserPermission($scope.permissions[dataSource], - PermissionSet.ObjectPermissionType.UPDATE, username); - - }; - - /** - * Returns whether the current user can change/set all user attributes for - * the user being edited, regardless of whether those attributes are - * already explicitly associated with that user. - * - * @returns {Boolean} - * true if the current user can change all attributes for the user - * being edited, regardless of whether those attributes are already - * explicitly associated with that user, false otherwise. - */ - $scope.canChangeAllAttributes = function canChangeAllAttributes() { - - // All attributes can be set if we are creating the user - return !$scope.userExists($scope.dataSource); - - }; - - /** - * Returns whether the current user can change permissions of any kind - * which are associated with the user being edited within the given data - * source. - * - * @param {String} [dataSource] - * The identifier of the data source to check. If omitted, this will - * default to the currently-selected data source. - * - * @returns {Boolean} - * true if the current user can grant or revoke permissions of any kind - * which are associated with the user being edited, false otherwise. - */ - $scope.canChangePermissions = function canChangePermissions(dataSource) { - - // Do not check if permissions are not yet loaded - if (!$scope.permissions) - return false; - - // Use currently-selected data source if unspecified - dataSource = dataSource || $scope.dataSource; - - // Permissions can always be set if we are creating the user - if (!$scope.userExists(dataSource)) - return true; - - // The administrator can always modify permissions - if (PermissionSet.hasSystemPermission($scope.permissions[dataSource], - PermissionSet.SystemPermissionType.ADMINISTER)) - return true; - - // Otherwise, can only modify permissions if we have explicit - // ADMINISTER permission - return PermissionSet.hasUserPermission($scope.permissions[dataSource], - PermissionSet.ObjectPermissionType.ADMINISTER, username); - - }; - - /** * Returns whether the current user can edit the username of the user being * edited within the given data source. * @@ -303,151 +231,158 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto }; /** - * Returns whether the current user can save the user being edited within - * the given data source. Saving will create or update that user depending - * on whether the user already exists. + * Loads the data associated with the user having the given username, + * preparing the interface for making modifications to that existing user. * - * @param {String} [dataSource] - * The identifier of the data source to check. If omitted, this will - * default to the currently-selected data source. + * @param {String} dataSource + * The unique identifier of the data source containing the user to + * load. * - * @returns {Boolean} - * true if the current user can save changes to the user being edited, - * false otherwise. - */ - $scope.canSaveUser = function canSaveUser(dataSource) { - - // Do not check if permissions are not yet loaded - if (!$scope.permissions) - return false; - - // Use currently-selected data source if unspecified - dataSource = dataSource || $scope.dataSource; + * @param {String} username + * The username of the user to load. + * + * @returns {Promise} + * A promise which is resolved when the interface has been prepared for + * editing the given user. + */ + var loadExistingUser = function loadExistingUser(dataSource, username) { + return $q.all({ + users : dataSourceService.apply(userService.getUser, dataSources, username), + permissions : permissionService.getPermissions(dataSource, username) + }) + .then(function userDataRetrieved(values) { - // The administrator can always save users - if (PermissionSet.hasSystemPermission($scope.permissions[dataSource], - PermissionSet.SystemPermissionType.ADMINISTER)) - return true; + $scope.users = values.users; + $scope.user = values.users[dataSource]; - // If user does not exist, can only save if we have permission to create users - if (!$scope.userExists(dataSource)) - return PermissionSet.hasSystemPermission($scope.permissions[dataSource], - PermissionSet.SystemPermissionType.CREATE_USER); + // Create skeleton user if user does not exist + if (!$scope.user) + $scope.user = new User({ + 'username' : username + }); - // Otherwise, can only save if we have permission to update this user - return PermissionSet.hasUserPermission($scope.permissions[dataSource], - PermissionSet.ObjectPermissionType.UPDATE, username); + // The current user will be associated with username of the existing + // user in the retrieved permission set + $scope.selfUsername = username; + $scope.permissionFlags = PermissionFlagSet.fromPermissionSet(values.permissions); + }); }; /** - * Returns whether the current user can clone the user being edited within - * the given data source. + * Loads the data associated with the user having the given username, + * preparing the interface for cloning that existing user. * - * @param {String} [dataSource] - * The identifier of the data source to check. If omitted, this will - * default to the currently-selected data source. + * @param {String} dataSource + * The unique identifier of the data source containing the user to + * be cloned. * - * @returns {Boolean} - * true if the current user can clone the user being edited, false - * otherwise. - */ - $scope.canCloneUser = function canCloneUser(dataSource) { - - // Do not check if permissions are not yet loaded - if (!$scope.permissions) - return false; - - // Use currently-selected data source if unspecified - dataSource = dataSource || $scope.dataSource; - - // If we are not editing an existing user, we cannot clone - if (!$scope.userExists($scope.dataSource)) - return false; + * @param {String} username + * The username of the user being cloned. + * + * @returns {Promise} + * A promise which is resolved when the interface has been prepared for + * cloning the given user. + */ + var loadClonedUser = function loadClonedUser(dataSource, username) { + return $q.all({ + users : dataSourceService.apply(userService.getUser, [dataSource], username), + permissions : permissionService.getPermissions(dataSource, username) + }) + .then(function userDataRetrieved(values) { - // The administrator can always clone users - if (PermissionSet.hasSystemPermission($scope.permissions[dataSource], - PermissionSet.SystemPermissionType.ADMINISTER)) - return true; + $scope.users = {}; + $scope.user = values.users[dataSource]; - // Otherwise we need explicit CREATE_USER permission - return PermissionSet.hasSystemPermission($scope.permissions[dataSource], - PermissionSet.SystemPermissionType.CREATE_USER); + // The current user will be associated with cloneSourceUsername in the + // retrieved permission set + $scope.selfUsername = username; + $scope.permissionFlags = PermissionFlagSet.fromPermissionSet(values.permissions); + $scope.permissionsAdded = values.permissions; + }); }; /** - * Returns whether the current user can delete the user being edited from - * the given data source. - * - * @param {String} [dataSource] - * The identifier of the data source to check. If omitted, this will - * default to the currently-selected data source. + * Loads skeleton user data, preparing the interface for creating a new + * user. * - * @returns {Boolean} - * true if the current user can delete the user being edited, false - * otherwise. + * @returns {Promise} + * A promise which is resolved when the interface has been prepared for + * creating a new user. */ - $scope.canDeleteUser = function canDeleteUser(dataSource) { - - // Do not check if permissions are not yet loaded - if (!$scope.permissions) - return false; + var loadSkeletonUser = function loadSkeletonUser() { - // Use currently-selected data source if unspecified - dataSource = dataSource || $scope.dataSource; + // No users exist regardless of data source if there is no username + $scope.users = {}; - // Can't delete what doesn't exist - if (!$scope.userExists(dataSource)) - return false; + // Use skeleton user object with no associated permissions + $scope.user = new User(); + $scope.permissionFlags = new PermissionFlagSet(); - // The administrator can always delete users - if (PermissionSet.hasSystemPermission($scope.permissions[dataSource], - PermissionSet.SystemPermissionType.ADMINISTER)) - return true; + // As no permissions are yet associated with the user, it is safe to + // use any non-empty username as a placeholder for self-referential + // permissions + $scope.selfUsername = 'SELF'; - // Otherwise, require explicit DELETE permission on the user - return PermissionSet.hasUserPermission($scope.permissions[dataSource], - PermissionSet.ObjectPermissionType.DELETE, username); + return $q.resolve(); }; /** - * Returns whether the user being edited within the given data source is - * read-only, and thus cannot be modified by the current user. - * - * @param {String} [dataSource] - * The identifier of the data source to check. If omitted, this will - * default to the currently-selected data source. + * Loads the data requred for performing the management task requested + * through the route parameters given at load time, automatically preparing + * the interface for editing an existing user, cloning an existing user, or + * creating an entirely new user. * - * @returns {Boolean} - * true if the user being edited is actually read-only and cannot be - * edited at all, false otherwise. + * @returns {Promise} + * A promise which is resolved when the interface has been prepared + * for performing the requested management task. */ - $scope.isReadOnly = function isReadOnly(dataSource) { + var loadRequestedUser = function loadRequestedUser() { - // Use currently-selected data source if unspecified - dataSource = dataSource || $scope.dataSource; + // Pull user data and permissions if we are editing an existing user + if (username) + return loadExistingUser($scope.dataSource, username); + + // If we are cloning an existing user, pull his/her data instead + if (cloneSourceUsername) + return loadClonedUser($scope.dataSource, cloneSourceUsername); - // User is read-only if they cannot be saved - return !$scope.canSaveUser(dataSource); + return loadSkeletonUser(); }; - // Update visible account pages whenever available users/permissions changes - $scope.$watchGroup(['users', 'permissions'], function updateAccountPages() { + // Populate interface with requested data + $q.all({ + userData : loadRequestedUser(), + permissions : dataSourceService.apply(permissionService.getEffectivePermissions, dataSources, currentUsername), + attributes : schemaService.getUserAttributes($scope.dataSource) + }) + .then(function dataReceived(values) { + + var managementPermissions = {}; + + $scope.attributes = values.attributes; // Generate pages for each applicable data source $scope.accountPages = []; angular.forEach(dataSources, function addAccountPage(dataSource) { // Determine whether data source contains this user - var linked = $scope.userExists(dataSource); - var readOnly = $scope.isReadOnly(dataSource); + var exists = (dataSource in $scope.users); + + // Calculate management actions available for this specific account + managementPermissions[dataSource] = ManagementPermissions.fromPermissionSet( + values.permissions[dataSource], + PermissionSet.SystemPermissionType.CREATE_USER, + PermissionSet.hasUserPermission, + exists ? username : null); // Account is not relevant if it does not exist and cannot be // created - if (!linked && readOnly) + var readOnly = !managementPermissions[dataSource].canSaveObject; + if (!exists && readOnly) return; // Only the selected data source is relevant when cloning @@ -457,7 +392,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto // Determine class name based on read-only / linked status var className; if (readOnly) className = 'read-only'; - else if (linked) className = 'linked'; + else if (exists) className = 'linked'; else className = 'unlinked'; // Add page entry @@ -469,121 +404,10 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto }); - }); + $scope.managementPermissions = managementPermissions[$scope.dataSource]; - // Pull user attribute schema - schemaService.getUserAttributes($scope.dataSource).then(function attributesReceived(attributes) { - $scope.attributes = attributes; }, requestService.WARN); - // Pull user data and permissions if we are editing an existing user - if (username) { - - // Pull user data - dataSourceService.apply(userService.getUser, dataSources, username) - .then(function usersReceived(users) { - - // Get user for currently-selected data source - $scope.users = users; - $scope.user = users[$scope.dataSource]; - - // Create skeleton user if user does not exist - if (!$scope.user) - $scope.user = new User({ - 'username' : username - }); - - }, requestService.WARN); - - // The current user will be associated with username of the existing - // user in the retrieved permission set - $scope.selfUsername = username; - - // Pull user permissions - permissionService.getPermissions($scope.dataSource, username).then(function gotPermissions(permissions) { - $scope.permissionFlags = PermissionFlagSet.fromPermissionSet(permissions); - }) - - // If permissions cannot be retrieved, use empty permissions - ['catch'](requestService.createErrorCallback(function permissionRetrievalFailed() { - $scope.permissionFlags = new PermissionFlagSet(); - })); - } - - // If we are cloning an existing user, pull his/her data instead - else if (cloneSourceUsername) { - - dataSourceService.apply(userService.getUser, dataSources, cloneSourceUsername) - .then(function usersReceived(users) { - - // Get user for currently-selected data source - $scope.users = {}; - $scope.user = users[$scope.dataSource]; - - }, requestService.WARN); - - // The current user will be associated with cloneSourceUsername in the - // retrieved permission set - $scope.selfUsername = cloneSourceUsername; - - // Pull user permissions - permissionService.getPermissions($scope.dataSource, cloneSourceUsername) - .then(function gotPermissions(permissions) { - $scope.permissionFlags = PermissionFlagSet.fromPermissionSet(permissions); - $scope.permissionsAdded = permissions; - }) - - // If permissions cannot be retrieved, use empty permissions - ['catch'](requestService.createErrorCallback(function permissionRetrievalFailed() { - $scope.permissionFlags = new PermissionFlagSet(); - })); - } - - // Use skeleton data if we are creating a new user - else { - - // No users exist regardless of data source if there is no username - $scope.users = {}; - - // Use skeleton user object with no associated permissions - $scope.user = new User(); - $scope.permissionFlags = new PermissionFlagSet(); - - // As no permissions are yet associated with the user, it is safe to - // use any non-empty username as a placeholder for self-referential - // permissions - $scope.selfUsername = 'SELF'; - - } - - // Query the user's permissions for the current user - dataSourceService.apply( - permissionService.getEffectivePermissions, - dataSources, - currentUsername - ) - .then(function permissionsReceived(permissions) { - $scope.permissions = permissions; - }, requestService.WARN); - - /** - * The set of permissions that will be added to the user when the user is - * saved. Permissions will only be present in this set if they are - * manually added, and not later manually removed before saving. - * - * @type PermissionSet - */ - $scope.permissionsAdded = new PermissionSet(); - - /** - * The set of permissions that will be removed from the user when the user - * is saved. Permissions will only be present in this set if they are - * manually removed, and not later manually added before saving. - * - * @type PermissionSet - */ - $scope.permissionsRemoved = new PermissionSet(); - /** * Cancels all pending edits, returning to the management page. */ http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/4f43ddc4/guacamole/src/main/webapp/app/manage/templates/manageUser.html ---------------------------------------------------------------------- diff --git a/guacamole/src/main/webapp/app/manage/templates/manageUser.html b/guacamole/src/main/webapp/app/manage/templates/manageUser.html index 5fed148..24db74e 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageUser.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageUser.html @@ -11,12 +11,12 @@ </div> <!-- Warn if user is read-only --> - <div class="section" ng-show="isReadOnly()"> + <div class="section" ng-hide="managementPermissions.canSaveObject"> <p class="notice read-only">{{'MANAGE_USER.INFO_READ_ONLY' | translate}}</p> </div> <!-- Sections applicable to non-read-only users --> - <div ng-show="!isReadOnly()"> + <div ng-show="managementPermissions.canSaveObject"> <!-- User password section --> <div class="section"> @@ -40,13 +40,13 @@ </div> <!-- User attributes section --> - <div class="attributes" ng-show="canChangeAttributes()"> + <div class="attributes" ng-show="managementPermissions.canChangeAttributes"> <guac-form namespace="'USER_ATTRIBUTES'" content="attributes" - model="user.attributes" model-only="!canChangeAllAttributes()"></guac-form> + model="user.attributes" model-only="!managementPermissions.canChangeAllAttributes"></guac-form> </div> <!-- System permissions section --> - <system-permission-editor ng-show="canChangePermissions()" + <system-permission-editor ng-show="managementPermissions.canChangePermissions" username="selfUsername" data-data-source="dataSource" permission-flags="permissionFlags" @@ -55,7 +55,7 @@ </system-permission-editor> <!-- Connection permissions section --> - <connection-permission-editor ng-show="canChangePermissions()" + <connection-permission-editor ng-show="managementPermissions.canChangePermissions" data-data-source="dataSource" permission-flags="permissionFlags" permissions-added="permissionsAdded" @@ -64,10 +64,10 @@ <!-- Form action buttons --> <div class="action-buttons"> - <button ng-show="canSaveUser()" ng-click="saveUser()">{{'MANAGE_USER.ACTION_SAVE' | translate}}</button> - <button ng-show="canCloneUser()" ng-click="cloneUser()">{{'MANAGE_USER.ACTION_CLONE' | translate}}</button> + <button ng-show="managementPermissions.canSaveObject" ng-click="saveUser()">{{'MANAGE_USER.ACTION_SAVE' | translate}}</button> + <button ng-show="managementPermissions.canCloneObject" ng-click="cloneUser()">{{'MANAGE_USER.ACTION_CLONE' | translate}}</button> <button ng-click="cancel()">{{'MANAGE_USER.ACTION_CANCEL' | translate}}</button> - <button ng-show="canDeleteUser()" ng-click="deleteUser()" class="danger">{{'MANAGE_USER.ACTION_DELETE' | translate}}</button> + <button ng-show="managementPermissions.canDeleteObject" ng-click="deleteUser()" class="danger">{{'MANAGE_USER.ACTION_DELETE' | translate}}</button> </div> </div>