From 5a4c6db6d198e7959e931a42d0490269c0f5b366 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 28 Dec 2015 21:33:40 -0800 Subject: [PATCH 1/3] GUAC-1407: Add support for user cloning. --- .../controllers/manageUserController.js | 77 +++++++++++++++++++ .../app/manage/templates/manageUser.html | 1 + .../src/main/webapp/translations/de.json | 1 + .../src/main/webapp/translations/en.json | 1 + .../src/main/webapp/translations/fr.json | 1 + .../src/main/webapp/translations/it.json | 1 + .../src/main/webapp/translations/nl.json | 1 + .../src/main/webapp/translations/ru.json | 1 + 8 files changed, 84 insertions(+) diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js index 1641091fa..f6684a7d2 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js @@ -80,6 +80,14 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto */ var selectedDataSource = $routeParams.dataSource; + /** + * The username of the original user from which this user is + * being cloned. Only valid if this is a new user. + * + * @type String + */ + var cloneSourceUsername = $location.search().clone; + /** * The username of the user being edited. If a new user is * being created, this will not be defined. @@ -362,6 +370,43 @@ 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. + * + * @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 save changes to 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 || selectedDataSource; + + // If we are not editing an existing user, we cannot clone + if (!username) + return false; + + // The administrator can always clone users + if (PermissionSet.hasSystemPermission($scope.permissions[dataSource], + PermissionSet.SystemPermissionType.ADMINISTER)) + return true; + + // Otherwise we need explicit CREATE_USER permission + return PermissionSet.hasSystemPermission($scope.permissions[dataSource], + PermissionSet.SystemPermissionType.CREATE_USER); + + }; + /** * Returns whether the current user can delete the user being edited from * the given data source. @@ -488,6 +533,30 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto }); } + // 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[selectedDataSource]; + + }); + + // Pull user permissions + permissionService.getPermissions(selectedDataSource, cloneSourceUsername).success(function gotPermissions(permissions) { + $scope.permissionFlags = PermissionFlagSet.fromPermissionSet(permissions); + permissionsAdded = permissions; + }) + + // If permissions cannot be retrieved, use empty permissions + .error(function permissionRetrievalFailed() { + $scope.permissionFlags = new PermissionFlagSet(); + }); + } + // Use skeleton data if we are creating a new user else { @@ -838,6 +907,14 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto $scope.cancel = function cancel() { $location.path('/settings/users'); }; + + /** + * Cancels all pending edits, opening an edit page for a new user + * which is prepopulated with the data from the user currently being edited. + */ + $scope.cloneUser = function cloneUser() { + $location.path('/manage/' + encodeURIComponent(selectedDataSource) + '/users').search('clone', username); + }; /** * Saves the user, updating the existing user only. diff --git a/guacamole/src/main/webapp/app/manage/templates/manageUser.html b/guacamole/src/main/webapp/app/manage/templates/manageUser.html index 6d275e5c1..18e0915ac 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageUser.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageUser.html @@ -108,6 +108,7 @@ THE SOFTWARE.
+
diff --git a/guacamole/src/main/webapp/translations/de.json b/guacamole/src/main/webapp/translations/de.json index 996f6adbb..058193835 100644 --- a/guacamole/src/main/webapp/translations/de.json +++ b/guacamole/src/main/webapp/translations/de.json @@ -237,6 +237,7 @@ "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CLONE" : "@:APP.ACTION_CLONE", "ACTION_DELETE" : "@:APP.ACTION_DELETE", "ACTION_SAVE" : "@:APP.ACTION_SAVE", diff --git a/guacamole/src/main/webapp/translations/en.json b/guacamole/src/main/webapp/translations/en.json index 951b38b03..c311f7eb2 100644 --- a/guacamole/src/main/webapp/translations/en.json +++ b/guacamole/src/main/webapp/translations/en.json @@ -237,6 +237,7 @@ "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CLONE" : "@:APP.ACTION_CLONE", "ACTION_DELETE" : "@:APP.ACTION_DELETE", "ACTION_SAVE" : "@:APP.ACTION_SAVE", diff --git a/guacamole/src/main/webapp/translations/fr.json b/guacamole/src/main/webapp/translations/fr.json index c8fa6be22..f391bfe5e 100644 --- a/guacamole/src/main/webapp/translations/fr.json +++ b/guacamole/src/main/webapp/translations/fr.json @@ -228,6 +228,7 @@ "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CLONE" : "@:APP.ACTION_CLONE", "ACTION_DELETE" : "@:APP.ACTION_DELETE", "ACTION_SAVE" : "@:APP.ACTION_SAVE", diff --git a/guacamole/src/main/webapp/translations/it.json b/guacamole/src/main/webapp/translations/it.json index 0dea25c3f..bed8c4c8c 100644 --- a/guacamole/src/main/webapp/translations/it.json +++ b/guacamole/src/main/webapp/translations/it.json @@ -225,6 +225,7 @@ "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CLONE" : "@:APP.ACTION_CLONE", "ACTION_DELETE" : "@:APP.ACTION_DELETE", "ACTION_SAVE" : "@:APP.ACTION_SAVE", diff --git a/guacamole/src/main/webapp/translations/nl.json b/guacamole/src/main/webapp/translations/nl.json index 4dbdd8c47..e4c9a8111 100644 --- a/guacamole/src/main/webapp/translations/nl.json +++ b/guacamole/src/main/webapp/translations/nl.json @@ -237,6 +237,7 @@ "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CLONE" : "@:APP.ACTION_CLONE", "ACTION_DELETE" : "@:APP.ACTION_DELETE", "ACTION_SAVE" : "@:APP.ACTION_SAVE", diff --git a/guacamole/src/main/webapp/translations/ru.json b/guacamole/src/main/webapp/translations/ru.json index 6ac7fbe0c..9fdd48fb9 100644 --- a/guacamole/src/main/webapp/translations/ru.json +++ b/guacamole/src/main/webapp/translations/ru.json @@ -225,6 +225,7 @@ "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CLONE" : "@:APP.ACTION_CLONE", "ACTION_DELETE" : "@:APP.ACTION_DELETE", "ACTION_SAVE" : "@:APP.ACTION_SAVE", From 18751c0e03db820f8f70c152dfff9a3d8a90f185 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Mon, 28 Dec 2015 23:01:33 -0800 Subject: [PATCH 2/3] GUAC-1407: Ensure that permissions referring to the cloned user correctly carry over. --- .../controllers/manageConnectionController.js | 4 +- .../controllers/manageUserController.js | 44 +++++++++++++++++-- .../app/manage/templates/manageUser.html | 4 +- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js index 1104741ab..b58481847 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js @@ -350,7 +350,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i * Cancels all pending edits, returning to the management page. */ $scope.cancel = function cancel() { - $location.path('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections'); + $location.url('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections'); }; /** @@ -372,7 +372,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i // Save the connection connectionService.saveConnection($scope.selectedDataSource, $scope.connection) .success(function savedConnection() { - $location.path('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections'); + $location.url('/settings/' + encodeURIComponent($scope.selectedDataSource) + '/connections'); }) // Notify of any errors diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js index f6684a7d2..327f56aee 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js @@ -96,6 +96,18 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto */ var username = $routeParams.id; + /** + * The string value representing the user currently being edited within the + * permission flag set. Note that his may not match the user's actual + * username - it is a marker that is (1) guaranteed to be associated with + * the current user's permissions in the permission set and (2) guaranteed + * not to collide with any user that does not represent the current user + * within the permission set. + * + * @type String + */ + var selfUsername = ''; + /** * All user accounts associated with the same username as the account being * created or edited, as a map of data source identifier to the User object @@ -522,6 +534,10 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto }); + // 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(selectedDataSource, username).success(function gotPermissions(permissions) { $scope.permissionFlags = PermissionFlagSet.fromPermissionSet(permissions); @@ -545,8 +561,13 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto }); + // The current user will be associated with cloneSourceUsername in the + // retrieved permission set + $scope.selfUsername = cloneSourceUsername; + // Pull user permissions - permissionService.getPermissions(selectedDataSource, cloneSourceUsername).success(function gotPermissions(permissions) { + permissionService.getPermissions(selectedDataSource, cloneSourceUsername) + .success(function gotPermissions(permissions) { $scope.permissionFlags = PermissionFlagSet.fromPermissionSet(permissions); permissionsAdded = permissions; }) @@ -905,7 +926,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto * Cancels all pending edits, returning to the management page. */ $scope.cancel = function cancel() { - $location.path('/settings/users'); + $location.url('/settings/users'); }; /** @@ -941,10 +962,27 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto saveUserPromise.success(function savedUser() { + // Move permission flags if username differs from marker + if ($scope.selfUsername !== $scope.user.username) { + + // Rename added permission + if (permissionsAdded.userPermissions[$scope.selfUsername]) { + permissionsAdded.userPermissions[$scope.user.username] = permissionsAdded.userPermissions[$scope.selfUsername]; + delete permissionsAdded.userPermissions[$scope.selfUsername]; + } + + // Rename removed permission + if (permissionsRemoved.userPermissions[$scope.selfUsername]) { + permissionsRemoved.userPermissions[$scope.user.username] = permissionsRemoved.userPermissions[$scope.selfUsername]; + delete permissionsRemoved.userPermissions[$scope.selfUsername]; + } + + } + // Upon success, save any changed permissions permissionService.patchPermissions(selectedDataSource, $scope.user.username, permissionsAdded, permissionsRemoved) .success(function patchedUserPermissions() { - $location.path('/settings/users'); + $location.url('/settings/users'); }) // Notify of any errors diff --git a/guacamole/src/main/webapp/app/manage/templates/manageUser.html b/guacamole/src/main/webapp/app/manage/templates/manageUser.html index 18e0915ac..858665463 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manageUser.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageUser.html @@ -78,8 +78,8 @@ THE SOFTWARE. {{'MANAGE_USER.FIELD_HEADER_CHANGE_OWN_PASSWORD' | translate}} - + From 2bee2eab494ab02d1fd2c4a3b820b8fd1a01aa40 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Tue, 29 Dec 2015 10:45:24 -0800 Subject: [PATCH 3/3] GUAC-1407: Fixed canClone comment and declaration of selfUsername variable, --- .../app/manage/controllers/manageUserController.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js index 327f56aee..125d41964 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js @@ -106,7 +106,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto * * @type String */ - var selfUsername = ''; + $scope.selfUsername = ''; /** * All user accounts associated with the same username as the account being @@ -383,17 +383,16 @@ 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. + * Returns whether the current user can clone 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 save changes to the user being edited, - * false otherwise. + * true if the current user can clone the user being edited, false + * otherwise. */ $scope.canCloneUser = function canCloneUser(dataSource) {