From 06b68a68343a76e4e0ba3e5355cc548f8aa2b4d7 Mon Sep 17 00:00:00 2001 From: James Muehlner Date: Tue, 17 Mar 2015 23:19:21 -0700 Subject: [PATCH] GUAC-1133 Split out session, user, and connection manage pages, rudimentary session management interface. --- .../app/index/config/indexRouteConfig.js | 26 ++- ...ller.js => manageConnectionsController.js} | 105 +-------- .../controllers/manageSessionsController.js | 220 ++++++++++++++++++ .../controllers/manageUsersController.js | 179 ++++++++++++++ .../manage/templates/manageConnections.html | 60 +++++ .../app/manage/templates/manageSessions.html | 80 +++++++ .../{manage.html => manageUsers.html} | 41 +--- .../ActiveTunnelWrapper.js} | 44 +++- .../app/userMenu/directives/guacUserMenu.js | 110 +++++++-- .../webapp/app/userMenu/styles/user-menu.css | 18 +- .../app/userMenu/templates/guacUserMenu.html | 20 +- .../src/main/webapp/translations/en_US.json | 109 +++++---- 12 files changed, 791 insertions(+), 221 deletions(-) rename guacamole/src/main/webapp/app/manage/controllers/{manageController.js => manageConnectionsController.js} (64%) create mode 100644 guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js create mode 100644 guacamole/src/main/webapp/app/manage/controllers/manageUsersController.js create mode 100644 guacamole/src/main/webapp/app/manage/templates/manageConnections.html create mode 100644 guacamole/src/main/webapp/app/manage/templates/manageSessions.html rename guacamole/src/main/webapp/app/manage/templates/{manage.html => manageUsers.html} (58%) rename guacamole/src/main/webapp/app/manage/{styles/lists.css => types/ActiveTunnelWrapper.js} (58%) diff --git a/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js b/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js index 8d6c77286..456997774 100644 --- a/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js +++ b/guacamole/src/main/webapp/app/index/config/indexRouteConfig.js @@ -74,12 +74,30 @@ angular.module('index').config(['$routeProvider', '$locationProvider', resolve : { updateCurrentToken: updateCurrentToken } }) - // Management screen - .when('/manage/', { + // Connection management screen + .when('/manage/modules/connections/', { title : 'APP.NAME', bodyClassName : 'manage', - templateUrl : 'app/manage/templates/manage.html', - controller : 'manageController', + templateUrl : 'app/manage/templates/manageConnections.html', + controller : 'manageConnectionsController', + resolve : { updateCurrentToken: updateCurrentToken } + }) + + // User management screen + .when('/manage/modules/users/', { + title : 'APP.NAME', + bodyClassName : 'manage', + templateUrl : 'app/manage/templates/manageUsers.html', + controller : 'manageUsersController', + resolve : { updateCurrentToken: updateCurrentToken } + }) + + // Session management screen + .when('/manage/modules/sessions/', { + title : 'APP.NAME', + bodyClassName : 'manage', + templateUrl : 'app/manage/templates/manageSessions.html', + controller : 'manageSessionsController', resolve : { updateCurrentToken: updateCurrentToken } }) diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionsController.js similarity index 64% rename from guacamole/src/main/webapp/app/manage/controllers/manageController.js rename to guacamole/src/main/webapp/app/manage/controllers/manageConnectionsController.js index 19704a552..76978ec10 100644 --- a/guacamole/src/main/webapp/app/manage/controllers/manageController.js +++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionsController.js @@ -21,15 +21,14 @@ */ /** - * The controller for the administration page. + * The controller for the connection and connection group administration page. */ -angular.module('manage').controller('manageController', ['$scope', '$injector', - function manageController($scope, $injector) { +angular.module('manage').controller('manageConnectionsController', ['$scope', '$injector', + function manageConnectionsController($scope, $injector) { // Required types var ConnectionGroup = $injector.get('ConnectionGroup'); var PermissionSet = $injector.get('PermissionSet'); - var User = $injector.get('User'); // Required services var $location = $injector.get('$location'); @@ -37,7 +36,6 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', var connectionGroupService = $injector.get('connectionGroupService'); var guacNotification = $injector.get('guacNotification'); var permissionService = $injector.get('permissionService'); - var userService = $injector.get('userService'); // Identifier of the current user var currentUserID = authenticationService.getCurrentUserID(); @@ -47,20 +45,13 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', * closes the currently-shown status dialog. */ var ACKNOWLEDGE_ACTION = { - name : "MANAGE.ACTION_ACKNOWLEDGE", + name : "MANAGE_CONNECTION.ACTION_ACKNOWLEDGE", // Handle action callback : function acknowledgeCallback() { guacNotification.showStatus(false); } }; - /** - * All visible users. - * - * @type User[] - */ - $scope.users = null; - /** * The root connection group of the connection group hierarchy. * @@ -68,14 +59,6 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', */ $scope.rootGroup = null; - /** - * Whether the current user can manage users. If the current permissions - * have not yet been loaded, this will be null. - * - * @type Boolean - */ - $scope.canManageUsers = null; - /** * Whether the current user can manage connections. If the current * permissions have not yet been loaded, this will be null. @@ -84,14 +67,6 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', */ $scope.canManageConnections = null; - /** - * Whether the current user can create new users. If the current - * permissions have not yet been loaded, this will be null. - * - * @type Boolean - */ - $scope.canCreateUsers = null; - /** * Whether the current user can create new connections. If the current * permissions have not yet been loaded, this will be null. @@ -108,14 +83,6 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', */ $scope.canCreateConnectionGroups = null; - /** - * The name of the new user to create, if any, when user creation is - * requested via newUser(). - * - * @type String - */ - $scope.newUsername = ""; - /** * All permissions associated with the current user, or null if the user's * permissions have not yet been loaded. @@ -133,12 +100,9 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', */ $scope.isLoaded = function isLoaded() { - return $scope.users !== null - && $scope.rootGroup !== null + return $scope.rootGroup !== null && $scope.permissions !== null - && $scope.canManageUsers !== null && $scope.canManageConnections !== null - && $scope.canCreateUsers !== null && $scope.canCreateConnections !== null && $scope.canCreateConnectionGroups !== null; @@ -153,11 +117,6 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', // Ignore permission to update root group PermissionSet.removeConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, ConnectionGroup.ROOT_IDENTIFIER); - // Determine whether the current user can create new users - $scope.canCreateUsers = - PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) - || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_USER); - // Determine whether the current user can create new users $scope.canCreateConnections = PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) @@ -168,12 +127,6 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION_GROUP); - // Determine whether the current user can manage other users - $scope.canManageUsers = - $scope.canCreateUsers - || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) - || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.DELETE); - // Determine whether the current user can manage other connections or groups $scope.canManageConnections = @@ -188,7 +141,7 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.DELETE); // Return to home if there's nothing to do here - if (!$scope.canManageUsers && !$scope.canManageConnections) + if (!$scope.canManageConnections) $location.path('/'); }); @@ -199,51 +152,5 @@ angular.module('manage').controller('manageController', ['$scope', '$injector', .success(function connectionGroupReceived(rootGroup) { $scope.rootGroup = rootGroup; }); - - // Retrieve all users for whom we have UPDATE or DELETE permission - userService.getUsers([PermissionSet.ObjectPermissionType.UPDATE, - PermissionSet.ObjectPermissionType.DELETE]) - .success(function usersReceived(users) { - - // Display only other users, not self - $scope.users = users.filter(function isNotSelf(user) { - return user.username !== currentUserID; - }); - - }); - - /** - * Creates a new user having the username specified in the user creation - * interface. - */ - $scope.newUser = function newUser() { - - // Create user skeleton - var user = new User({ - username: $scope.newUsername || '' - }); - - // Create specified user - userService.createUser(user) - - // Add user to visible list upon success - .success(function userCreated() { - $scope.users.push(user); - }) - - // Notify of any errors - .error(function userCreationFailed(error) { - guacNotification.showStatus({ - 'className' : 'error', - 'title' : 'MANAGE.DIALOG_HEADER_ERROR', - 'text' : error.message, - 'actions' : [ ACKNOWLEDGE_ACTION ] - }); - }); - - // Reset username - $scope.newUsername = ""; - - }; }]); diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js b/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js new file mode 100644 index 000000000..8aa49022f --- /dev/null +++ b/guacamole/src/main/webapp/app/manage/controllers/manageSessionsController.js @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * The controller for the user session administration page. + */ +angular.module('manage').controller('manageSessionsController', ['$scope', '$injector', + function manageSessionsController($scope, $injector) { + + // Required types + var ActiveTunnelWrapper = $injector.get('ActiveTunnelWrapper'); + var ConnectionGroup = $injector.get('ConnectionGroup'); + + // Required services + var authenticationService = $injector.get('authenticationService'); + var connectionGroupService = $injector.get('connectionGroupService'); + var guacNotification = $injector.get('guacNotification'); + var permissionService = $injector.get('permissionService'); + var tunnelService = $injector.get('tunnelService'); + + /** + * The root connection group of the connection group hierarchy. + * + * @type ConnectionGroup + */ + $scope.rootGroup = null; + + /** + * All permissions associated with the current user, or null if the user's + * permissions have not yet been loaded. + * + * @type PermissionSet + */ + $scope.permissions = null; + + /** + * The ActiveTunnelWrappers of all active sessions accessible by the current + * user, or null if the tunnels have not yet been loaded. + * + * @type ActiveTunnelWrapper[] + */ + $scope.wrappers = null; + + // Query the user's permissions + permissionService.getPermissions(authenticationService.getCurrentUserID()) + .success(function permissionsReceived(permissions) { + $scope.permissions = permissions; + }); + + /** + * Map of all visible connections by object identifier. + * + * @type Object. + */ + $scope.connections = {}; + + /** + * The count of currently selected tunnel wrappers. + * + * @type Number + */ + var selectedWrapperCount = 0; + + /** + * Adds the given connection to the internal set of visible + * connections. + * + * @param {Connection} connection + * The connection to add to the internal set of visible connections. + */ + var addConnection = function addConnection(connection) { + + // Add given connection to set of visible connections + $scope.connections[connection.identifier] = connection; + + }; + + /** + * Adds all descendant connections of the given connection group to the + * internal set of connections. + * + * @param {ConnectionGroup} connectionGroup + * The connection group whose descendant connections should be added to + * the internal set of connections. + */ + var addDescendantConnections = function addDescendantConnections(connectionGroup) { + + // Add all child connections + if (connectionGroup.childConnections) + connectionGroup.childConnections.forEach(addConnection); + + // Add all child connection groups + if (connectionGroup.childConnectionGroups) + connectionGroup.childConnectionGroups.forEach(addDescendantConnections); + + }; + + // Retrieve all connections + connectionGroupService.getConnectionGroupTree(ConnectionGroup.ROOT_IDENTIFIER) + .success(function connectionGroupReceived(rootGroup) { + $scope.rootGroup = rootGroup; + addDescendantConnections($scope.rootGroup); + }); + + // Query active sessions + tunnelService.getActiveTunnels().success(function sessionsRetrieved(tunnels) { + + // Wrap all active tunnels for sake of display + $scope.wrappers = []; + tunnels.forEach(function wrapActiveTunnel(tunnel) { + $scope.wrappers.push(new ActiveTunnelWrapper(tunnel)); + }); + + }); + + /** + * Returns whether critical data has completed being loaded. + * + * @returns {Boolean} + * true if enough data has been loaded for the user interface to be + * useful, false otherwise. + */ + $scope.isLoaded = function isLoaded() { + + return $scope.wrappers !== null + && $scope.permissions !== null + && $scope.rootGroup !== null; + + }; + + /** + * An action to be provided along with the object sent to showStatus which + * closes the currently-shown status dialog. + */ + var CANCEL_ACTION = { + name : "MANAGE_USER.ACTION_CANCEL", + // Handle action + callback : function cancelCallback() { + guacNotification.showStatus(false); + } + }; + + /** + * An action to be provided along with the object sent to showStatus which + * immediately deletes the currently selected sessions. + */ + var DELETE_ACTION = { + name : "MANAGE_SESSION.ACTION_DELETE", + className : "danger", + // Handle action + callback : function deleteCallback() { + deleteSessionsImmediately(); + guacNotification.showStatus(false); + } + }; + + /** + * Immediately deletes the selected sessions, without prompting the user for + * confirmation. + */ + var deleteSessionsImmediately = function deleteSessionsImmediately() { + // TODO: Use a batch delete function to delete the sessions. + }; + + /** + * Delete all selected sessions, prompting the user first to confirm that + * deletion is desired. + */ + $scope.deleteSessions = function deleteSessions() { + // Confirm deletion request + guacNotification.showStatus({ + 'title' : 'MANAGE_SESSION.DIALOG_HEADER_CONFIRM_DELETE', + 'text' : 'MANAGE_SESSION.TEXT_CONFIRM_DELETE', + 'actions' : [ DELETE_ACTION, CANCEL_ACTION] + }); + }; + + /** + * Returns whether the selected sessions can be deleted. + * + * @returns {Boolean} + * true if selected sessions can be deleted, false otherwise. + */ + $scope.canDeleteSessions = function canDeleteSessions() { + return selectedWrapperCount > 0; + }; + + /** + * Called whenever a tunnel wrapper changes selected status. + * + * @param {Boolean} selected + * Whether the wrapper is now selected. + */ + $scope.wrapperSelectionChange = function wrapperSelectionChange(selected) { + if (selected) + selectedWrapperCount++; + else + selectedWrapperCount--; + }; + +}]); diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUsersController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUsersController.js new file mode 100644 index 000000000..d50b5d5b5 --- /dev/null +++ b/guacamole/src/main/webapp/app/manage/controllers/manageUsersController.js @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2015 Glyptodon LLC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * The controller for the user administration page. + */ +angular.module('manage').controller('manageUsersController', ['$scope', '$injector', + function manageUsersController($scope, $injector) { + + // Required types + var PermissionSet = $injector.get('PermissionSet'); + var User = $injector.get('User'); + + // Required services + var $location = $injector.get('$location'); + var authenticationService = $injector.get('authenticationService'); + var guacNotification = $injector.get('guacNotification'); + var permissionService = $injector.get('permissionService'); + var userService = $injector.get('userService'); + + // Identifier of the current user + var currentUserID = authenticationService.getCurrentUserID(); + + /** + * 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() { + guacNotification.showStatus(false); + } + }; + + /** + * All visible users. + * + * @type User[] + */ + $scope.users = null; + + /** + * Whether the current user can manage users. If the current permissions + * have not yet been loaded, this will be null. + * + * @type Boolean + */ + $scope.canManageUsers = null; + + /** + * Whether the current user can create new users. If the current + * permissions have not yet been loaded, this will be null. + * + * @type Boolean + */ + $scope.canCreateUsers = null; + + /** + * The name of the new user to create, if any, when user creation is + * requested via newUser(). + * + * @type String + */ + $scope.newUsername = ""; + + /** + * All permissions associated with the current user, or null if the user's + * permissions have not yet been loaded. + * + * @type PermissionSet + */ + $scope.permissions = null; + + /** + * Returns whether critical data has completed being loaded. + * + * @returns {Boolean} + * true if enough data has been loaded for the user interface to be + * useful, false otherwise. + */ + $scope.isLoaded = function isLoaded() { + + return $scope.users !== null + && $scope.permissions !== null + && $scope.canManageUsers !== null + && $scope.canCreateUsers !== null; + + }; + + // Retrieve current permissions + permissionService.getPermissions(currentUserID) + .success(function permissionsRetrieved(permissions) { + + $scope.permissions = permissions; + + // Determine whether the current user can create new users + $scope.canCreateUsers = + PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) + || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_USER); + + // Determine whether the current user can manage other users + $scope.canManageUsers = + $scope.canCreateUsers + || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) + || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.DELETE); + + // Return to home if there's nothing to do here + if (!$scope.canManageUsers) + $location.path('/'); + + }); + + // Retrieve all users for whom we have UPDATE or DELETE permission + userService.getUsers([PermissionSet.ObjectPermissionType.UPDATE, + PermissionSet.ObjectPermissionType.DELETE]) + .success(function usersReceived(users) { + + // Display only other users, not self + $scope.users = users.filter(function isNotSelf(user) { + return user.username !== currentUserID; + }); + + }); + + /** + * Creates a new user having the username specified in the user creation + * interface. + */ + $scope.newUser = function newUser() { + + // Create user skeleton + var user = new User({ + username: $scope.newUsername || '' + }); + + // Create specified user + userService.createUser(user) + + // Add user to visible list upon success + .success(function userCreated() { + $scope.users.push(user); + }) + + // Notify of any errors + .error(function userCreationFailed(error) { + guacNotification.showStatus({ + 'className' : 'error', + 'title' : 'MANAGE_USER.DIALOG_HEADER_ERROR', + 'text' : error.message, + 'actions' : [ ACKNOWLEDGE_ACTION ] + }); + }); + + // Reset username + $scope.newUsername = ""; + + }; + +}]); diff --git a/guacamole/src/main/webapp/app/manage/templates/manageConnections.html b/guacamole/src/main/webapp/app/manage/templates/manageConnections.html new file mode 100644 index 000000000..3404fa47a --- /dev/null +++ b/guacamole/src/main/webapp/app/manage/templates/manageConnections.html @@ -0,0 +1,60 @@ + + +
+ +
+

{{'MANAGE_CONNECTION.SECTION_HEADER_CONNECTIONS' | translate}}

+ +
+ + +
+
+ +

{{'MANAGE_CONNECTION.HELP_CONNECTIONS' | translate}}

+ + + + + +
+ +
+
+
+ +
\ No newline at end of file diff --git a/guacamole/src/main/webapp/app/manage/templates/manageSessions.html b/guacamole/src/main/webapp/app/manage/templates/manageSessions.html new file mode 100644 index 000000000..7bb34cf5d --- /dev/null +++ b/guacamole/src/main/webapp/app/manage/templates/manageSessions.html @@ -0,0 +1,80 @@ + + +
+ +
+

{{'MANAGE_SESSION.SECTION_HEADER_SESSIONS' | translate}}

+ +
+ + +
+
+ +

{{'MANAGE_SESSION.HELP_SESSIONS' | translate}}

+ + + + + + + + + + + + + + + + + + + + + + +
+ {{'MANAGE_SESSION.TABLE_HEADER_SESSION_USERNAME' | translate}} + + {{'MANAGE_SESSION.TABLE_HEADER_SESSION_STARTDATE' | translate}} + + {{'MANAGE_SESSION.TABLE_HEADER_SESSION_REMOTEHOST' | translate}} + + {{'MANAGE_SESSION.TABLE_HEADER_SESSION_CONNECTION_NAME' | translate}} +
+ + + {{wrapper.tunnel.username}} + + {{wrapper.tunnel.startDate | date:'short'}} + + {{wrapper.tunnel.remoteHost}} + + {{connections[wrapper.tunnel.identifier].name}} +
+ +
+
+ +
\ No newline at end of file diff --git a/guacamole/src/main/webapp/app/manage/templates/manage.html b/guacamole/src/main/webapp/app/manage/templates/manageUsers.html similarity index 58% rename from guacamole/src/main/webapp/app/manage/templates/manage.html rename to guacamole/src/main/webapp/app/manage/templates/manageUsers.html index 638f5b9d4..a0e231191 100644 --- a/guacamole/src/main/webapp/app/manage/templates/manage.html +++ b/guacamole/src/main/webapp/app/manage/templates/manageUsers.html @@ -1,5 +1,5 @@
-

{{'MANAGE.SECTION_HEADER_USERS' | translate}}

-

{{'MANAGE.HELP_USERS' | translate}}

+

{{'MANAGE_USER.HELP_USERS' | translate}}

- +
@@ -53,39 +52,9 @@ THE SOFTWARE.
- +
- -
-

{{'MANAGE.SECTION_HEADER_CONNECTIONS' | translate}}

-
- -

{{'MANAGE.HELP_CONNECTIONS' | translate}}

- - - - - -
- -
-
-
- \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/manage/styles/lists.css b/guacamole/src/main/webapp/app/manage/types/ActiveTunnelWrapper.js similarity index 58% rename from guacamole/src/main/webapp/app/manage/styles/lists.css rename to guacamole/src/main/webapp/app/manage/types/ActiveTunnelWrapper.js index df30143db..88fe5e93b 100644 --- a/guacamole/src/main/webapp/app/manage/styles/lists.css +++ b/guacamole/src/main/webapp/app/manage/types/ActiveTunnelWrapper.js @@ -20,12 +20,38 @@ * THE SOFTWARE. */ -.manage .connection-list .group-list-page, -.manage .user-list { - border: 1px solid rgba(0, 0, 0, 0.25); - min-height: 15em; - -moz-border-radius: 0.2em; - -webkit-border-radius: 0.2em; - -khtml-border-radius: 0.2em; - border-radius: 0.2em; -} +/** + * A service for defining the ActiveTunnelWrapper class. + */ +angular.module('manage').factory('ActiveTunnelWrapper', [ + function defineActiveTunnelWrapper() { + + /** + * Wrapper for ActiveTunnel which adds display-specific + * properties, such as a checked option. + * + * @constructor + * @param {ActiveTunnel} activeTunnel + * The ActiveTunnel to wrap. + */ + var ActiveTunnelWrapper = function ActiveTunnelWrapper(activeTunnel) { + + /** + * The wrapped ActiveTunnel. + * + * @type ActiveTunnel + */ + this.tunnel = activeTunnel; + + /** + * A flag indicating that the tunnel has been selected. + * + * @type Boolean + */ + this.checked = false; + + }; + + return ActiveTunnelWrapper; + +}]); \ No newline at end of file diff --git a/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js b/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js index 590d709a3..6ae882977 100644 --- a/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js +++ b/guacamole/src/main/webapp/app/userMenu/directives/guacUserMenu.js @@ -89,23 +89,64 @@ angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { $scope.homeDisabled = ($location.path() === '/'); /** - * Whether the option to go to the management interface is - * disabled. Note that shis is different from canManageGuacamole, + * Whether the option to go to the user management interface is + * disabled. Note that shis is different from canManageUsers, * which deals with whether permission to manage is granted. A user * may have permission, yet see this option as currently disabled. * * @type Boolean */ - $scope.manageDisabled = ($location.path() === '/manage/'); + $scope.manageUsersDisabled = + ($location.path() === '/manage/modules/user'); /** - * Whether the current user has sufficient permissions to use the - * management interface. If permissions have not yet been loaded, - * this will be null. + * Whether the option to go to the connection management interface is + * disabled. Note that shis is different from canManageConnections, + * which deals with whether permission to manage is granted. A user + * may have permission, yet see this option as currently disabled. * * @type Boolean */ - $scope.canManageGuacamole = null; + $scope.manageConnectionsDisabled = + ($location.path() === '/manage/modules/connections'); + + /** + * Whether the option to go to the session management interface is + * disabled. Note that shis is different from canManageConnections, + * which deals with whether permission to manage is granted. A user + * may have permission, yet see this option as currently disabled. + * + * @type Boolean + */ + $scope.manageSessionsDisabled = + ($location.path() === '/manage/modules/sessions'); + + /** + * Whether the current user has sufficient permissions to use the + * user management interface. If permissions have not yet been + * loaded, this will be null. + * + * @type Boolean + */ + $scope.canManageUsers = null; + + /** + * Whether the current user has sufficient permissions to use the + * connection management interface. If permissions have not yet been + * loaded, this will be null. + * + * @type Boolean + */ + $scope.canManageConnections = null; + + /** + * Whether the current user has sufficient permissions to use the + * session management interface. If permissions have not yet been + * loaded, this will be null. + * + * @type Boolean + */ + $scope.canManageSessions = null; /** * Whether the current user has sufficient permissions to change @@ -173,29 +214,46 @@ angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { // Ignore permission to update self PermissionSet.removeUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE, $scope.username); - // Determine whether the current user needs access to the management UI - $scope.canManageGuacamole = + // Determine whether the current user needs access to the user management UI + $scope.canManageUsers = // System permissions PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_USER) + + // Permission to update users + || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) + + // Permission to delete users + || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.DELETE) + + // Permission to administer users + || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.ADMINISTER); + + // Determine whether the current user needs access to the connection management UI + $scope.canManageConnections = + + // System permissions + PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER) || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION) || PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_CONNECTION_GROUP) - // Permission to update objects + // Permission to update connections or connection groups || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) - || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE) - // Permission to delete objects + // Permission to delete connections or connection groups || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.DELETE) || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.DELETE) - || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.DELETE) - // Permission to administer objects + // Permission to administer connections or connection groups || PermissionSet.hasConnectionPermission(permissions, PermissionSet.ObjectPermissionType.ADMINISTER) - || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.ADMINISTER) - || PermissionSet.hasUserPermission(permissions, PermissionSet.ObjectPermissionType.ADMINISTER); + || PermissionSet.hasConnectionGroupPermission(permissions, PermissionSet.ObjectPermissionType.ADMINISTER); + + $scope.canManageSessions = + + // A user must be a system administrator to manage sessions + PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER); }); @@ -289,10 +347,24 @@ angular.module('userMenu').directive('guacUserMenu', [function guacUserMenu() { }; /** - * Navigates to the management interface. + * Navigates to the user management interface. */ - $scope.manage = function manage() { - $location.path('/manage/'); + $scope.manageUsers = function manageUsers() { + $location.path('/manage/modules/users'); + }; + + /** + * Navigates to the connection management interface. + */ + $scope.manageConnections = function manageConnections() { + $location.path('/manage/modules/connections'); + }; + + /** + * Navigates to the user session management interface. + */ + $scope.manageSessions = function manageSessions() { + $location.path('/manage/modules/sessions'); }; /** diff --git a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css index f46665c3a..deb81cc84 100644 --- a/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css +++ b/guacamole/src/main/webapp/app/userMenu/styles/user-menu.css @@ -97,7 +97,7 @@ cursor: default; margin: 0; - min-width: 1.75in; + min-width: 2in; font-size: 1.25em; font-weight: bold; @@ -180,7 +180,9 @@ } .user-menu .options li a.home, -.user-menu .options li a.manage, +.user-menu .options li a.manage-users, +.user-menu .options li a.manage-connections, +.user-menu .options li a.manage-sessions, .user-menu .options li a.change-password, .user-menu .options li a.logout { background-repeat: no-repeat; @@ -193,8 +195,16 @@ background-image: url('images/action-icons/guac-home-dark.png'); } -.user-menu .options li a.manage { - background-image: url('images/action-icons/guac-config-dark.png'); +.user-menu .options li a.manage-users { + background-image: url('images/user-icons/guac-user.png'); +} + +.user-menu .options li a.manage-connections { + background-image: url('images/protocol-icons/guac-monitor.png'); +} + +.user-menu .options li a.manage-sessions { + background-image: url('images/protocol-icons/guac-plug.png'); } .user-menu .options li a.change-password { diff --git a/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html index b49c956f9..4cb556468 100644 --- a/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html +++ b/guacamole/src/main/webapp/app/userMenu/templates/guacUserMenu.html @@ -35,10 +35,24 @@ - +
  • - - {{'USER_MENU.ACTION_MANAGE' | translate}} + + {{'USER_MENU.ACTION_MANAGE_USERS' | translate}} + +
  • + + +
  • + + {{'USER_MENU.ACTION_MANAGE_CONNECTIONS' | translate}} + +
  • + + +
  • + + {{'USER_MENU.ACTION_MANAGE_SESSIONS' | translate}}
  • diff --git a/guacamole/src/main/webapp/translations/en_US.json b/guacamole/src/main/webapp/translations/en_US.json index 7e31dc77a..e19e4dfe1 100644 --- a/guacamole/src/main/webapp/translations/en_US.json +++ b/guacamole/src/main/webapp/translations/en_US.json @@ -2,19 +2,22 @@ "APP" : { - "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", + "ACTION_ACKNOWLEDGE" : "OK", + "ACTION_CANCEL" : "Cancel", + "ACTION_CHANGE_PASSWORD" : "Change Password", + "ACTION_CLONE" : "Clone", + "ACTION_DELETE" : "Delete", + "ACTION_DELETE_SESSIONS" : "Kill Sessions", + "ACTION_LOGIN" : "Login", + "ACTION_LOGOUT" : "Logout", + "ACTION_MANAGE_CONNECTIONS" : "Manage Connections", + "ACTION_MANAGE_SESSIONS" : "Manage Sessions", + "ACTION_MANAGE_USERS" : "Manage Users", + "ACTION_NAVIGATE_BACK" : "Back", + "ACTION_NAVIGATE_HOME" : "Home", + "ACTION_SAVE" : "Save", - "DIALOG_HEADER_ERROR" : "Error", + "DIALOG_HEADER_ERROR" : "Error", "ERROR_PASSWORD_MISMATCH" : "The provided passwords do not match.", @@ -135,36 +138,16 @@ "FIELD_PLACEHOLDER_PASSWORD" : "Password" }, - - "MANAGE" : { - - "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", - "ACTION_NEW_USER" : "New User", - "ACTION_NEW_CONNECTION" : "New Connection", - "ACTION_NEW_CONNECTION_GROUP" : "New Group", - - "DIALOG_HEADER_ERROR" : "Error", - - "HELP_CONNECTIONS" : "Click or tap on a connection below to manage that connection. Depending on your access level, connections can be added and deleted, and their properties (protocol, hostname, port, etc.) can be changed.", - "HELP_SHOW_PASSWORD" : "Click to show password", - "HELP_HIDE_PASSWORD" : "Click to hide password", - "HELP_USERS" : "Click or tap on a user below to manage that user. Depending on your access level, users can be added and deleted, and their passwords can be changed.", - - "INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT", - - "SECTION_HEADER_ADMINISTRATION" : "Administration", - "SECTION_HEADER_CONNECTIONS" : "Connections", - "SECTION_HEADER_USERS" : "Users" - - }, "MANAGE_CONNECTION" : { - "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", + "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", + "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CLONE" : "@:APP.ACTION_CLONE", + "ACTION_DELETE" : "@:APP.ACTION_DELETE", + "ACTION_NEW_CONNECTION" : "New Connection", + "ACTION_NEW_CONNECTION_GROUP" : "New Group", + "ACTION_SAVE" : "@:APP.ACTION_SAVE", "DIALOG_HEADER_CONFIRM_DELETE" : "Delete Connection", "DIALOG_HEADER_ERROR" : "Error", @@ -172,11 +155,17 @@ "FIELD_HEADER_LOCATION" : "Location:", "FIELD_HEADER_NAME" : "Name:", "FIELD_HEADER_PROTOCOL" : "Protocol:", - + + "HELP_CONNECTIONS" : "Click or tap on a connection below to manage that connection. Depending on your access level, connections can be added and deleted, and their properties (protocol, hostname, port, etc.) can be changed.", + "HELP_SHOW_PASSWORD" : "Click to show password", + "HELP_HIDE_PASSWORD" : "Click to hide password", + + "INFO_ACTIVE_USER_COUNT" : "@:APP.INFO_ACTIVE_USER_COUNT", "INFO_CONNECTION_DURATION_UNKNOWN" : "--", "INFO_CONNECTION_ACTIVE_NOW" : "Active Now", "INFO_CONNECTION_NOT_USED" : "This connection has not yet been used.", + "SECTION_HEADER_CONNECTIONS" : "Connections", "SECTION_HEADER_EDIT_CONNECTION" : "Edit Connection", "SECTION_HEADER_HISTORY" : "Usage History", "SECTION_HEADER_PARAMETERS" : "Parameters", @@ -218,6 +207,7 @@ "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", "ACTION_DELETE" : "@:APP.ACTION_DELETE", + "ACTION_NEW_USER" : "New User", "ACTION_SAVE" : "@:APP.ACTION_SAVE", "DIALOG_HEADER_CONFIRM_DELETE" : "Delete User", @@ -232,14 +222,37 @@ "FIELD_HEADER_PASSWORD" : "@:APP.FIELD_HEADER_PASSWORD", "FIELD_HEADER_PASSWORD_AGAIN" : "@:APP.FIELD_HEADER_PASSWORD_AGAIN", "FIELD_HEADER_USERNAME" : "Username:", + + "HELP_USERS" : "Click or tap on a user below to manage that user. Depending on your access level, users can be added and deleted, and their passwords can be changed.", "SECTION_HEADER_CONNECTIONS" : "Connections", "SECTION_HEADER_EDIT_USER" : "Edit User", "SECTION_HEADER_PERMISSIONS" : "Permissions", + "SECTION_HEADER_USERS" : "Users", "TEXT_CONFIRM_DELETE" : "Users cannot be restored after they have been deleted. Are you sure you want to delete this user?" }, + + "MANAGE_SESSION" : { + + "ACTION_DELETE" : "Kill all selected sessions", + + "DIALOG_HEADER_CONFIRM_DELETE" : "Kill Sessions", + "DIALOG_HEADER_ERROR" : "Error", + + "HELP_SESSIONS" : "Click to kill a user session.", + + "SECTION_HEADER_SESSIONS" : "Sessions", + + "TABLE_HEADER_SESSION_USERNAME" : "Username", + "TABLE_HEADER_SESSION_STARTDATE" : "Start date", + "TABLE_HEADER_SESSION_REMOTEHOST" : "Remote host", + "TABLE_HEADER_SESSION_CONNECTION_NAME" : "Connection name", + + "TEXT_CONFIRM_DELETE" : "Are you sure you want to kill these sessions?" + + }, "PROTOCOL_RDP" : { @@ -383,13 +396,15 @@ "USER_MENU" : { - "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_NAVIGATE_HOME" : "@:APP.ACTION_NAVIGATE_HOME", - "ACTION_SAVE" : "@:APP.ACTION_SAVE", + "ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE", + "ACTION_CANCEL" : "@:APP.ACTION_CANCEL", + "ACTION_CHANGE_PASSWORD" : "@:APP.ACTION_CHANGE_PASSWORD", + "ACTION_LOGOUT" : "@:APP.ACTION_LOGOUT", + "ACTION_MANAGE_CONNECTIONS" : "@:APP.ACTION_MANAGE_CONNECTIONS", + "ACTION_MANAGE_SESSIONS" : "@:APP.ACTION_MANAGE_SESSIONS", + "ACTION_MANAGE_USERS" : "@:APP.ACTION_MANAGE_USERS", + "ACTION_NAVIGATE_HOME" : "@:APP.ACTION_NAVIGATE_HOME", + "ACTION_SAVE" : "@:APP.ACTION_SAVE", "DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",