GUAC-801 Created password update dialog on home screen, grant self READ and UPDATE permission to users upon creation, and added sql update script to grant self READ and UPDATE permissions for users in pre-existing databases.

This commit is contained in:
James Muehlner
2015-03-04 23:18:16 -08:00
parent e35a26ce6a
commit f513fa6e2e
9 changed files with 318 additions and 39 deletions

View File

@@ -34,6 +34,7 @@ angular.module('home').controller('homeController', ['$scope', '$injector',
var authenticationService = $injector.get("authenticationService");
var connectionGroupService = $injector.get("connectionGroupService");
var permissionService = $injector.get("permissionService");
var userService = $injector.get("userService");
/**
* The root connection group, or null if the connection group hierarchy has
@@ -52,6 +53,37 @@ angular.module('home').controller('homeController', ['$scope', '$injector',
*/
$scope.canManageGuacamole = null;
/**
* Whether the current user has sufficient permissions to change
* his/her own password. If permissions have not yet been loaded, this will
* be null.
*
* @type Boolean
*/
$scope.canChangePassword = null;
/**
* Whether the password edit dialog should be shown.
*
* @type Boolean
*/
$scope.showPasswordDialog = false;
/**
* The new password for the user.
*
* @type String
*/
$scope.password = null;
/**
* The password match for the user. The update password action will fail if
* $scope.password !== $scope.passwordMatch.
*
* @type String
*/
$scope.passwordMatch = null;
/**
* Returns whether critical data has completed being loaded.
*
@@ -62,7 +94,8 @@ angular.module('home').controller('homeController', ['$scope', '$injector',
$scope.isLoaded = function isLoaded() {
return $scope.rootConnectionGroup !== null
&& $scope.canManageGuacamole !== null;
&& $scope.canManageGuacamole !== null
&& $scope.canChangePassword !== null;
};
@@ -71,13 +104,24 @@ angular.module('home').controller('homeController', ['$scope', '$injector',
.success(function rootGroupRetrieved(rootConnectionGroup) {
$scope.rootConnectionGroup = rootConnectionGroup;
});
// Identifier of the current user
var currentUserID = authenticationService.getCurrentUserID();
// Retrieve current permissions
permissionService.getPermissions(authenticationService.getCurrentUserID())
permissionService.getPermissions(currentUserID)
.success(function permissionsRetrieved(permissions) {
// Determine whether the current user can change his/her own password
$scope.canChangePassword =
PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, currentUserID)
&& PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.READ, currentUserID);
// Ignore permission to update root group
PermissionSet.removeConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, ConnectionGroup.ROOT_IDENTIFIER);
// Ignore permission to update self
PermissionSet.removeUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, currentUserID);
// Determine whether the current user needs access to the management UI
$scope.canManageGuacamole =
@@ -105,4 +149,68 @@ angular.module('home').controller('homeController', ['$scope', '$injector',
});
/**
* An action to be provided along with the object sent to showStatus which
* closes the currently-shown status dialog.
*/
var ACKNOWLEDGE_ACTION = {
name : "MANAGE_USER.ACTION_ACKNOWLEDGE",
// Handle action
callback : function acknowledgeCallback() {
$scope.showStatus(false);
}
};
/**
* Show the password update dialog.
*/
$scope.showPasswordUpdate = function showPasswordUpdate() {
// Show the dialog
$scope.showPasswordDialog = true;
};
/**
* Close the password update dialog.
*/
$scope.closePasswordUpdate = function closePasswordUpdate() {
// Clear the password fields and close the dialog
$scope.password = null;
$scope.passwordMatch = null;
$scope.showPasswordDialog = false;
};
/**
* Update the current user's password to the password currently set within
* the password change dialog.
*/
$scope.updatePassword = function updatePassword() {
// Verify passwords match
if ($scope.passwordMatch !== $scope.password) {
$scope.showStatus({
'className' : 'error',
'title' : 'HOME.DIALOG_HEADER_ERROR',
'text' : 'HOME.ERROR_PASSWORD_MISMATCH',
'actions' : [ ACKNOWLEDGE_ACTION ]
});
return;
}
// Save the user with the new password
userService.saveUser({
username: currentUserID,
password: $scope.password
});
$scope.closePasswordUpdate();
// Indicate that the password has been changed
$scope.showStatus({
'text' : 'HOME.PASSWORD_CHANGED',
'actions' : [ ACKNOWLEDGE_ACTION ]
});
};
}]);

View File

@@ -69,3 +69,13 @@ div.recent-connections div.connection {
max-width: 75%;
overflow: hidden;
}
.password-dialog {
position: absolute;
background: white;
border: 1px solid rgba(0, 0, 0, 0.25);
margin: 1em;
width: 5in;
right: 0;
z-index: 1;
}

View File

@@ -25,6 +25,31 @@
<div class="connection-list-ui">
<div class="logout-panel">
<a class="change-password button" ng-click="showPasswordUpdate()" ng-show="canChangePassword">{{'HOME.ACTION_CHANGE_PASSWORD' | translate}}</a>
<div class="password-dialog" ng-show="showPasswordDialog">
<!-- Password editor -->
<div class="section">
<table class="properties">
<tr>
<th>{{'HOME.FIELD_HEADER_PASSWORD' | translate}}</th>
<td><input ng-model="password" type="password" /></td>
</tr>
<tr>
<th>{{'HOME.FIELD_HEADER_PASSWORD_AGAIN' | translate}}</th>
<td><input ng-model="passwordMatch" type="password" /></td>
</tr>
</table>
</div>
<!-- Form action buttons -->
<div class="action-buttons">
<button ng-click="updatePassword()">{{'HOME.ACTION_SAVE' | translate}}</button>
<button ng-click="closePasswordUpdate()">{{'HOME.ACTION_CANCEL' | translate}}</button>
</div>
</div>
<a class="manage button" ng-show="canManageGuacamole" href="#/manage">{{'HOME.ACTION_MANAGE' | translate}}</a>
<a class="logout button" ng-click="logout()">{{'HOME.ACTION_LOGOUT' | translate}}</a>
</div>

View File

@@ -73,7 +73,7 @@ button.danger:active, a.button.danger:active {
background: #932;
}
.button.logout, .button.manage, .button.back, .button.home {
.button.logout, .button.manage, .button.back, .button.home, .button.change-password {
background-repeat: no-repeat;
background-size: 1em;
background-position: 0.5em 0.45em;
@@ -95,3 +95,7 @@ button.danger:active, a.button.danger:active {
.button.home {
background-image: url('images/action-icons/guac-home.png');
}
.button.change-password {
background-image: url('images/action-icons/guac-key.png');
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 B

View File

@@ -2,16 +2,24 @@
"APP" : {
"ACTION_ACKNOWLEDGE" : "OK",
"ACTION_CANCEL" : "Cancel",
"ACTION_CLONE" : "Clone",
"ACTION_DELETE" : "Delete",
"ACTION_LOGIN" : "Login",
"ACTION_LOGOUT" : "Logout",
"ACTION_MANAGE" : "Manage",
"ACTION_NAVIGATE_BACK" : "Back",
"ACTION_NAVIGATE_HOME" : "Home",
"ACTION_SAVE" : "Save",
"ACTION_ACKNOWLEDGE" : "OK",
"ACTION_CANCEL" : "Cancel",
"ACTION_CHANGE_PASSWORD" : "Change Password",
"ACTION_CLONE" : "Clone",
"ACTION_DELETE" : "Delete",
"ACTION_LOGIN" : "Login",
"ACTION_LOGOUT" : "Logout",
"ACTION_MANAGE" : "Manage",
"ACTION_NAVIGATE_BACK" : "Back",
"ACTION_NAVIGATE_HOME" : "Home",
"ACTION_SAVE" : "Save",
"DIALOG_HEADER_ERROR" : "Error",
"ERROR_PASSWORD_MISMATCH" : "The provided passwords do not match.",
"FIELD_HEADER_PASSWORD" : "Password:",
"FIELD_HEADER_PASSWORD_AGAIN" : "Re-enter Password:",
"INFO_ACTIVE_USER_COUNT" : "Currently in use by {USERS} {USERS, plural, one{user} other{users}}.",
@@ -106,12 +114,25 @@
"HOME" : {
"ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT",
"ACTION_MANAGE" : "@:APP.ACTION_MANAGE",
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
"ACTION_CANCEL" : "@:APP.ACTION_CANCEL",
"ACTION_CHANGE_PASSWORD" : "@:APP.ACTION_CHANGE_PASSWORD",
"ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT",
"ACTION_MANAGE" : "@:APP.ACTION_MANAGE",
"ACTION_SAVE" : "@:APP.ACTION_SAVE",
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
"ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
"FIELD_HEADER_PASSWORD" : "@:APP.FIELD_HEADER_PASSWORD",
"FIELD_HEADER_PASSWORD_AGAIN" : "@:APP.FIELD_HEADER_PASSWORD_AGAIN",
"INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT",
"INFO_NO_RECENT_CONNECTIONS" : "No recent connections.",
"PASSWORD_CHANGED" : "Password changed.",
"SECTION_HEADER_ALL_CONNECTIONS" : "All Connections",
"SECTION_HEADER_RECENT_CONNECTIONS" : "Recent Connections"
@@ -224,14 +245,14 @@
"DIALOG_HEADER_CONFIRM_DELETE" : "Delete User",
"DIALOG_HEADER_ERROR" : "Error",
"ERROR_PASSWORD_MISMATCH" : "The provided passwords do not match.",
"ERROR_PASSWORD_MISMATCH" : "@:APP.ERROR_PASSWORD_MISMATCH",
"FIELD_HEADER_ADMINISTER_SYSTEM" : "Administer system:",
"FIELD_HEADER_CREATE_NEW_USERS" : "Create new users:",
"FIELD_HEADER_CREATE_NEW_CONNECTIONS" : "Create new connections:",
"FIELD_HEADER_CREATE_NEW_CONNECTION_GROUPS" : "Create new connection groups:",
"FIELD_HEADER_PASSWORD" : "Password:",
"FIELD_HEADER_PASSWORD_AGAIN" : "Re-enter Password:",
"FIELD_HEADER_PASSWORD" : "@:APP.FIELD_HEADER_PASSWORD",
"FIELD_HEADER_PASSWORD_AGAIN" : "@:APP.FIELD_HEADER_PASSWORD_AGAIN",
"FIELD_HEADER_USERNAME" : "Username:",
"SECTION_HEADER_CONNECTIONS" : "Connections",