mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 13:17:41 +00:00
Merge 1.0.0 changes back to master.
This commit is contained in:
@@ -308,7 +308,7 @@
|
||||
<dependency>
|
||||
<groupId>org.webjars.bower</groupId>
|
||||
<artifactId>lodash</artifactId>
|
||||
<version>2.4.1</version>
|
||||
<version>4.17.10</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -316,7 +316,7 @@
|
||||
<dependency>
|
||||
<groupId>org.webjars.bower</groupId>
|
||||
<artifactId>jquery</artifactId>
|
||||
<version>2.1.3</version>
|
||||
<version>3.3.1</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
|
@@ -486,13 +486,21 @@ Jettison (https://github.com/jettison-json/jettison)
|
||||
jQuery (http://jquery.com/)
|
||||
---------------------------
|
||||
|
||||
Version: 2.1.3
|
||||
From: 'jQuery Foundation' (http://jquery.com/)
|
||||
Version: 3.3.1
|
||||
From: 'JS Foundation' (https://js.foundation/)
|
||||
License(s):
|
||||
MIT (bundled/jquery-2.1.3/MIT-LICENSE.txt)
|
||||
MIT (bundled/jquery-3.3.1/LICENSE.txt)
|
||||
|
||||
Copyright 2014 jQuery Foundation and other contributors
|
||||
http://jquery.com/
|
||||
Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
|
||||
This software consists of voluntary contributions made by many
|
||||
individuals. For exact contribution history, see the revision history
|
||||
available at https://github.com/jquery/jquery
|
||||
|
||||
The following license applies to all parts of this software except as
|
||||
documented below:
|
||||
|
||||
====
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@@ -513,6 +521,13 @@ 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.
|
||||
|
||||
====
|
||||
|
||||
All files located in the node_modules and external directories are
|
||||
externally maintained libraries used by this software which have their
|
||||
own licenses; we recommend you read them, as their terms may differ from
|
||||
the terms above.
|
||||
|
||||
|
||||
JSR-250 Reference Implementation
|
||||
(https://jcp.org/aboutJava/communityprocess/final/jsr250/index.html)
|
||||
@@ -536,15 +551,25 @@ JSR-330 / Dependency Injection for Java (http://code.google.com/p/atinject/)
|
||||
Lodash (https://lodash.com/)
|
||||
----------------------------
|
||||
|
||||
Version: 2.4.1
|
||||
From: 'The Dojo Foundation' (http://dojofoundation.org/)
|
||||
Version: 4.17.10
|
||||
From: 'JS Foundation' (https://js.foundation/)
|
||||
License(s):
|
||||
MIT (bundled/lodash-2.4.1/LICENSE.txt)
|
||||
MIT (bundled/lodash-4.17.10/LICENSE)
|
||||
|
||||
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
|
||||
Copyright JS Foundation and other contributors <https://js.foundation/>
|
||||
|
||||
Based on Underscore.js, copyright Jeremy Ashkenas,
|
||||
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||
|
||||
This software consists of voluntary contributions made by many
|
||||
individuals. For exact contribution history, see the revision history
|
||||
available at https://github.com/lodash/lodash
|
||||
|
||||
The following license applies to all parts of this software except as
|
||||
documented below:
|
||||
|
||||
====
|
||||
|
||||
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
|
||||
@@ -564,6 +589,21 @@ 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.
|
||||
|
||||
====
|
||||
|
||||
Copyright and related rights for sample code are waived via CC0. Sample
|
||||
code is defined as all source code displayed within the prose of the
|
||||
documentation.
|
||||
|
||||
CC0: http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
====
|
||||
|
||||
Files located in the node_modules and vendor directories are externally
|
||||
maintained libraries used by this software which have their own
|
||||
licenses; we recommend you read them, as their terms may differ from the
|
||||
terms above.
|
||||
|
||||
|
||||
Logback (http://logback.qos.ch/)
|
||||
--------------------------------
|
||||
|
@@ -1,5 +1,13 @@
|
||||
Copyright 2014 jQuery Foundation and other contributors
|
||||
http://jquery.com/
|
||||
Copyright JS Foundation and other contributors, https://js.foundation/
|
||||
|
||||
This software consists of voluntary contributions made by many
|
||||
individuals. For exact contribution history, see the revision history
|
||||
available at https://github.com/jquery/jquery
|
||||
|
||||
The following license applies to all parts of this software except as
|
||||
documented below:
|
||||
|
||||
====
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
@@ -19,3 +27,10 @@ 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.
|
||||
|
||||
====
|
||||
|
||||
All files located in the node_modules and external directories are
|
||||
externally maintained libraries used by this software which have their
|
||||
own licenses; we recommend you read them, as their terms may differ from
|
||||
the terms above.
|
@@ -1,7 +1,17 @@
|
||||
Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
|
||||
Based on Underscore.js 1.5.2, copyright 2009-2013 Jeremy Ashkenas,
|
||||
Copyright JS Foundation and other contributors <https://js.foundation/>
|
||||
|
||||
Based on Underscore.js, copyright Jeremy Ashkenas,
|
||||
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||
|
||||
This software consists of voluntary contributions made by many
|
||||
individuals. For exact contribution history, see the revision history
|
||||
available at https://github.com/lodash/lodash
|
||||
|
||||
The following license applies to all parts of this software except as
|
||||
documented below:
|
||||
|
||||
====
|
||||
|
||||
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
|
||||
@@ -19,4 +29,19 @@ 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.
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
====
|
||||
|
||||
Copyright and related rights for sample code are waived via CC0. Sample
|
||||
code is defined as all source code displayed within the prose of the
|
||||
documentation.
|
||||
|
||||
CC0: http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
====
|
||||
|
||||
Files located in the node_modules and vendor directories are externally
|
||||
maintained libraries used by this software which have their own
|
||||
licenses; we recommend you read them, as their terms may differ from the
|
||||
terms above.
|
@@ -171,6 +171,15 @@ angular.module('index').config(['$routeProvider', '$locationProvider',
|
||||
resolve : { updateCurrentToken: updateCurrentToken }
|
||||
})
|
||||
|
||||
// User group editor
|
||||
.when('/manage/:dataSource/userGroups/:id?', {
|
||||
title : 'APP.NAME',
|
||||
bodyClassName : 'manage',
|
||||
templateUrl : 'app/manage/templates/manageUserGroup.html',
|
||||
controller : 'manageUserGroupController',
|
||||
resolve : { updateCurrentToken: updateCurrentToken }
|
||||
})
|
||||
|
||||
// Client view
|
||||
.when('/client/:id/:params?', {
|
||||
bodyClassName : 'client',
|
||||
|
@@ -18,12 +18,14 @@
|
||||
*/
|
||||
|
||||
.user,
|
||||
.user-group,
|
||||
.connection-group,
|
||||
.connection {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.user a,
|
||||
.user-group a,
|
||||
.connection a,
|
||||
.connection-group a {
|
||||
text-decoration:none;
|
||||
@@ -31,6 +33,7 @@
|
||||
}
|
||||
|
||||
.user a:hover,
|
||||
.user-group a:hover,
|
||||
.connection a:hover,
|
||||
.connection-group a:hover {
|
||||
text-decoration:none;
|
||||
@@ -38,6 +41,7 @@
|
||||
}
|
||||
|
||||
.user a:visited,
|
||||
.user-group a:visited,
|
||||
.connection a:visited,
|
||||
.connection-group a:visited {
|
||||
text-decoration:none;
|
||||
|
@@ -156,6 +156,14 @@ div.section {
|
||||
background-image: url('images/action-icons/guac-user-add.png');
|
||||
}
|
||||
|
||||
.icon.user-group {
|
||||
background-image: url('images/user-icons/guac-user-group.png');
|
||||
}
|
||||
|
||||
.icon.user-group.add {
|
||||
background-image: url('images/action-icons/guac-user-group-add.png');
|
||||
}
|
||||
|
||||
.icon.connection {
|
||||
background-image: url('images/protocol-icons/guac-plug.png');
|
||||
}
|
||||
|
@@ -36,9 +36,11 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
|
||||
var $q = $injector.get('$q');
|
||||
var authenticationService = $injector.get('authenticationService');
|
||||
var dataSourceService = $injector.get('dataSourceService');
|
||||
var membershipService = $injector.get('membershipService');
|
||||
var permissionService = $injector.get('permissionService');
|
||||
var requestService = $injector.get('requestService');
|
||||
var schemaService = $injector.get('schemaService');
|
||||
var userGroupService = $injector.get('userGroupService');
|
||||
var userService = $injector.get('userService');
|
||||
|
||||
/**
|
||||
@@ -133,6 +135,46 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
|
||||
*/
|
||||
$scope.permissionsRemoved = new PermissionSet();
|
||||
|
||||
/**
|
||||
* The identifiers of all user groups which can be manipulated (all groups
|
||||
* for which the user accessing this interface has UPDATE permission),
|
||||
* either through adding the current user as a member or removing the
|
||||
* current user from that group. If this information has not yet been
|
||||
* retrieved, this will be null.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.availableGroups = null;
|
||||
|
||||
/**
|
||||
* The identifiers of all user groups of which the user is a member,
|
||||
* taking into account any user groups which will be added/removed when
|
||||
* saved. If this information has not yet been retrieved, this will be
|
||||
* null.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.parentGroups = null;
|
||||
|
||||
/**
|
||||
* The set of identifiers of all parent user groups to which the user will
|
||||
* be added when saved. Parent groups will only be present in this set if
|
||||
* they are manually added, and not later manually removed before saving.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.parentGroupsAdded = [];
|
||||
|
||||
/**
|
||||
* The set of identifiers of all parent user groups from which the user
|
||||
* will be removed when saved. Parent groups will only be present in this
|
||||
* set if they are manually removed, and not later manually added before
|
||||
* saving.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.parentGroupsRemoved = [];
|
||||
|
||||
/**
|
||||
* For each applicable data source, the management-related actions that the
|
||||
* current user may perform on the user account currently being created
|
||||
@@ -166,6 +208,8 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
|
||||
return $scope.users !== null
|
||||
&& $scope.permissionFlags !== null
|
||||
&& $scope.managementPermissions !== null
|
||||
&& $scope.availableGroups !== null
|
||||
&& $scope.parentGroups !== null
|
||||
&& $scope.attributes !== null;
|
||||
|
||||
};
|
||||
@@ -204,12 +248,14 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
|
||||
var loadExistingUser = function loadExistingUser(dataSource, username) {
|
||||
return $q.all({
|
||||
users : dataSourceService.apply(userService.getUser, dataSources, username),
|
||||
permissions : permissionService.getPermissions(dataSource, username)
|
||||
permissions : permissionService.getPermissions(dataSource, username),
|
||||
parentGroups : membershipService.getUserGroups(dataSource, username)
|
||||
})
|
||||
.then(function userDataRetrieved(values) {
|
||||
|
||||
$scope.users = values.users;
|
||||
$scope.user = values.users[dataSource];
|
||||
$scope.parentGroups = values.parentGroups;
|
||||
|
||||
// Create skeleton user if user does not exist
|
||||
if (!$scope.user)
|
||||
@@ -243,12 +289,15 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
|
||||
var loadClonedUser = function loadClonedUser(dataSource, username) {
|
||||
return $q.all({
|
||||
users : dataSourceService.apply(userService.getUser, [dataSource], username),
|
||||
permissions : permissionService.getPermissions(dataSource, username)
|
||||
permissions : permissionService.getPermissions(dataSource, username),
|
||||
parentGroups : membershipService.getUserGroups(dataSource, username)
|
||||
})
|
||||
.then(function userDataRetrieved(values) {
|
||||
|
||||
$scope.users = {};
|
||||
$scope.user = values.users[dataSource];
|
||||
$scope.parentGroups = values.parentGroups;
|
||||
$scope.parentGroupsAdded = values.parentGroups;
|
||||
|
||||
// The current user will be associated with cloneSourceUsername in the
|
||||
// retrieved permission set
|
||||
@@ -274,6 +323,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
|
||||
|
||||
// Use skeleton user object with no associated permissions
|
||||
$scope.user = new User();
|
||||
$scope.parentGroups = [];
|
||||
$scope.permissionFlags = new PermissionFlagSet();
|
||||
|
||||
// As no permissions are yet associated with the user, it is safe to
|
||||
@@ -314,6 +364,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
|
||||
$q.all({
|
||||
userData : loadRequestedUser(),
|
||||
permissions : dataSourceService.apply(permissionService.getEffectivePermissions, dataSources, currentUsername),
|
||||
userGroups : userGroupService.getUserGroups($scope.dataSource, [ PermissionSet.ObjectPermissionType.UPDATE ]),
|
||||
attributes : schemaService.getUserAttributes($scope.dataSource)
|
||||
})
|
||||
.then(function dataReceived(values) {
|
||||
@@ -326,6 +377,12 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
|
||||
// Determine whether data source contains this user
|
||||
var exists = (dataSource in $scope.users);
|
||||
|
||||
// Add the identifiers of all modifiable user groups
|
||||
$scope.availableGroups = [];
|
||||
angular.forEach(values.userGroups, function addUserGroupIdentifier(userGroup) {
|
||||
$scope.availableGroups.push(userGroup.identifier);
|
||||
});
|
||||
|
||||
// Calculate management actions available for this specific account
|
||||
$scope.managementPermissions[dataSource] = ManagementPermissions.fromPermissionSet(
|
||||
values.permissions[dataSource],
|
||||
@@ -415,9 +472,11 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
|
||||
|
||||
}
|
||||
|
||||
// Upon success, save any changed permissions
|
||||
return permissionService.patchPermissions($scope.dataSource, $scope.user.username,
|
||||
$scope.permissionsAdded, $scope.permissionsRemoved);
|
||||
// Upon success, save any changed permissions/groups
|
||||
return $q.all([
|
||||
permissionService.patchPermissions($scope.dataSource, $scope.user.username, $scope.permissionsAdded, $scope.permissionsRemoved),
|
||||
membershipService.patchUserGroups($scope.dataSource, $scope.user.username, $scope.parentGroupsAdded, $scope.parentGroupsRemoved)
|
||||
]);
|
||||
|
||||
});
|
||||
|
||||
|
@@ -0,0 +1,538 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The controller for editing user groups.
|
||||
*/
|
||||
angular.module('manage').controller('manageUserGroupController', ['$scope', '$injector',
|
||||
function manageUserGroupController($scope, $injector) {
|
||||
|
||||
// Required types
|
||||
var ManagementPermissions = $injector.get('ManagementPermissions');
|
||||
var PermissionFlagSet = $injector.get('PermissionFlagSet');
|
||||
var PermissionSet = $injector.get('PermissionSet');
|
||||
var UserGroup = $injector.get('UserGroup');
|
||||
|
||||
// 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 membershipService = $injector.get('membershipService');
|
||||
var permissionService = $injector.get('permissionService');
|
||||
var requestService = $injector.get('requestService');
|
||||
var schemaService = $injector.get('schemaService');
|
||||
var userGroupService = $injector.get('userGroupService');
|
||||
var userService = $injector.get('userService');
|
||||
|
||||
/**
|
||||
* The identifiers of all data sources currently available to the
|
||||
* authenticated user.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
var dataSources = authenticationService.getAvailableDataSources();
|
||||
|
||||
/**
|
||||
* The username of the current, authenticated user.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
var currentUsername = authenticationService.getCurrentUsername();
|
||||
|
||||
/**
|
||||
* The identifier of the original user group from which this user group is
|
||||
* being cloned. Only valid if this is a new user group.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
var cloneSourceIdentifier = $location.search().clone;
|
||||
|
||||
/**
|
||||
* The identifier of the user group being edited. If a new user group is
|
||||
* being created, this will not be defined.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
var identifier = $routeParams.id;
|
||||
|
||||
/**
|
||||
* The unique identifier of the data source containing the user group being
|
||||
* edited.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
$scope.dataSource = $routeParams.dataSource;
|
||||
|
||||
/**
|
||||
* All user groups associated with the same identifier as the group being
|
||||
* created or edited, as a map of data source identifier to the UserGroup
|
||||
* object within that data source.
|
||||
*
|
||||
* @type Object.<String, UserGroup>
|
||||
*/
|
||||
$scope.userGroups = null;
|
||||
|
||||
/**
|
||||
* The user group being modified.
|
||||
*
|
||||
* @type UserGroup
|
||||
*/
|
||||
$scope.userGroup = null;
|
||||
|
||||
/**
|
||||
* All permissions associated with the user group being modified.
|
||||
*
|
||||
* @type PermissionFlagSet
|
||||
*/
|
||||
$scope.permissionFlags = null;
|
||||
|
||||
/**
|
||||
* The set of permissions that will be added to the user group when the
|
||||
* user group 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 group when the
|
||||
* user group 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 identifiers of all user groups which can be manipulated (all groups
|
||||
* for which the user accessing this interface has UPDATE permission),
|
||||
* whether that means changing the members of those groups or changing the
|
||||
* groups of which those groups are members. If this information has not
|
||||
* yet been retrieved, this will be null.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.availableGroups = null;
|
||||
|
||||
/**
|
||||
* The identifiers of all users which can be manipulated (all users for
|
||||
* which the user accessing this interface has UPDATE permission), either
|
||||
* through adding those users as a member of the current group or removing
|
||||
* those users from the current group. If this information has not yet been
|
||||
* retrieved, this will be null.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.availableUsers = null;
|
||||
|
||||
/**
|
||||
* The identifiers of all user groups of which this group is a member,
|
||||
* taking into account any user groups which will be added/removed when
|
||||
* saved. If this information has not yet been retrieved, this will be
|
||||
* null.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.parentGroups = null;
|
||||
|
||||
/**
|
||||
* The set of identifiers of all parent user groups to which this group
|
||||
* will be added when saved. Parent groups will only be present in this set
|
||||
* if they are manually added, and not later manually removed before
|
||||
* saving.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.parentGroupsAdded = [];
|
||||
|
||||
/**
|
||||
* The set of identifiers of all parent user groups from which this group
|
||||
* will be removed when saved. Parent groups will only be present in this
|
||||
* set if they are manually removed, and not later manually added before
|
||||
* saving.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.parentGroupsRemoved = [];
|
||||
|
||||
/**
|
||||
* The identifiers of all user groups which are members of this group,
|
||||
* taking into account any user groups which will be added/removed when
|
||||
* saved. If this information has not yet been retrieved, this will be
|
||||
* null.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.memberGroups = null;
|
||||
|
||||
/**
|
||||
* The set of identifiers of all member user groups which will be added to
|
||||
* this group when saved. Member groups will only be present in this set if
|
||||
* they are manually added, and not later manually removed before saving.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.memberGroupsAdded = [];
|
||||
|
||||
/**
|
||||
* The set of identifiers of all member user groups which will be removed
|
||||
* from this group when saved. Member groups will only be present in this
|
||||
* set if they are manually removed, and not later manually added before
|
||||
* saving.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.memberGroupsRemoved = [];
|
||||
|
||||
/**
|
||||
* The identifiers of all users which are members of this group, taking
|
||||
* into account any users which will be added/removed when saved. If this
|
||||
* information has not yet been retrieved, this will be null.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.memberUsers = null;
|
||||
|
||||
/**
|
||||
* The set of identifiers of all member users which will be added to this
|
||||
* group when saved. Member users will only be present in this set if they
|
||||
* are manually added, and not later manually removed before saving.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.memberUsersAdded = [];
|
||||
|
||||
/**
|
||||
* The set of identifiers of all member users which will be removed from
|
||||
* this group when saved. Member users will only be present in this set if
|
||||
* they are manually removed, and not later manually added before saving.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.memberUsersRemoved = [];
|
||||
|
||||
/**
|
||||
* For each applicable data source, the management-related actions that the
|
||||
* current user may perform on the user group currently being created
|
||||
* or modified, as a map of data source identifier to the
|
||||
* {@link ManagementPermissions} object describing the actions available
|
||||
* within that data source, or null if the current user's permissions have
|
||||
* not yet been loaded.
|
||||
*
|
||||
* @type Object.<String, ManagementPermissions>
|
||||
*/
|
||||
$scope.managementPermissions = null;
|
||||
|
||||
/**
|
||||
* All available user group attributes. This is only the set of attribute
|
||||
* definitions, organized as logical groupings of attributes, not attribute
|
||||
* values.
|
||||
*
|
||||
* @type Form[]
|
||||
*/
|
||||
$scope.attributes = null;
|
||||
|
||||
/**
|
||||
* Returns whether critical data has completed being loaded.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* true if enough data has been loaded for the user group interface to
|
||||
* be useful, false otherwise.
|
||||
*/
|
||||
$scope.isLoaded = function isLoaded() {
|
||||
|
||||
return $scope.userGroups !== null
|
||||
&& $scope.permissionFlags !== null
|
||||
&& $scope.managementPermissions !== null
|
||||
&& $scope.availableGroups !== null
|
||||
&& $scope.availableUsers !== null
|
||||
&& $scope.parentGroups !== null
|
||||
&& $scope.memberGroups !== null
|
||||
&& $scope.memberUsers !== null
|
||||
&& $scope.attributes !== null;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether the current user can edit the identifier of the user
|
||||
* group being edited.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* true if the current user can edit the identifier of the user group
|
||||
* being edited, false otherwise.
|
||||
*/
|
||||
$scope.canEditIdentifier = function canEditIdentifier() {
|
||||
return !identifier;
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads the data associated with the user group having the given
|
||||
* identifier, preparing the interface for making modifications to that
|
||||
* existing user group.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user group
|
||||
* to load.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The unique identifier of the user group to load.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* A promise which is resolved when the interface has been prepared for
|
||||
* editing the given user group.
|
||||
*/
|
||||
var loadExistingUserGroup = function loadExistingGroup(dataSource, identifier) {
|
||||
return $q.all({
|
||||
userGroups : dataSourceService.apply(userGroupService.getUserGroup, dataSources, identifier),
|
||||
permissions : permissionService.getPermissions(dataSource, identifier, true),
|
||||
parentGroups : membershipService.getUserGroups(dataSource, identifier, true),
|
||||
memberGroups : membershipService.getMemberUserGroups(dataSource, identifier),
|
||||
memberUsers : membershipService.getMemberUsers(dataSource, identifier)
|
||||
})
|
||||
.then(function userGroupDataRetrieved(values) {
|
||||
|
||||
$scope.userGroups = values.userGroups;
|
||||
$scope.userGroup = values.userGroups[dataSource];
|
||||
$scope.parentGroups = values.parentGroups;
|
||||
$scope.memberGroups = values.memberGroups;
|
||||
$scope.memberUsers = values.memberUsers;
|
||||
|
||||
// Create skeleton user group if user group does not exist
|
||||
if (!$scope.userGroup)
|
||||
$scope.userGroup = new UserGroup({
|
||||
'identifier' : identifier
|
||||
});
|
||||
|
||||
$scope.permissionFlags = PermissionFlagSet.fromPermissionSet(values.permissions);
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads the data associated with the user group having the given
|
||||
* identifier, preparing the interface for cloning that existing user
|
||||
* group.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user group to
|
||||
* be cloned.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The unique identifier of the user group being cloned.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* A promise which is resolved when the interface has been prepared for
|
||||
* cloning the given user group.
|
||||
*/
|
||||
var loadClonedUserGroup = function loadClonedUserGroup(dataSource, identifier) {
|
||||
return $q.all({
|
||||
userGroups : dataSourceService.apply(userGroupService.getUserGroup, [dataSource], identifier),
|
||||
permissions : permissionService.getPermissions(dataSource, identifier, true),
|
||||
parentGroups : membershipService.getUserGroups(dataSource, identifier, true),
|
||||
memberGroups : membershipService.getMemberUserGroups(dataSource, identifier),
|
||||
memberUsers : membershipService.getMemberUsers(dataSource, identifier)
|
||||
})
|
||||
.then(function userGroupDataRetrieved(values) {
|
||||
|
||||
$scope.userGroups = {};
|
||||
$scope.userGroup = values.userGroups[dataSource];
|
||||
$scope.parentGroups = values.parentGroups;
|
||||
$scope.parentGroupsAdded = values.parentGroups;
|
||||
$scope.memberGroups = values.memberGroups;
|
||||
$scope.memberGroupsAdded = values.memberGroups;
|
||||
$scope.memberUsers = values.memberUsers;
|
||||
$scope.memberUsersAdded = values.memberUsers;
|
||||
|
||||
$scope.permissionFlags = PermissionFlagSet.fromPermissionSet(values.permissions);
|
||||
$scope.permissionsAdded = values.permissions;
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads skeleton user group data, preparing the interface for creating a
|
||||
* new user group.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* A promise which is resolved when the interface has been prepared for
|
||||
* creating a new user group.
|
||||
*/
|
||||
var loadSkeletonUserGroup = function loadSkeletonUserGroup() {
|
||||
|
||||
// No user groups exist regardless of data source if the user group is
|
||||
// being created
|
||||
$scope.userGroups = {};
|
||||
|
||||
// Use skeleton user group object with no associated permissions
|
||||
$scope.userGroup = new UserGroup();
|
||||
$scope.parentGroups = [];
|
||||
$scope.memberGroups = [];
|
||||
$scope.memberUsers = [];
|
||||
$scope.permissionFlags = new PermissionFlagSet();
|
||||
|
||||
return $q.resolve();
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads the data required for performing the management task requested
|
||||
* through the route parameters given at load time, automatically preparing
|
||||
* the interface for editing an existing user group, cloning an existing
|
||||
* user group, or creating an entirely new user group.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* A promise which is resolved when the interface has been prepared
|
||||
* for performing the requested management task.
|
||||
*/
|
||||
var loadRequestedUserGroup = function loadRequestedUserGroup() {
|
||||
|
||||
// Pull user group data and permissions if we are editing an existing
|
||||
// user group
|
||||
if (identifier)
|
||||
return loadExistingUserGroup($scope.dataSource, identifier);
|
||||
|
||||
// If we are cloning an existing user group, pull its data instead
|
||||
if (cloneSourceIdentifier)
|
||||
return loadClonedUserGroup($scope.dataSource, cloneSourceIdentifier);
|
||||
|
||||
// If we are creating a new user group, populate skeleton user group data
|
||||
return loadSkeletonUserGroup();
|
||||
|
||||
};
|
||||
|
||||
// Populate interface with requested data
|
||||
$q.all({
|
||||
userGroupData : loadRequestedUserGroup(),
|
||||
permissions : dataSourceService.apply(permissionService.getEffectivePermissions, dataSources, currentUsername),
|
||||
userGroups : userGroupService.getUserGroups($scope.dataSource, [ PermissionSet.ObjectPermissionType.UPDATE ]),
|
||||
users : userService.getUsers($scope.dataSource, [ PermissionSet.ObjectPermissionType.UPDATE ]),
|
||||
attributes : schemaService.getUserGroupAttributes($scope.dataSource)
|
||||
})
|
||||
.then(function dataReceived(values) {
|
||||
|
||||
$scope.attributes = values.attributes;
|
||||
|
||||
$scope.managementPermissions = {};
|
||||
angular.forEach(dataSources, function deriveManagementPermissions(dataSource) {
|
||||
|
||||
// Determine whether data source contains this user group
|
||||
var exists = (dataSource in $scope.userGroups);
|
||||
|
||||
// Add the identifiers of all modifiable user groups
|
||||
$scope.availableGroups = [];
|
||||
angular.forEach(values.userGroups, function addUserGroupIdentifier(userGroup) {
|
||||
$scope.availableGroups.push(userGroup.identifier);
|
||||
});
|
||||
|
||||
// Add the identifiers of all modifiable users
|
||||
$scope.availableUsers = [];
|
||||
angular.forEach(values.users, function addUserIdentifier(user) {
|
||||
$scope.availableUsers.push(user.username);
|
||||
});
|
||||
|
||||
// Calculate management actions available for this specific group
|
||||
$scope.managementPermissions[dataSource] = ManagementPermissions.fromPermissionSet(
|
||||
values.permissions[dataSource],
|
||||
PermissionSet.SystemPermissionType.CREATE_USER_GROUP,
|
||||
PermissionSet.hasUserGroupPermission,
|
||||
exists ? identifier : null);
|
||||
|
||||
});
|
||||
|
||||
}, requestService.WARN);
|
||||
|
||||
/**
|
||||
* Returns the URL for the page which manages the user group currently
|
||||
* being edited under the given data source. The given data source need not
|
||||
* be the same as the data source currently selected.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source that the URL is being
|
||||
* generated for.
|
||||
*
|
||||
* @returns {String}
|
||||
* The URL for the page which manages the user group currently being
|
||||
* edited under the given data source.
|
||||
*/
|
||||
$scope.getUserGroupURL = function getUserGroupURL(dataSource) {
|
||||
return '/manage/' + encodeURIComponent(dataSource) + '/userGroups/' + encodeURIComponent(identifier || '');
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancels all pending edits, returning to the main list of user groups.
|
||||
*/
|
||||
$scope.returnToUserGroupList = function returnToUserGroupList() {
|
||||
$location.url('/settings/userGroups');
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancels all pending edits, opening an edit page for a new user group
|
||||
* which is prepopulated with the data from the user currently being edited.
|
||||
*/
|
||||
$scope.cloneUserGroup = function cloneUserGroup() {
|
||||
$location.path('/manage/' + encodeURIComponent($scope.dataSource) + '/userGroups').search('clone', identifier);
|
||||
};
|
||||
|
||||
/**
|
||||
* Saves the current user group, creating a new user group or updating the
|
||||
* existing user group depending on context, returning a promise which is
|
||||
* resolved if the save operation succeeds and rejected if the save
|
||||
* operation fails.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* A promise which is resolved if the save operation succeeds and is
|
||||
* rejected with an {@link Error} if the save operation fails.
|
||||
*/
|
||||
$scope.saveUserGroup = function saveUserGroup() {
|
||||
|
||||
// Save or create the user group, depending on whether the user group exists
|
||||
var saveUserGroupPromise;
|
||||
if ($scope.dataSource in $scope.userGroups)
|
||||
saveUserGroupPromise = userGroupService.saveUserGroup($scope.dataSource, $scope.userGroup);
|
||||
else
|
||||
saveUserGroupPromise = userGroupService.createUserGroup($scope.dataSource, $scope.userGroup);
|
||||
|
||||
return saveUserGroupPromise.then(function savedUserGroup() {
|
||||
return $q.all([
|
||||
permissionService.patchPermissions($scope.dataSource, $scope.userGroup.identifier, $scope.permissionsAdded, $scope.permissionsRemoved, true),
|
||||
membershipService.patchUserGroups($scope.dataSource, $scope.userGroup.identifier, $scope.parentGroupsAdded, $scope.parentGroupsRemoved, true),
|
||||
membershipService.patchMemberUserGroups($scope.dataSource, $scope.userGroup.identifier, $scope.memberGroupsAdded, $scope.memberGroupsRemoved),
|
||||
membershipService.patchMemberUsers($scope.dataSource, $scope.userGroup.identifier, $scope.memberUsersAdded, $scope.memberUsersRemoved)
|
||||
]);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes the current user group, returning a promise which is resolved if
|
||||
* the delete operation succeeds and rejected if the delete operation
|
||||
* fails.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* A promise which is resolved if the delete operation succeeds and is
|
||||
* rejected with an {@link Error} if the delete operation fails.
|
||||
*/
|
||||
$scope.deleteUserGroup = function deleteUserGroup() {
|
||||
return userGroupService.deleteUserGroup($scope.dataSource, $scope.userGroup);
|
||||
};
|
||||
|
||||
}]);
|
@@ -76,9 +76,12 @@ angular.module('manage').directive('dataSourceTabs', ['$injector',
|
||||
$scope.$watch('permissions', function permissionsChanged(permissions) {
|
||||
|
||||
$scope.pages = [];
|
||||
angular.forEach(permissions, function addDataSourcePage(managementPermissions, dataSource) {
|
||||
|
||||
var dataSources = _.keys($scope.permissions).sort();
|
||||
angular.forEach(dataSources, function addDataSourcePage(dataSource) {
|
||||
|
||||
// Determine whether data source contains this object
|
||||
var managementPermissions = permissions[dataSource];
|
||||
var exists = !!managementPermissions.identifier;
|
||||
|
||||
// Data source is not relevant if the associated object does not
|
||||
|
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A directive for manipulating a set of objects sharing some common relation
|
||||
* and represented by an array of their identifiers. The specific objects
|
||||
* added or removed are tracked within a separate pair of arrays of
|
||||
* identifiers.
|
||||
*/
|
||||
angular.module('manage').directive('identifierSetEditor', ['$injector',
|
||||
function identifierSetEditor($injector) {
|
||||
|
||||
var directive = {
|
||||
|
||||
// Element only
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
|
||||
scope: {
|
||||
|
||||
/**
|
||||
* The translation key of the text which should be displayed within
|
||||
* the main header of the identifier set editor.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
header : '@',
|
||||
|
||||
/**
|
||||
* The translation key of the text which should be displayed if no
|
||||
* identifiers are currently present within the set.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
emptyPlaceholder : '@',
|
||||
|
||||
/**
|
||||
* The translation key of the text which should be displayed if no
|
||||
* identifiers are available to be added within the set.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
unavailablePlaceholder : '@',
|
||||
|
||||
/**
|
||||
* All identifiers which are available to be added to or removed
|
||||
* from the identifier set being edited.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
identifiersAvailable : '=',
|
||||
|
||||
/**
|
||||
* The current state of the identifier set being manipulated. This
|
||||
* array will be modified as changes are made through this
|
||||
* identifier set editor.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
identifiers : '=',
|
||||
|
||||
/**
|
||||
* The set of identifiers that have been added, relative to the
|
||||
* initial state of the identifier set being manipulated.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
identifiersAdded : '=',
|
||||
|
||||
/**
|
||||
* The set of identifiers that have been removed, relative to the
|
||||
* initial state of the identifier set being manipulated.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
identifiersRemoved : '='
|
||||
|
||||
},
|
||||
|
||||
templateUrl: 'app/manage/templates/identifierSetEditor.html'
|
||||
|
||||
};
|
||||
|
||||
directive.controller = ['$scope', function identifierSetEditorController($scope) {
|
||||
|
||||
/**
|
||||
* Whether the full list of available identifiers should be displayed.
|
||||
* Initially, only an abbreviated list of identifiers currently present
|
||||
* is shown.
|
||||
*
|
||||
* @type Boolean
|
||||
*/
|
||||
$scope.expanded = false;
|
||||
|
||||
/**
|
||||
* Map of identifiers to boolean flags indicating whether that
|
||||
* identifier is currently present (true) or absent (false). If an
|
||||
* identifier is absent, it may also be absent from this map.
|
||||
*
|
||||
* @type Object.<String, Boolean>
|
||||
*/
|
||||
$scope.identifierFlags = {};
|
||||
|
||||
/**
|
||||
* Map of identifiers to boolean flags indicating whether that
|
||||
* identifier is editable. If an identifier is not editable, it will be
|
||||
* absent from this map.
|
||||
*
|
||||
* @type Object.<String, Boolean>
|
||||
*/
|
||||
$scope.isEditable = {};
|
||||
|
||||
/**
|
||||
* Adds the given identifier to the given sorted array of identifiers,
|
||||
* preserving the sorted order of the array. If the identifier is
|
||||
* already present, no change is made to the array. The given array
|
||||
* must already be sorted in ascending order.
|
||||
*
|
||||
* @param {String[]} arr
|
||||
* The sorted array of identifiers to add the given identifier to.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier to add to the given array.
|
||||
*/
|
||||
var addIdentifier = function addIdentifier(arr, identifier) {
|
||||
|
||||
// Determine location that the identifier should be added to
|
||||
// maintain sorted order
|
||||
var index = _.sortedIndex(arr, identifier);
|
||||
|
||||
// Do not add if already present
|
||||
if (arr[index] === identifier)
|
||||
return;
|
||||
|
||||
// Insert identifier at determined location
|
||||
arr.splice(index, 0, identifier);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the given identifier from the given sorted array of
|
||||
* identifiers, preserving the sorted order of the array. If the
|
||||
* identifier is already absent, no change is made to the array. The
|
||||
* given array must already be sorted in ascending order.
|
||||
*
|
||||
* @param {String[]} arr
|
||||
* The sorted array of identifiers to remove the given identifier
|
||||
* from.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier to remove from the given array.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* true if the identifier was present in the given array and has
|
||||
* been removed, false otherwise.
|
||||
*/
|
||||
var removeIdentifier = function removeIdentifier(arr, identifier) {
|
||||
|
||||
// Search for identifier in sorted array
|
||||
var index = _.sortedIndexOf(arr, identifier);
|
||||
|
||||
// Nothing to do if already absent
|
||||
if (index === -1)
|
||||
return false;
|
||||
|
||||
// Remove identifier
|
||||
arr.splice(index, 1);
|
||||
return true;
|
||||
|
||||
};
|
||||
|
||||
// Keep identifierFlags up to date when identifiers array is replaced
|
||||
// or initially assigned
|
||||
$scope.$watch('identifiers', function identifiersChanged(identifiers) {
|
||||
|
||||
// Maintain identifiers in sorted order so additions and removals
|
||||
// can be made more efficiently
|
||||
if (identifiers)
|
||||
identifiers.sort();
|
||||
|
||||
// Convert array of identifiers into set of boolean
|
||||
// presence/absence flags
|
||||
$scope.identifierFlags = {};
|
||||
angular.forEach(identifiers, function storeIdentifierFlag(identifier) {
|
||||
$scope.identifierFlags[identifier] = true;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// An identifier is editable iff it is available to be added or removed
|
||||
// from the identifier set being edited (iff it is within the
|
||||
// identifiersAvailable array)
|
||||
$scope.$watch('identifiersAvailable', function availableIdentifiersChanged(identifiers) {
|
||||
$scope.isEditable = {};
|
||||
angular.forEach(identifiers, function storeEditableIdentifier(identifier) {
|
||||
$scope.isEditable[identifier] = true;
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Notifies the controller that a change has been made to the flag
|
||||
* denoting presence/absence of a particular identifier within the
|
||||
* <code>identifierFlags</code> map. The <code>identifiers</code>,
|
||||
* <code>identifiersAdded</code>, and <code>identifiersRemoved</code>
|
||||
* arrays are updated accordingly.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier which has been added or removed through modifying
|
||||
* its boolean flag within <code>identifierFlags</code>.
|
||||
*/
|
||||
$scope.identifierChanged = function identifierChanged(identifier) {
|
||||
|
||||
// Determine status of modified identifier
|
||||
var present = !!$scope.identifierFlags[identifier];
|
||||
|
||||
// Add/remove identifier from added/removed sets depending on
|
||||
// change in flag state
|
||||
if (present) {
|
||||
|
||||
addIdentifier($scope.identifiers, identifier);
|
||||
|
||||
if (!removeIdentifier($scope.identifiersRemoved, identifier))
|
||||
addIdentifier($scope.identifiersAdded, identifier);
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
removeIdentifier($scope.identifiers, identifier);
|
||||
|
||||
if (!removeIdentifier($scope.identifiersAdded, identifier))
|
||||
addIdentifier($scope.identifiersRemoved, identifier);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the given identifier, updating <code>identifierFlags</code>,
|
||||
* <code>identifiers</code>, <code>identifiersAdded</code>, and
|
||||
* <code>identifiersRemoved</code> accordingly.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier to remove.
|
||||
*/
|
||||
$scope.removeIdentifier = function removeIdentifier(identifier) {
|
||||
$scope.identifierFlags[identifier] = false;
|
||||
$scope.identifierChanged(identifier);
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows the full list of available identifiers. If the full list is
|
||||
* already shown, this function has no effect.
|
||||
*/
|
||||
$scope.expand = function expand() {
|
||||
$scope.expanded = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides the full list of available identifiers. If the full list is
|
||||
* already hidden, this function has no effect.
|
||||
*/
|
||||
$scope.collapse = function collapse() {
|
||||
$scope.expanded = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether there are absolutely no identifiers that can be
|
||||
* managed using this editor. If true, the editor is effectively
|
||||
* useless, as there is nothing whatsoever to display.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* true if there are no identifiers that can be managed using this
|
||||
* editor, false otherwise.
|
||||
*/
|
||||
$scope.isEmpty = function isEmpty() {
|
||||
return _.isEmpty($scope.identifiers)
|
||||
&& _.isEmpty($scope.identifiersAvailable);
|
||||
};
|
||||
|
||||
}];
|
||||
|
||||
return directive;
|
||||
|
||||
}]);
|
@@ -125,6 +125,10 @@ angular.module('manage').directive('systemPermissionEditor', ['$injector',
|
||||
label: "MANAGE_USER.FIELD_HEADER_CREATE_NEW_USERS",
|
||||
value: PermissionSet.SystemPermissionType.CREATE_USER
|
||||
},
|
||||
{
|
||||
label: "MANAGE_USER.FIELD_HEADER_CREATE_NEW_USER_GROUPS",
|
||||
value: PermissionSet.SystemPermissionType.CREATE_USER_GROUP
|
||||
},
|
||||
{
|
||||
label: "MANAGE_USER.FIELD_HEADER_CREATE_NEW_CONNECTIONS",
|
||||
value: PermissionSet.SystemPermissionType.CREATE_CONNECTION
|
||||
|
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.manage-user-group .page-tabs .page-list li.read-only a[href],
|
||||
.manage-user-group .page-tabs .page-list li.unlinked a[href],
|
||||
.manage-user-group .page-tabs .page-list li.linked a[href] {
|
||||
padding-right: 2.5em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.manage-user-group .page-tabs .page-list li.read-only a[href]:before,
|
||||
.manage-user-group .page-tabs .page-list li.unlinked a[href]:before,
|
||||
.manage-user-group .page-tabs .page-list li.linked a[href]:before {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
width: 2.5em;
|
||||
background-size: 1.25em;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.manage-user-group .page-tabs .page-list li.read-only a[href]:before {
|
||||
background-image: url('images/lock.png');
|
||||
}
|
||||
|
||||
.manage-user-group .page-tabs .page-list li.unlinked a[href]:before {
|
||||
background-image: url('images/plus.png');
|
||||
}
|
||||
|
||||
.manage-user-group .page-tabs .page-list li.unlinked a[href] {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.manage-user-group .page-tabs .page-list li.unlinked a[href]:hover,
|
||||
.manage-user-group .page-tabs .page-list li.unlinked a[href].current {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.manage-user-group .page-tabs .page-list li.linked a[href]:before {
|
||||
background-image: url('images/checkmark.png');
|
||||
}
|
||||
|
||||
.manage-user-group .notice.read-only {
|
||||
|
||||
background: #FDA;
|
||||
border: 1px solid rgba(0, 0, 0, 0.125);
|
||||
border-radius: 0.25em;
|
||||
|
||||
text-align: center;
|
||||
padding: 1em;
|
||||
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.related-objects .abbreviated-related-objects {
|
||||
display: table;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.related-objects .abbreviated-related-objects ul {
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.related-objects .abbreviated-related-objects ul,
|
||||
.related-objects .all-related-objects ul {
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.related-objects .abbreviated-related-objects ul li {
|
||||
|
||||
display: inline-block;
|
||||
margin: 0.25em;
|
||||
padding: 0.25em;
|
||||
|
||||
border: 1px solid silver;
|
||||
background: #F5F5F5;
|
||||
-moz-border-radius: 0.25em;
|
||||
-webkit-border-radius: 0.25em;
|
||||
-khtml-border-radius: 0.25em;
|
||||
border-radius: 0.25em;
|
||||
|
||||
}
|
||||
|
||||
.related-objects .abbreviated-related-objects ul li img.remove {
|
||||
max-height: 0.75em;
|
||||
max-width: 0.75em;
|
||||
margin: 0 0.25em;
|
||||
}
|
||||
|
||||
.related-objects .abbreviated-related-objects ul li .identifier {
|
||||
margin: 0 0.25em;
|
||||
}
|
||||
|
||||
.related-objects .abbreviated-related-objects img.expand,
|
||||
.related-objects .abbreviated-related-objects img.collapse {
|
||||
display: table-cell;
|
||||
max-height: 1.5em;
|
||||
max-width: 1.5em;
|
||||
margin: 0.375em 0;
|
||||
}
|
||||
|
||||
.related-objects .all-related-objects {
|
||||
border-top: 1px solid silver;
|
||||
}
|
||||
|
||||
.related-objects .abbreviated-related-objects p.no-related-objects,
|
||||
.related-objects .all-related-objects p.no-objects-available {
|
||||
font-style: italic;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.related-objects .abbreviated-related-objects p.no-related-objects {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
<div class="related-objects" ng-hide="isEmpty()">
|
||||
<div class="header">
|
||||
<h2>{{ header | translate }}</h2>
|
||||
<div class="filter">
|
||||
<input class="search-string" type="text"
|
||||
placeholder="{{ 'SETTINGS_USERS.FIELD_PLACEHOLDER_FILTER' | translate }}"
|
||||
ng-model="filterString"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
|
||||
<!-- Abbreviated list of only the currently selected objects -->
|
||||
<div class="abbreviated-related-objects">
|
||||
<img src="images/arrows/right.png" alt="Expand" class="expand" ng-hide="expanded" ng-click="expand()"/>
|
||||
<img src="images/arrows/down.png" alt="Collapse" class="collapse" ng-show="expanded" ng-click="collapse()"/>
|
||||
<p ng-hide="identifiers.length" class="no-related-objects">{{ emptyPlaceholder | translate }}</p>
|
||||
<ul>
|
||||
<li ng-repeat="identifier in identifiers | filter: filterString">
|
||||
<label><img src="images/x-red.png" alt="Remove" class="remove"
|
||||
ng-click="removeIdentifier(identifier)"
|
||||
ng-show="isEditable[identifier]"/><span class="identifier">{{ identifier }}</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Exhaustive, paginated list of all objects -->
|
||||
<div class="all-related-objects" ng-show="expanded">
|
||||
<p ng-hide="identifiersAvailablePage.length" class="no-objects-available">{{ unavailablePlaceholder | translate }}</p>
|
||||
<ul>
|
||||
<li ng-repeat="identifier in identifiersAvailablePage">
|
||||
<label><input type="checkbox"
|
||||
ng-model="identifierFlags[identifier]"
|
||||
ng-change="identifierChanged(identifier)"/>
|
||||
<span class="identifier">{{ identifier }}</span>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- Pager controls for user list -->
|
||||
<guac-pager page="identifiersAvailablePage" page-size="25"
|
||||
items="identifiersAvailable | orderBy | filter: filterString"></guac-pager>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
@@ -56,6 +56,17 @@
|
||||
permissions-removed="permissionsRemoved">
|
||||
</system-permission-editor>
|
||||
|
||||
<!-- Parent group section -->
|
||||
<identifier-set-editor
|
||||
header="MANAGE_USER.SECTION_HEADER_USER_GROUPS"
|
||||
empty-placeholder="MANAGE_USER.HELP_NO_USER_GROUPS"
|
||||
unavailable-placeholder="MANAGE_USER.INFO_NO_USER_GROUPS_AVAILABLE"
|
||||
identifiers-available="availableGroups"
|
||||
identifiers="parentGroups"
|
||||
identifiers-added="parentGroupsAdded"
|
||||
identifiers-removed="parentGroupsRemoved">
|
||||
</identifier-set-editor>
|
||||
|
||||
<!-- Connection permissions section -->
|
||||
<connection-permission-editor ng-show="managementPermissions[dataSource].canChangePermissions"
|
||||
data-data-source="dataSource"
|
||||
|
@@ -0,0 +1,101 @@
|
||||
<div class="manage-user-group view" ng-class="{loading: !isLoaded()}">
|
||||
|
||||
<!-- User group header and data source tabs -->
|
||||
<div class="header tabbed">
|
||||
<h2>{{'MANAGE_USER_GROUP.SECTION_HEADER_EDIT_USER_GROUP' | translate}}</h2>
|
||||
<guac-user-menu></guac-user-menu>
|
||||
</div>
|
||||
<data-data-source-tabs ng-hide="cloneSourceIdentifier"
|
||||
permissions="managementPermissions"
|
||||
url="getUserGroupURL(dataSource)">
|
||||
</data-data-source-tabs>
|
||||
|
||||
<!-- Warn if user group is read-only -->
|
||||
<div class="section" ng-hide="managementPermissions[dataSource].canSaveObject">
|
||||
<p class="notice read-only">{{'MANAGE_USER_GROUP.INFO_READ_ONLY' | translate}}</p>
|
||||
</div>
|
||||
|
||||
<!-- Sections applicable to non-read-only user groups -->
|
||||
<div ng-show="managementPermissions[dataSource].canSaveObject">
|
||||
|
||||
<!-- User group name -->
|
||||
<div class="section">
|
||||
<table class="properties">
|
||||
<tr>
|
||||
<th>{{'MANAGE_USER_GROUP.FIELD_HEADER_USER_GROUP_NAME' | translate}}</th>
|
||||
<td>
|
||||
<input ng-show="canEditIdentifier()" ng-model="userGroup.identifier" type="text"/>
|
||||
<span ng-hide="canEditIdentifier()">{{userGroup.identifier}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- User group attributes section -->
|
||||
<div class="attributes" ng-show="managementPermissions[dataSource].canChangeAttributes">
|
||||
<guac-form namespace="'USER_GROUP_ATTRIBUTES'" content="attributes"
|
||||
model="userGroup.attributes"
|
||||
model-only="!managementPermissions[dataSource].canChangeAllAttributes"></guac-form>
|
||||
</div>
|
||||
|
||||
<!-- System permissions section -->
|
||||
<system-permission-editor ng-show="managementPermissions[dataSource].canChangePermissions"
|
||||
data-data-source="dataSource"
|
||||
permission-flags="permissionFlags"
|
||||
permissions-added="permissionsAdded"
|
||||
permissions-removed="permissionsRemoved">
|
||||
</system-permission-editor>
|
||||
|
||||
<!-- Parent group section -->
|
||||
<identifier-set-editor
|
||||
header="MANAGE_USER_GROUP.SECTION_HEADER_USER_GROUPS"
|
||||
empty-placeholder="MANAGE_USER_GROUP.HELP_NO_USER_GROUPS"
|
||||
unavailable-placeholder="MANAGE_USER_GROUP.INFO_NO_USER_GROUPS_AVAILABLE"
|
||||
identifiers-available="availableGroups"
|
||||
identifiers="parentGroups"
|
||||
identifiers-added="parentGroupsAdded"
|
||||
identifiers-removed="parentGroupsRemoved">
|
||||
</identifier-set-editor>
|
||||
|
||||
<!-- Member group section -->
|
||||
<identifier-set-editor
|
||||
header="MANAGE_USER_GROUP.SECTION_HEADER_MEMBER_USER_GROUPS"
|
||||
empty-placeholder="MANAGE_USER_GROUP.HELP_NO_MEMBER_USER_GROUPS"
|
||||
unavailable-placeholder="MANAGE_USER_GROUP.INFO_NO_USER_GROUPS_AVAILABLE"
|
||||
identifiers-available="availableGroups"
|
||||
identifiers="memberGroups"
|
||||
identifiers-added="memberGroupsAdded"
|
||||
identifiers-removed="memberGroupsRemoved">
|
||||
</identifier-set-editor>
|
||||
|
||||
<!-- Member user section -->
|
||||
<identifier-set-editor
|
||||
header="MANAGE_USER_GROUP.SECTION_HEADER_MEMBER_USERS"
|
||||
empty-placeholder="MANAGE_USER_GROUP.HELP_NO_MEMBER_USERS"
|
||||
unavailable-placeholder="MANAGE_USER_GROUP.INFO_NO_USERS_AVAILABLE"
|
||||
identifiers-available="availableUsers"
|
||||
identifiers="memberUsers"
|
||||
identifiers-added="memberUsersAdded"
|
||||
identifiers-removed="memberUsersRemoved">
|
||||
</identifier-set-editor>
|
||||
|
||||
<!-- Connection permissions section -->
|
||||
<connection-permission-editor ng-show="managementPermissions[dataSource].canChangePermissions"
|
||||
data-data-source="dataSource"
|
||||
permission-flags="permissionFlags"
|
||||
permissions-added="permissionsAdded"
|
||||
permissions-removed="permissionsRemoved">
|
||||
</connection-permission-editor>
|
||||
|
||||
<!-- Form action buttons -->
|
||||
<management-buttons namespace="MANAGE_USER_GROUP"
|
||||
permissions="managementPermissions[dataSource]"
|
||||
save="saveUserGroup()"
|
||||
delete="deleteUserGroup()"
|
||||
clone="cloneUserGroup()"
|
||||
return="returnToUserGroupList()">
|
||||
</management-buttons>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A service for defining the ManageableUserGroup class.
|
||||
*/
|
||||
angular.module('manage').factory('ManageableUserGroup', [function defineManageableUserGroup() {
|
||||
|
||||
/**
|
||||
* A pairing of an @link{UserGroup} with the identifier of its corresponding
|
||||
* data source.
|
||||
*
|
||||
* @constructor
|
||||
* @param {Object|ManageableUserGroup} template
|
||||
*/
|
||||
var ManageableUserGroup = function ManageableUserGroup(template) {
|
||||
|
||||
/**
|
||||
* The unique identifier of the data source containing this user.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.dataSource = template.dataSource;
|
||||
|
||||
/**
|
||||
* The @link{UserGroup} object represented by this ManageableUserGroup
|
||||
* and contained within the associated data source.
|
||||
*
|
||||
* @type UserGroup
|
||||
*/
|
||||
this.userGroup = template.userGroup;
|
||||
|
||||
};
|
||||
|
||||
return ManageableUserGroup;
|
||||
|
||||
}]);
|
@@ -194,6 +194,7 @@ angular.module('navigation').factory('userPageService', ['$injector',
|
||||
var pages = [];
|
||||
|
||||
var canManageUsers = [];
|
||||
var canManageUserGroups = [];
|
||||
var canManageConnections = [];
|
||||
var canViewConnectionRecords = [];
|
||||
|
||||
@@ -236,6 +237,24 @@ angular.module('navigation').factory('userPageService', ['$injector',
|
||||
canManageUsers.push(dataSource);
|
||||
}
|
||||
|
||||
// Determine whether the current user needs access to the group management UI
|
||||
if (
|
||||
// System permissions
|
||||
PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.ADMINISTER)
|
||||
|| PermissionSet.hasSystemPermission(permissions, PermissionSet.SystemPermissionType.CREATE_USER_GROUP)
|
||||
|
||||
// Permission to update user groups
|
||||
|| PermissionSet.hasUserGroupPermission(permissions, PermissionSet.ObjectPermissionType.UPDATE)
|
||||
|
||||
// Permission to delete user groups
|
||||
|| PermissionSet.hasUserGroupPermission(permissions, PermissionSet.ObjectPermissionType.DELETE)
|
||||
|
||||
// Permission to administer user groups
|
||||
|| PermissionSet.hasUserGroupPermission(permissions, PermissionSet.ObjectPermissionType.ADMINISTER)
|
||||
) {
|
||||
canManageUserGroups.push(dataSource);
|
||||
}
|
||||
|
||||
// Determine whether the current user needs access to the connection management UI
|
||||
if (
|
||||
// System permissions
|
||||
@@ -293,6 +312,14 @@ angular.module('navigation').factory('userPageService', ['$injector',
|
||||
}));
|
||||
}
|
||||
|
||||
// If user can manage user groups, add link to group management page
|
||||
if (canManageUserGroups.length) {
|
||||
pages.push(new PageDefinition({
|
||||
name : 'USER_MENU.ACTION_MANAGE_USER_GROUPS',
|
||||
url : '/settings/userGroups'
|
||||
}));
|
||||
}
|
||||
|
||||
// If user can manage connections, add links for connection management pages
|
||||
angular.forEach(canManageConnections, function addConnectionManagementLink(dataSource) {
|
||||
pages.push(new PageDefinition({
|
||||
|
@@ -60,7 +60,8 @@ angular.module('rest').factory('cacheService', ['$injector',
|
||||
service.schema = $cacheFactory('API-SCHEMA');
|
||||
|
||||
/**
|
||||
* Shared cache used by both userService and permissionService.
|
||||
* Shared cache used by userService, userGroupService, permissionService,
|
||||
* and membershipService.
|
||||
*
|
||||
* @type $cacheFactory.Cache
|
||||
*/
|
||||
|
385
guacamole/src/main/webapp/app/rest/services/membershipService.js
Normal file
385
guacamole/src/main/webapp/app/rest/services/membershipService.js
Normal file
@@ -0,0 +1,385 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Service for operating on user group memberships via the REST API.
|
||||
*/
|
||||
angular.module('rest').factory('membershipService', ['$injector',
|
||||
function membershipService($injector) {
|
||||
|
||||
// Required services
|
||||
var requestService = $injector.get('requestService');
|
||||
var authenticationService = $injector.get('authenticationService');
|
||||
var cacheService = $injector.get('cacheService');
|
||||
|
||||
// Required types
|
||||
var RelatedObjectPatch = $injector.get('RelatedObjectPatch');
|
||||
|
||||
var service = {};
|
||||
|
||||
/**
|
||||
* Creates a new array of patches which represents the given changes to an
|
||||
* arbitrary set of objects sharing some common relation.
|
||||
*
|
||||
* @param {String[]} [identifiersToAdd]
|
||||
* The identifiers of all objects which should be added to the
|
||||
* relation, if any.
|
||||
*
|
||||
* @param {String[]} [identifiersToRemove]
|
||||
* The identifiers of all objects which should be removed from the
|
||||
* relation, if any.
|
||||
*
|
||||
* @returns {RelatedObjectPatch[]}
|
||||
* A new array of patches which represents the given changes.
|
||||
*/
|
||||
var getRelatedObjectPatch = function getRelatedObjectPatch(identifiersToAdd, identifiersToRemove) {
|
||||
|
||||
var patch = [];
|
||||
|
||||
angular.forEach(identifiersToAdd, function addIdentifier(identifier) {
|
||||
patch.push(new RelatedObjectPatch({
|
||||
op : RelatedObjectPatch.Operation.ADD,
|
||||
value : identifier
|
||||
}));
|
||||
});
|
||||
|
||||
angular.forEach(identifiersToRemove, function removeIdentifier(identifier) {
|
||||
patch.push(new RelatedObjectPatch({
|
||||
op : RelatedObjectPatch.Operation.REMOVE,
|
||||
value : identifier
|
||||
}));
|
||||
});
|
||||
|
||||
return patch;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the URL for the REST resource most appropriate for accessing
|
||||
* the parent user groups of the user or group having the given identifier.
|
||||
*
|
||||
* It is important to note that a particular data source can authenticate
|
||||
* and provide user groups for a user, even if that user does not exist
|
||||
* within that data source (and thus cannot be found beneath
|
||||
* "api/session/data/{dataSource}/users")
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user or
|
||||
* group whose parent user groups should be retrieved. This identifier
|
||||
* corresponds to an AuthenticationProvider within the Guacamole web
|
||||
* application.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier of the user or group for which the URL of the proper
|
||||
* REST resource should be derived.
|
||||
*
|
||||
* @param {Boolean} [group]
|
||||
* Whether the provided identifier refers to a user group. If false or
|
||||
* omitted, the identifier given is assumed to refer to a user.
|
||||
*
|
||||
* @returns {String}
|
||||
* The URL for the REST resource representing the parent user groups of
|
||||
* the user or group having the given identifier.
|
||||
*/
|
||||
var getUserGroupsResourceURL = function getUserGroupsResourceURL(dataSource, identifier, group) {
|
||||
|
||||
// Create base URL for data source
|
||||
var base = 'api/session/data/' + encodeURIComponent(dataSource);
|
||||
|
||||
// Access parent groups directly (there is no "self" for user groups
|
||||
// as there is for users)
|
||||
if (group)
|
||||
return base + '/userGroups/' + encodeURIComponent(identifier) + '/userGroups';
|
||||
|
||||
// If the username is that of the current user, do not rely on the
|
||||
// user actually existing (they may not). Access their parent groups via
|
||||
// "self" rather than the collection of defined users.
|
||||
if (identifier === authenticationService.getCurrentUsername())
|
||||
return base + '/self/userGroups';
|
||||
|
||||
// Otherwise, the user must exist for their parent groups to be
|
||||
// accessible. Use the collection of defined users.
|
||||
return base + '/users/' + encodeURIComponent(identifier) + '/userGroups';
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to retrieve the identifiers of all
|
||||
* parent user groups of which a given user or group is a member, returning
|
||||
* a promise that can be used for processing the results of the call.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user or
|
||||
* group whose parent user groups should be retrieved. This identifier
|
||||
* corresponds to an AuthenticationProvider within the Guacamole web
|
||||
* application.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier of the user or group to retrieve the parent user
|
||||
* groups of.
|
||||
*
|
||||
* @param {Boolean} [group]
|
||||
* Whether the provided identifier refers to a user group. If false or
|
||||
* omitted, the identifier given is assumed to refer to a user.
|
||||
*
|
||||
* @returns {Promise.<String[]>}
|
||||
* A promise for the HTTP call which will resolve with an array
|
||||
* containing the requested identifiers upon success.
|
||||
*/
|
||||
service.getUserGroups = function getUserGroups(dataSource, identifier, group) {
|
||||
|
||||
// Build HTTP parameters set
|
||||
var httpParameters = {
|
||||
token : authenticationService.getCurrentToken()
|
||||
};
|
||||
|
||||
// Retrieve parent groups
|
||||
return requestService({
|
||||
cache : cacheService.users,
|
||||
method : 'GET',
|
||||
url : getUserGroupsResourceURL(dataSource, identifier, group),
|
||||
params : httpParameters
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to modify the parent user groups of
|
||||
* which a given user or group is a member, returning a promise that can be
|
||||
* used for processing the results of the call.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user or
|
||||
* group whose parent user groups should be modified. This identifier
|
||||
* corresponds to an AuthenticationProvider within the Guacamole web
|
||||
* application.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier of the user or group to modify the parent user
|
||||
* groups of.
|
||||
*
|
||||
* @param {String[]} [addToUserGroups]
|
||||
* The identifier of all parent user groups to which the given user or
|
||||
* group should be added as a member, if any.
|
||||
*
|
||||
* @param {String[]} [removeFromUserGroups]
|
||||
* The identifier of all parent user groups from which the given member
|
||||
* user or group should be removed, if any.
|
||||
*
|
||||
* @param {Boolean} [group]
|
||||
* Whether the provided identifier refers to a user group. If false or
|
||||
* omitted, the identifier given is assumed to refer to a user.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* A promise for the HTTP call which will succeed if and only if the
|
||||
* patch operation is successful.
|
||||
*/
|
||||
service.patchUserGroups = function patchUserGroups(dataSource, identifier,
|
||||
addToUserGroups, removeFromUserGroups, group) {
|
||||
|
||||
// Build HTTP parameters set
|
||||
var httpParameters = {
|
||||
token : authenticationService.getCurrentToken()
|
||||
};
|
||||
|
||||
// Update parent user groups
|
||||
return requestService({
|
||||
method : 'PATCH',
|
||||
url : getUserGroupsResourceURL(dataSource, identifier, group),
|
||||
params : httpParameters,
|
||||
data : getRelatedObjectPatch(addToUserGroups, removeFromUserGroups)
|
||||
})
|
||||
|
||||
// Clear the cache
|
||||
.then(function parentUserGroupsChanged(){
|
||||
cacheService.users.removeAll();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to retrieve the identifiers of all
|
||||
* users which are members of the given user group, returning a promise
|
||||
* that can be used for processing the results of the call.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user group
|
||||
* whose member users should be retrieved. This identifier corresponds
|
||||
* to an AuthenticationProvider within the Guacamole web application.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier of the user group to retrieve the member users of.
|
||||
*
|
||||
* @returns {Promise.<String[]>}
|
||||
* A promise for the HTTP call which will resolve with an array
|
||||
* containing the requested identifiers upon success.
|
||||
*/
|
||||
service.getMemberUsers = function getMemberUsers(dataSource, identifier) {
|
||||
|
||||
// Build HTTP parameters set
|
||||
var httpParameters = {
|
||||
token : authenticationService.getCurrentToken()
|
||||
};
|
||||
|
||||
// Retrieve member users
|
||||
return requestService({
|
||||
cache : cacheService.users,
|
||||
method : 'GET',
|
||||
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/userGroups/' + encodeURIComponent(identifier) + '/memberUsers',
|
||||
params : httpParameters
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to modify the member users of a given
|
||||
* user group, returning a promise that can be used for processing the
|
||||
* results of the call.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user group
|
||||
* whose member users should be modified. This identifier corresponds
|
||||
* to an AuthenticationProvider within the Guacamole web application.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier of the user group to modify the member users of.
|
||||
*
|
||||
* @param {String[]} [usersToAdd]
|
||||
* The identifier of all users to add as members of the given user
|
||||
* group, if any.
|
||||
*
|
||||
* @param {String[]} [usersToRemove]
|
||||
* The identifier of all users to remove from the given user group,
|
||||
* if any.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* A promise for the HTTP call which will succeed if and only if the
|
||||
* patch operation is successful.
|
||||
*/
|
||||
service.patchMemberUsers = function patchMemberUsers(dataSource, identifier,
|
||||
usersToAdd, usersToRemove) {
|
||||
|
||||
// Build HTTP parameters set
|
||||
var httpParameters = {
|
||||
token : authenticationService.getCurrentToken()
|
||||
};
|
||||
|
||||
// Update member users
|
||||
return requestService({
|
||||
method : 'PATCH',
|
||||
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/userGroups/' + encodeURIComponent(identifier) + '/memberUsers',
|
||||
params : httpParameters,
|
||||
data : getRelatedObjectPatch(usersToAdd, usersToRemove)
|
||||
})
|
||||
|
||||
// Clear the cache
|
||||
.then(function memberUsersChanged(){
|
||||
cacheService.users.removeAll();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to retrieve the identifiers of all
|
||||
* user groups which are members of the given user group, returning a
|
||||
* promise that can be used for processing the results of the call.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user group
|
||||
* whose member user groups should be retrieved. This identifier
|
||||
* corresponds to an AuthenticationProvider within the Guacamole web
|
||||
* application.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier of the user group to retrieve the member user
|
||||
* groups of.
|
||||
*
|
||||
* @returns {Promise.<String[]>}
|
||||
* A promise for the HTTP call which will resolve with an array
|
||||
* containing the requested identifiers upon success.
|
||||
*/
|
||||
service.getMemberUserGroups = function getMemberUserGroups(dataSource, identifier) {
|
||||
|
||||
// Build HTTP parameters set
|
||||
var httpParameters = {
|
||||
token : authenticationService.getCurrentToken()
|
||||
};
|
||||
|
||||
// Retrieve member user groups
|
||||
return requestService({
|
||||
cache : cacheService.users,
|
||||
method : 'GET',
|
||||
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/userGroups/' + encodeURIComponent(identifier) + '/memberUserGroups',
|
||||
params : httpParameters
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to modify the member user groups of a
|
||||
* given user group, returning a promise that can be used for processing
|
||||
* the results of the call.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user group
|
||||
* whose member user groups should be modified. This identifier
|
||||
* corresponds to an AuthenticationProvider within the Guacamole web
|
||||
* application.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier of the user group to modify the member user groups of.
|
||||
*
|
||||
* @param {String[]} [userGroupsToAdd]
|
||||
* The identifier of all user groups to add as members of the given
|
||||
* user group, if any.
|
||||
*
|
||||
* @param {String[]} [userGroupsToRemove]
|
||||
* The identifier of all member user groups to remove from the given
|
||||
* user group, if any.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* A promise for the HTTP call which will succeed if and only if the
|
||||
* patch operation is successful.
|
||||
*/
|
||||
service.patchMemberUserGroups = function patchMemberUserGroups(dataSource,
|
||||
identifier, userGroupsToAdd, userGroupsToRemove) {
|
||||
|
||||
// Build HTTP parameters set
|
||||
var httpParameters = {
|
||||
token : authenticationService.getCurrentToken()
|
||||
};
|
||||
|
||||
// Update member user groups
|
||||
return requestService({
|
||||
method : 'PATCH',
|
||||
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/userGroups/' + encodeURIComponent(identifier) + '/memberUserGroups',
|
||||
params : httpParameters,
|
||||
data : getRelatedObjectPatch(userGroupsToAdd, userGroupsToRemove)
|
||||
})
|
||||
|
||||
// Clear the cache
|
||||
.then(function memberUserGroupsChanged(){
|
||||
cacheService.users.removeAll();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
}]);
|
@@ -45,6 +45,11 @@ angular.module('rest').factory('permissionService', ['$injector',
|
||||
* within that data source (and thus cannot be found beneath
|
||||
* "api/session/data/{dataSource}/users")
|
||||
*
|
||||
* NOTE: Unlike getPermissionsResourceURL(),
|
||||
* getEffectivePermissionsResourceURL() CANNOT be applied to user groups.
|
||||
* Only users have retrievable effective permissions as far as the REST API
|
||||
* is concerned.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user whose
|
||||
* permissions should be retrieved. This identifier corresponds to an
|
||||
@@ -82,6 +87,10 @@ angular.module('rest').factory('permissionService', ['$injector',
|
||||
* from the permissions returned via getPermissions() in that permissions
|
||||
* which are not directly granted to the user are included.
|
||||
*
|
||||
* NOTE: Unlike getPermissions(), getEffectivePermissions() CANNOT be
|
||||
* applied to user groups. Only users have retrievable effective
|
||||
* permissions as far as the REST API is concerned.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user whose
|
||||
* permissions should be retrieved. This identifier corresponds to an
|
||||
@@ -113,10 +122,10 @@ angular.module('rest').factory('permissionService', ['$injector',
|
||||
|
||||
/**
|
||||
* Returns the URL for the REST resource most appropriate for accessing
|
||||
* the permissions of the user having the given identifier. The permissions
|
||||
* retrieved differ from effective permissions (those returned by
|
||||
* getEffectivePermissions()) in that only permissions which are directly
|
||||
* granted to the user are included.
|
||||
* the permissions of the user or group having the given identifier. The
|
||||
* permissions retrieved differ from effective permissions (those returned
|
||||
* by getEffectivePermissions()) in that only permissions which are directly
|
||||
* granted to the user or group are included.
|
||||
*
|
||||
* It is important to note that a particular data source can authenticate
|
||||
* and provide permissions for a user, even if that user does not exist
|
||||
@@ -129,18 +138,27 @@ angular.module('rest').factory('permissionService', ['$injector',
|
||||
* AuthenticationProvider within the Guacamole web application.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier of the user for which the URL of the proper REST
|
||||
* resource should be derived.
|
||||
* The identifier of the user or group for which the URL of the proper
|
||||
* REST resource should be derived.
|
||||
*
|
||||
* @param {Boolean} [group]
|
||||
* Whether the provided identifier refers to a user group. If false or
|
||||
* omitted, the identifier given is assumed to refer to a user.
|
||||
*
|
||||
* @returns {String}
|
||||
* The URL for the REST resource representing the user having the given
|
||||
* identifier.
|
||||
* The URL for the REST resource representing the user or group having
|
||||
* the given identifier.
|
||||
*/
|
||||
var getPermissionsResourceURL = function getPermissionsResourceURL(dataSource, identifier) {
|
||||
var getPermissionsResourceURL = function getPermissionsResourceURL(dataSource, identifier, group) {
|
||||
|
||||
// Create base URL for data source
|
||||
var base = 'api/session/data/' + encodeURIComponent(dataSource);
|
||||
|
||||
// Access group permissions directly (there is no "self" for user groups
|
||||
// as there is for users)
|
||||
if (group)
|
||||
return base + '/userGroups/' + encodeURIComponent(identifier) + '/permissions';
|
||||
|
||||
// If the username is that of the current user, do not rely on the
|
||||
// user actually existing (they may not). Access their permissions via
|
||||
// "self" rather than the collection of defined users.
|
||||
@@ -155,36 +173,41 @@ angular.module('rest').factory('permissionService', ['$injector',
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to get the list of permissions for a
|
||||
* given user, returning a promise that provides an array of
|
||||
* given user or user group, returning a promise that provides an array of
|
||||
* @link{Permission} objects if successful. The permissions retrieved
|
||||
* differ from effective permissions (those returned by
|
||||
* getEffectivePermissions()) in that only permissions which are directly
|
||||
* granted to the user included.
|
||||
* getEffectivePermissions()) in that both users and groups may be queried,
|
||||
* and only permissions which are directly granted to the user or group are
|
||||
* included.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user whose
|
||||
* permissions should be retrieved. This identifier corresponds to an
|
||||
* AuthenticationProvider within the Guacamole web application.
|
||||
* The unique identifier of the data source containing the user or group
|
||||
* whose permissions should be retrieved. This identifier corresponds to
|
||||
* an AuthenticationProvider within the Guacamole web application.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier of the user to retrieve the permissions for.
|
||||
* The identifier of the user or group to retrieve the permissions for.
|
||||
*
|
||||
* @param {Boolean} [group]
|
||||
* Whether the provided identifier refers to a user group. If false or
|
||||
* omitted, the identifier given is assumed to refer to a user.
|
||||
*
|
||||
* @returns {Promise.<PermissionSet>}
|
||||
* A promise which will resolve with a @link{PermissionSet} upon
|
||||
* success.
|
||||
*/
|
||||
service.getPermissions = function getPermissions(dataSource, identifier) {
|
||||
service.getPermissions = function getPermissions(dataSource, identifier, group) {
|
||||
|
||||
// Build HTTP parameters set
|
||||
var httpParameters = {
|
||||
token : authenticationService.getCurrentToken()
|
||||
};
|
||||
|
||||
// Retrieve user permissions
|
||||
// Retrieve user/group permissions
|
||||
return requestService({
|
||||
cache : cacheService.users,
|
||||
method : 'GET',
|
||||
url : getPermissionsResourceURL(dataSource, identifier),
|
||||
url : getPermissionsResourceURL(dataSource, identifier, group),
|
||||
params : httpParameters
|
||||
});
|
||||
|
||||
@@ -261,6 +284,10 @@ angular.module('rest').factory('permissionService', ['$injector',
|
||||
addObjectPatchOperations(patch, operation, "/userPermissions",
|
||||
permissions.userPermissions);
|
||||
|
||||
// Add user group permission operations to patch
|
||||
addObjectPatchOperations(patch, operation, "/userGroupPermissions",
|
||||
permissions.userGroupPermissions);
|
||||
|
||||
// Add system operations to patch
|
||||
permissions.systemPermissions.forEach(function addSystemPatch(type) {
|
||||
patch.push({
|
||||
@@ -274,18 +301,18 @@ angular.module('rest').factory('permissionService', ['$injector',
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to modify the permissions for a given
|
||||
* user, returning a promise that can be used for processing the results of
|
||||
* the call. This request affects only the permissions directly granted to
|
||||
* the user, and may not affect permissions inherited through other means
|
||||
* (effective permissions).
|
||||
* user or group, returning a promise that can be used for processing the
|
||||
* results of the call. This request affects only the permissions directly
|
||||
* granted to the user or group, and may not affect permissions inherited
|
||||
* through other means (effective permissions).
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user whose
|
||||
* permissions should be modified. This identifier corresponds to an
|
||||
* AuthenticationProvider within the Guacamole web application.
|
||||
* The unique identifier of the data source containing the user or group
|
||||
* whose permissions should be modified. This identifier corresponds to
|
||||
* an AuthenticationProvider within the Guacamole web application.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier of the user to modify the permissions of.
|
||||
* The identifier of the user or group to modify the permissions of.
|
||||
*
|
||||
* @param {PermissionSet} [permissionsToAdd]
|
||||
* The set of permissions to add, if any.
|
||||
@@ -293,12 +320,16 @@ angular.module('rest').factory('permissionService', ['$injector',
|
||||
* @param {PermissionSet} [permissionsToRemove]
|
||||
* The set of permissions to remove, if any.
|
||||
*
|
||||
* @param {Boolean} [group]
|
||||
* Whether the provided identifier refers to a user group. If false or
|
||||
* omitted, the identifier given is assumed to refer to a user.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* A promise for the HTTP call which will succeed if and only if the
|
||||
* patch operation is successful.
|
||||
*/
|
||||
service.patchPermissions = function patchPermissions(dataSource, identifier,
|
||||
permissionsToAdd, permissionsToRemove) {
|
||||
permissionsToAdd, permissionsToRemove, group) {
|
||||
|
||||
var permissionPatch = [];
|
||||
|
||||
@@ -313,10 +344,10 @@ angular.module('rest').factory('permissionService', ['$injector',
|
||||
// Add all the remove operations to the patch
|
||||
addPatchOperations(permissionPatch, PermissionPatch.Operation.REMOVE, permissionsToRemove);
|
||||
|
||||
// Patch user permissions
|
||||
// Patch user/group permissions
|
||||
return requestService({
|
||||
method : 'PATCH',
|
||||
url : getPermissionsResourceURL(dataSource, identifier),
|
||||
url : getPermissionsResourceURL(dataSource, identifier, group),
|
||||
params : httpParameters,
|
||||
data : permissionPatch
|
||||
})
|
||||
|
@@ -64,6 +64,40 @@ angular.module('rest').factory('schemaService', ['$injector',
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to get the list of available attributes
|
||||
* for user group objects, returning a promise that provides an array of
|
||||
* @link{Form} objects if successful. Each element of the array describes
|
||||
* a logical grouping of possible attributes.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user groups
|
||||
* whose available attributes are to be retrieved. This identifier
|
||||
* corresponds to an AuthenticationProvider within the Guacamole web
|
||||
* application.
|
||||
*
|
||||
* @returns {Promise.<Form[]>}
|
||||
* A promise which will resolve with an array of @link{Form}
|
||||
* objects, where each @link{Form} describes a logical grouping of
|
||||
* possible attributes.
|
||||
*/
|
||||
service.getUserGroupAttributes = function getUserGroupAttributes(dataSource) {
|
||||
|
||||
// Build HTTP parameters set
|
||||
var httpParameters = {
|
||||
token : authenticationService.getCurrentToken()
|
||||
};
|
||||
|
||||
// Retrieve available user group attributes
|
||||
return requestService({
|
||||
cache : cacheService.schema,
|
||||
method : 'GET',
|
||||
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/schema/userGroupAttributes',
|
||||
params : httpParameters
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to get the list of available attributes
|
||||
* for connection objects, returning a promise that provides an array of
|
||||
|
223
guacamole/src/main/webapp/app/rest/services/userGroupService.js
Normal file
223
guacamole/src/main/webapp/app/rest/services/userGroupService.js
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Service for operating on user groups via the REST API.
|
||||
*/
|
||||
angular.module('rest').factory('userGroupService', ['$injector',
|
||||
function userGroupService($injector) {
|
||||
|
||||
// Required services
|
||||
var requestService = $injector.get('requestService');
|
||||
var authenticationService = $injector.get('authenticationService');
|
||||
var cacheService = $injector.get('cacheService');
|
||||
|
||||
var service = {};
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to get the list of user groups,
|
||||
* returning a promise that provides an array of @link{UserGroup} objects if
|
||||
* successful.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user groups
|
||||
* to be retrieved. This identifier corresponds to an
|
||||
* AuthenticationProvider within the Guacamole web application.
|
||||
*
|
||||
* @param {String[]} [permissionTypes]
|
||||
* The set of permissions to filter with. A user group must have one or
|
||||
* more of these permissions for a user group to appear in the result.
|
||||
* If null, no filtering will be performed. Valid values are listed
|
||||
* within PermissionSet.ObjectType.
|
||||
*
|
||||
* @returns {Promise.<Object.<String, UserGroup>>}
|
||||
* A promise which will resolve with a map of @link{UserGroup} objects
|
||||
* where each key is the identifier of the corresponding user group.
|
||||
*/
|
||||
service.getUserGroups = function getUserGroups(dataSource, permissionTypes) {
|
||||
|
||||
// Build HTTP parameters set
|
||||
var httpParameters = {
|
||||
token : authenticationService.getCurrentToken()
|
||||
};
|
||||
|
||||
// Add permission filter if specified
|
||||
if (permissionTypes)
|
||||
httpParameters.permission = permissionTypes;
|
||||
|
||||
// Retrieve user groups
|
||||
return requestService({
|
||||
cache : cacheService.users,
|
||||
method : 'GET',
|
||||
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/userGroups',
|
||||
params : httpParameters
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to get the user group having the given
|
||||
* identifier, returning a promise that provides the corresponding
|
||||
* @link{UserGroup} if successful.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user group to
|
||||
* be retrieved. This identifier corresponds to an
|
||||
* AuthenticationProvider within the Guacamole web application.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier of the user group to retrieve.
|
||||
*
|
||||
* @returns {Promise.<UserGroup>}
|
||||
* A promise which will resolve with a @link{UserGroup} upon success.
|
||||
*/
|
||||
service.getUserGroup = function getUserGroup(dataSource, identifier) {
|
||||
|
||||
// Build HTTP parameters set
|
||||
var httpParameters = {
|
||||
token : authenticationService.getCurrentToken()
|
||||
};
|
||||
|
||||
// Retrieve user group
|
||||
return requestService({
|
||||
cache : cacheService.users,
|
||||
method : 'GET',
|
||||
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/userGroups/' + encodeURIComponent(identifier),
|
||||
params : httpParameters
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to delete a user group, returning a
|
||||
* promise that can be used for processing the results of the call.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user group to
|
||||
* be deleted. This identifier corresponds to an AuthenticationProvider
|
||||
* within the Guacamole web application.
|
||||
*
|
||||
* @param {UserGroup} userGroup
|
||||
* The user group to delete.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* A promise for the HTTP call which will succeed if and only if the
|
||||
* delete operation is successful.
|
||||
*/
|
||||
service.deleteUserGroup = function deleteUserGroup(dataSource, userGroup) {
|
||||
|
||||
// Build HTTP parameters set
|
||||
var httpParameters = {
|
||||
token : authenticationService.getCurrentToken()
|
||||
};
|
||||
|
||||
// Delete user group
|
||||
return requestService({
|
||||
method : 'DELETE',
|
||||
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/userGroups/' + encodeURIComponent(userGroup.identifier),
|
||||
params : httpParameters
|
||||
})
|
||||
|
||||
// Clear the cache
|
||||
.then(function userGroupDeleted(){
|
||||
cacheService.users.removeAll();
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to create a user group, returning a promise
|
||||
* that can be used for processing the results of the call.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source in which the user group
|
||||
* should be created. This identifier corresponds to an
|
||||
* AuthenticationProvider within the Guacamole web application.
|
||||
*
|
||||
* @param {UserGroup} userGroup
|
||||
* The user group to create.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* A promise for the HTTP call which will succeed if and only if the
|
||||
* create operation is successful.
|
||||
*/
|
||||
service.createUserGroup = function createUserGroup(dataSource, userGroup) {
|
||||
|
||||
// Build HTTP parameters set
|
||||
var httpParameters = {
|
||||
token : authenticationService.getCurrentToken()
|
||||
};
|
||||
|
||||
// Create user group
|
||||
return requestService({
|
||||
method : 'POST',
|
||||
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/userGroups',
|
||||
params : httpParameters,
|
||||
data : userGroup
|
||||
})
|
||||
|
||||
// Clear the cache
|
||||
.then(function userGroupCreated(){
|
||||
cacheService.users.removeAll();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a request to the REST API to save a user group, returning a
|
||||
* promise that can be used for processing the results of the call.
|
||||
*
|
||||
* @param {String} dataSource
|
||||
* The unique identifier of the data source containing the user group to
|
||||
* be updated. This identifier corresponds to an AuthenticationProvider
|
||||
* within the Guacamole web application.
|
||||
*
|
||||
* @param {UserGroup} userGroup
|
||||
* The user group to update.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* A promise for the HTTP call which will succeed if and only if the
|
||||
* save operation is successful.
|
||||
*/
|
||||
service.saveUserGroup = function saveUserGroup(dataSource, userGroup) {
|
||||
|
||||
// Build HTTP parameters set
|
||||
var httpParameters = {
|
||||
token : authenticationService.getCurrentToken()
|
||||
};
|
||||
|
||||
// Update user group
|
||||
return requestService({
|
||||
method : 'PUT',
|
||||
url : 'api/session/data/' + encodeURIComponent(dataSource) + '/userGroups/' + encodeURIComponent(userGroup.identifier),
|
||||
params : httpParameters,
|
||||
data : userGroup
|
||||
})
|
||||
|
||||
// Clear the cache
|
||||
.then(function userGroupUpdated(){
|
||||
cacheService.users.removeAll();
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
}]);
|
@@ -84,7 +84,7 @@ angular.module('rest').factory('Connection', [function defineConnection() {
|
||||
*
|
||||
* @type Object.<String, String>
|
||||
*/
|
||||
this.attributes = {};
|
||||
this.attributes = template.attributes || {};
|
||||
|
||||
/**
|
||||
* The count of currently active connections using this connection.
|
||||
|
@@ -95,7 +95,7 @@ angular.module('rest').factory('ConnectionGroup', [function defineConnectionGrou
|
||||
*
|
||||
* @type Object.<String, String>
|
||||
*/
|
||||
this.attributes = {};
|
||||
this.attributes = template.attributes || {};
|
||||
|
||||
/**
|
||||
* The count of currently active connections using this connection
|
||||
|
@@ -133,7 +133,7 @@ angular.module('rest').factory('PermissionFlagSet', ['PermissionSet',
|
||||
* true. Valid permission type strings are defined within
|
||||
* PermissionSet.ObjectPermissionType. Permissions which are not
|
||||
* granted may be set to false, but this is not required.
|
||||
*
|
||||
*
|
||||
* @type Object.<String, Object.<String, Boolean>>
|
||||
*/
|
||||
this.userPermissions = template.userPermissions || {
|
||||
@@ -143,6 +143,24 @@ angular.module('rest').factory('PermissionFlagSet', ['PermissionSet',
|
||||
'ADMINISTER' : {}
|
||||
};
|
||||
|
||||
/**
|
||||
* The granted state of each permission for each user group, as a map of
|
||||
* object permission type string to permission map. The permission map
|
||||
* is, in turn, a map of group identifier to boolean value. A particular
|
||||
* permission is granted if its corresponding boolean value is set to
|
||||
* true. Valid permission type strings are defined within
|
||||
* PermissionSet.ObjectPermissionType. Permissions which are not
|
||||
* granted may be set to false, but this is not required.
|
||||
*
|
||||
* @type Object.<String, Object.<String, Boolean>>
|
||||
*/
|
||||
this.userGroupPermissions = template.userGroupPermissions || {
|
||||
'READ' : {},
|
||||
'UPDATE' : {},
|
||||
'DELETE' : {},
|
||||
'ADMINISTER' : {}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -216,6 +234,9 @@ angular.module('rest').factory('PermissionFlagSet', ['PermissionSet',
|
||||
// Add all granted user permissions
|
||||
addObjectPermissions(permissionSet.userPermissions, permissionFlagSet.userPermissions);
|
||||
|
||||
// Add all granted user group permissions
|
||||
addObjectPermissions(permissionSet.userGroupPermissions, permissionFlagSet.userGroupPermissions);
|
||||
|
||||
return permissionFlagSet;
|
||||
|
||||
};
|
||||
|
@@ -81,6 +81,15 @@ angular.module('rest').factory('PermissionSet', [function definePermissionSet()
|
||||
*/
|
||||
this.userPermissions = template.userPermissions || {};
|
||||
|
||||
/**
|
||||
* Map of user group identifiers to the corresponding array of granted
|
||||
* permissions. Each permission is represented by a string listed
|
||||
* within PermissionSet.ObjectPermissionType.
|
||||
*
|
||||
* @type Object.<String, String[]>
|
||||
*/
|
||||
this.userGroupPermissions = template.userGroupPermissions || {};
|
||||
|
||||
/**
|
||||
* Array of granted system permissions. Each permission is represented
|
||||
* by a string listed within PermissionSet.SystemPermissionType.
|
||||
@@ -306,7 +315,7 @@ angular.module('rest').factory('PermissionSet', [function definePermissionSet()
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether the given permission is granted for the user having the
|
||||
* Returns whether the given permission is granted for the user having the
|
||||
* given ID.
|
||||
*
|
||||
* @param {PermissionSet|Object} permSet
|
||||
@@ -315,7 +324,7 @@ angular.module('rest').factory('PermissionSet', [function definePermissionSet()
|
||||
* @param {String} type
|
||||
* The permission to search for, as defined by
|
||||
* PermissionSet.ObjectPermissionType.
|
||||
*
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier of the user to which the permission applies.
|
||||
*
|
||||
@@ -326,6 +335,27 @@ angular.module('rest').factory('PermissionSet', [function definePermissionSet()
|
||||
return hasPermission(permSet.userPermissions, type, identifier);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether the given permission is granted for the user group having
|
||||
* the given identifier.
|
||||
*
|
||||
* @param {PermissionSet|Object} permSet
|
||||
* The permission set to check.
|
||||
*
|
||||
* @param {String} type
|
||||
* The permission to search for, as defined by
|
||||
* PermissionSet.ObjectPermissionType.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier of the user group to which the permission applies.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* true if the permission is present (granted), false otherwise.
|
||||
*/
|
||||
PermissionSet.hasUserGroupPermission = function hasUserGroupPermission(permSet, type, identifier) {
|
||||
return hasPermission(permSet.userGroupPermissions, type, identifier);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether the given permission is granted at the system level.
|
||||
*
|
||||
@@ -733,6 +763,54 @@ angular.module('rest').factory('PermissionSet', [function definePermissionSet()
|
||||
return removeObjectPermission(permSet.userPermissions, type, identifier);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds the given user group permission applying to the user group with the
|
||||
* given identifier to the given permission set, if not already present. If
|
||||
* the permission is already present, this function has no effect.
|
||||
*
|
||||
* @param {PermissionSet} permSet
|
||||
* The permission set to modify.
|
||||
*
|
||||
* @param {String} type
|
||||
* The permission to add, as defined by
|
||||
* PermissionSet.ObjectPermissionType.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier of the user group to which the permission applies.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* true if the permission was added, false if the permission was
|
||||
* already present in the given permission set.
|
||||
*/
|
||||
PermissionSet.addUserGroupPermission = function addUserGroupPermission(permSet, type, identifier) {
|
||||
permSet.userGroupPermissions = permSet.userGroupPermissions || {};
|
||||
return addObjectPermission(permSet.userGroupPermissions, type, identifier);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the given user group permission applying to the user group with
|
||||
* the given identifier from the given permission set, if present. If the
|
||||
* permission is not present, this function has no effect.
|
||||
*
|
||||
* @param {PermissionSet} permSet
|
||||
* The permission set to modify.
|
||||
*
|
||||
* @param {String} type
|
||||
* The permission to remove, as defined by
|
||||
* PermissionSet.ObjectPermissionType.
|
||||
*
|
||||
* @param {String} identifier
|
||||
* The identifier of the user group to whom the permission applies.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* true if the permission was removed, false if the permission was not
|
||||
* present in the given permission set.
|
||||
*/
|
||||
PermissionSet.removeUserGroupPermission = function removeUserGroupPermission(permSet, type, identifier) {
|
||||
permSet.userGroupPermissions = permSet.userGroupPermissions || {};
|
||||
return removeObjectPermission(permSet.userGroupPermissions, type, identifier);
|
||||
};
|
||||
|
||||
return PermissionSet;
|
||||
|
||||
}]);
|
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Service which defines the RelatedObjectPatch class.
|
||||
*/
|
||||
angular.module('rest').factory('RelatedObjectPatch', [function defineRelatedObjectPatch() {
|
||||
|
||||
/**
|
||||
* The object returned by REST API calls when representing changes to an
|
||||
* arbitrary set of objects which share some common relation.
|
||||
*
|
||||
* @constructor
|
||||
* @param {RelatedObjectPatch|Object} [template={}]
|
||||
* The object whose properties should be copied within the new
|
||||
* RelatedObjectPatch.
|
||||
*/
|
||||
var RelatedObjectPatch = function RelatedObjectPatch(template) {
|
||||
|
||||
// Use empty object by default
|
||||
template = template || {};
|
||||
|
||||
/**
|
||||
* The operation to apply to the objects indicated by the path. Valid
|
||||
* operation values are defined within RelatedObjectPatch.Operation.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.op = template.op;
|
||||
|
||||
/**
|
||||
* The path of the objects to modify. This will always be "/".
|
||||
*
|
||||
* @type String
|
||||
* @default '/'
|
||||
*/
|
||||
this.path = template.path || '/';
|
||||
|
||||
/**
|
||||
* The identifier of the object being added or removed from the
|
||||
* relation.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.value = template.value;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* All valid patch operations for objects sharing some common relation.
|
||||
* Currently, only add and remove are supported.
|
||||
*/
|
||||
RelatedObjectPatch.Operation = {
|
||||
|
||||
/**
|
||||
* Adds the specified object to the relation.
|
||||
*/
|
||||
ADD : "add",
|
||||
|
||||
/**
|
||||
* Removes the specified object from the relation.
|
||||
*/
|
||||
REMOVE : "remove"
|
||||
|
||||
};
|
||||
|
||||
return RelatedObjectPatch;
|
||||
|
||||
}]);
|
@@ -76,7 +76,7 @@ angular.module('rest').factory('SharingProfile', [function defineSharingProfile(
|
||||
*
|
||||
* @type Object.<String, String>
|
||||
*/
|
||||
this.attributes = {};
|
||||
this.attributes = template.attributes || {};
|
||||
|
||||
};
|
||||
|
||||
|
@@ -69,7 +69,7 @@ angular.module('rest').factory('User', [function defineUser() {
|
||||
*
|
||||
* @type Object.<String, String>
|
||||
*/
|
||||
this.attributes = {};
|
||||
this.attributes = template.attributes || {};
|
||||
|
||||
};
|
||||
|
||||
|
59
guacamole/src/main/webapp/app/rest/types/UserGroup.js
Normal file
59
guacamole/src/main/webapp/app/rest/types/UserGroup.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Service which defines the UserGroup class.
|
||||
*/
|
||||
angular.module('rest').factory('UserGroup', [function defineUserGroup() {
|
||||
|
||||
/**
|
||||
* The object returned by REST API calls when representing the data
|
||||
* associated with a user group.
|
||||
*
|
||||
* @constructor
|
||||
* @param {UserGroup|Object} [template={}]
|
||||
* The object whose properties should be copied within the new
|
||||
* UserGroup.
|
||||
*/
|
||||
var UserGroup = function UserGroup(template) {
|
||||
|
||||
// Use empty object by default
|
||||
template = template || {};
|
||||
|
||||
/**
|
||||
* The name which uniquely identifies this user group.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.identifier = template.identifier;
|
||||
|
||||
/**
|
||||
* Arbitrary name/value pairs which further describe this user group.
|
||||
* The semantics and validity of these attributes are dictated by the
|
||||
* extension which defines them.
|
||||
*
|
||||
* @type Object.<String, String>
|
||||
*/
|
||||
this.attributes = template.attributes || {};
|
||||
|
||||
};
|
||||
|
||||
return UserGroup;
|
||||
|
||||
}]);
|
@@ -36,8 +36,8 @@ angular.module('manage').controller('settingsController', ['$scope', '$injector'
|
||||
$scope.settingsPages = null;
|
||||
|
||||
/**
|
||||
* The currently-selected settings tab. This may be 'users', 'connections',
|
||||
* or 'sessions'.
|
||||
* The currently-selected settings tab. This may be 'users', 'userGroups',
|
||||
* 'connections', 'history', 'preferences', or 'sessions'.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
|
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A directive for managing all user groups in the system.
|
||||
*/
|
||||
angular.module('settings').directive('guacSettingsUserGroups', ['$injector',
|
||||
function guacSettingsUserGroups($injector) {
|
||||
|
||||
// Required types
|
||||
var ManageableUserGroup = $injector.get('ManageableUserGroup');
|
||||
var PermissionSet = $injector.get('PermissionSet');
|
||||
var SortOrder = $injector.get('SortOrder');
|
||||
|
||||
// Required services
|
||||
var $location = $injector.get('$location');
|
||||
var authenticationService = $injector.get('authenticationService');
|
||||
var dataSourceService = $injector.get('dataSourceService');
|
||||
var permissionService = $injector.get('permissionService');
|
||||
var requestService = $injector.get('requestService');
|
||||
var userGroupService = $injector.get('userGroupService');
|
||||
|
||||
var directive = {
|
||||
restrict : 'E',
|
||||
replace : true,
|
||||
templateUrl : 'app/settings/templates/settingsUserGroups.html',
|
||||
scope : {}
|
||||
};
|
||||
|
||||
directive.controller = ['$scope', function settingsUserGroupsController($scope) {
|
||||
|
||||
// Identifier of the current user
|
||||
var currentUsername = authenticationService.getCurrentUsername();
|
||||
|
||||
/**
|
||||
* The identifiers of all data sources accessible by the current
|
||||
* user.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
var dataSources = authenticationService.getAvailableDataSources();
|
||||
|
||||
/**
|
||||
* Map of data source identifiers to all permissions associated
|
||||
* with the current user within that data source, or null if the
|
||||
* user's permissions have not yet been loaded.
|
||||
*
|
||||
* @type Object.<String, PermissionSet>
|
||||
*/
|
||||
var permissions = null;
|
||||
|
||||
/**
|
||||
* All visible user groups, along with their corresponding data
|
||||
* sources.
|
||||
*
|
||||
* @type ManageableUserGroup[]
|
||||
*/
|
||||
$scope.manageableUserGroups = null;
|
||||
|
||||
/**
|
||||
* Array of all user group properties that are filterable.
|
||||
*
|
||||
* @type String[]
|
||||
*/
|
||||
$scope.filteredUserGroupProperties = [
|
||||
'userGroup.identifier'
|
||||
];
|
||||
|
||||
/**
|
||||
* SortOrder instance which stores the sort order of the listed
|
||||
* user groups.
|
||||
*
|
||||
* @type SortOrder
|
||||
*/
|
||||
$scope.order = new SortOrder([
|
||||
'userGroup.identifier'
|
||||
]);
|
||||
|
||||
/**
|
||||
* Returns whether critical data has completed being loaded.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* true if enough data has been loaded for the user group
|
||||
* interface to be useful, false otherwise.
|
||||
*/
|
||||
$scope.isLoaded = function isLoaded() {
|
||||
return $scope.manageableUserGroups !== null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the identifier of the data source that should be used by
|
||||
* default when creating a new user group.
|
||||
*
|
||||
* @return {String}
|
||||
* The identifier of the data source that should be used by
|
||||
* default when creating a new user group, or null if user group
|
||||
* creation is not allowed.
|
||||
*/
|
||||
$scope.getDefaultDataSource = function getDefaultDataSource() {
|
||||
|
||||
// Abort if permissions have not yet loaded
|
||||
if (!permissions)
|
||||
return null;
|
||||
|
||||
// For each data source
|
||||
var dataSources = _.keys(permissions).sort();
|
||||
for (var i = 0; i < dataSources.length; i++) {
|
||||
|
||||
// Retrieve corresponding permission set
|
||||
var dataSource = dataSources[i];
|
||||
var permissionSet = permissions[dataSource];
|
||||
|
||||
// Can create user groups if adminstrator or have explicit permission
|
||||
if (PermissionSet.hasSystemPermission(permissionSet, PermissionSet.SystemPermissionType.ADMINISTER)
|
||||
|| PermissionSet.hasSystemPermission(permissionSet, PermissionSet.SystemPermissionType.CREATE_USER_GROUP))
|
||||
return dataSource;
|
||||
|
||||
}
|
||||
|
||||
// No data sources allow user group creation
|
||||
return null;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether the current user can create new user groups
|
||||
* within at least one data source.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* true if the current user can create new user groups within at
|
||||
* least one data source, false otherwise.
|
||||
*/
|
||||
$scope.canCreateUserGroups = function canCreateUserGroups() {
|
||||
return $scope.getDefaultDataSource() !== null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether the current user can create new user groups or
|
||||
* make changes to existing user groups within at least one data
|
||||
* source. The user group management interface as a whole is useless
|
||||
* if this function returns false.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* true if the current user can create new user groups or make
|
||||
* changes to existing user groups within at least one data
|
||||
* source, false otherwise.
|
||||
*/
|
||||
var canManageUserGroups = function canManageUserGroups() {
|
||||
|
||||
// Abort if permissions have not yet loaded
|
||||
if (!permissions)
|
||||
return false;
|
||||
|
||||
// Creating user groups counts as management
|
||||
if ($scope.canCreateUserGroups())
|
||||
return true;
|
||||
|
||||
// For each data source
|
||||
for (var dataSource in permissions) {
|
||||
|
||||
// Retrieve corresponding permission set
|
||||
var permissionSet = permissions[dataSource];
|
||||
|
||||
// Can manage user groups if granted explicit update or delete
|
||||
if (PermissionSet.hasUserGroupPermission(permissionSet, PermissionSet.ObjectPermissionType.UPDATE)
|
||||
|| PermissionSet.hasUserGroupPermission(permissionSet, PermissionSet.ObjectPermissionType.DELETE))
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// No data sources allow management of user groups
|
||||
return false;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the displayed list of user groups. If any user groups are
|
||||
* already shown within the interface, those user groups are replaced
|
||||
* with the given user groups.
|
||||
*
|
||||
* @param {Object.<String, PermissionSet>} permissions
|
||||
* A map of data source identifiers to all permissions associated
|
||||
* with the current user within that data source.
|
||||
*
|
||||
* @param {Object.<String, Object.<String, UserGroup>>} userGroups
|
||||
* A map of all user groups which should be displayed, where each
|
||||
* key is the data source identifier from which the user groups
|
||||
* were retrieved and each value is a map of user group identifiers
|
||||
* to their corresponding @link{UserGroup} objects.
|
||||
*/
|
||||
var setDisplayedUserGroups = function setDisplayedUserGroups(permissions, userGroups) {
|
||||
|
||||
var addedUserGroups = {};
|
||||
$scope.manageableUserGroups = [];
|
||||
|
||||
// For each user group in each data source
|
||||
angular.forEach(dataSources, function addUserGroupList(dataSource) {
|
||||
angular.forEach(userGroups[dataSource], function addUserGroup(userGroup) {
|
||||
|
||||
// Do not add the same user group twice
|
||||
if (addedUserGroups[userGroup.identifier])
|
||||
return;
|
||||
|
||||
// Link to default creation data source if we cannot manage this user
|
||||
if (!PermissionSet.hasSystemPermission(permissions[dataSource], PermissionSet.ObjectPermissionType.ADMINISTER)
|
||||
&& !PermissionSet.hasUserGroupPermission(permissions[dataSource], PermissionSet.ObjectPermissionType.UPDATE, userGroup.identifier)
|
||||
&& !PermissionSet.hasUserGroupPermission(permissions[dataSource], PermissionSet.ObjectPermissionType.DELETE, userGroup.identifier))
|
||||
dataSource = $scope.getDefaultDataSource();
|
||||
|
||||
// Add user group to overall list
|
||||
addedUserGroups[userGroup.identifier] = userGroup;
|
||||
$scope.manageableUserGroups.push(new ManageableUserGroup ({
|
||||
'dataSource' : dataSource,
|
||||
'userGroup' : userGroup
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// Retrieve current permissions
|
||||
dataSourceService.apply(
|
||||
permissionService.getEffectivePermissions,
|
||||
dataSources,
|
||||
currentUsername
|
||||
)
|
||||
.then(function permissionsRetrieved(retrievedPermissions) {
|
||||
|
||||
// Store retrieved permissions
|
||||
permissions = retrievedPermissions;
|
||||
|
||||
// Return to home if there's nothing to do here
|
||||
if (!canManageUserGroups())
|
||||
$location.path('/');
|
||||
|
||||
// If user groups can be created, list all readable user groups
|
||||
if ($scope.canCreateUserGroups())
|
||||
return dataSourceService.apply(userGroupService.getUserGroups, dataSources);
|
||||
|
||||
// Otherwise, list only updateable/deletable users
|
||||
return dataSourceService.apply(userGroupService.getUserGroups, dataSources, [
|
||||
PermissionSet.ObjectPermissionType.UPDATE,
|
||||
PermissionSet.ObjectPermissionType.DELETE
|
||||
]);
|
||||
|
||||
})
|
||||
.then(function userGroupsReceived(userGroups) {
|
||||
setDisplayedUserGroups(permissions, userGroups);
|
||||
}, requestService.WARN);
|
||||
|
||||
}];
|
||||
|
||||
return directive;
|
||||
|
||||
}]);
|
@@ -150,9 +150,11 @@ angular.module('settings').directive('guacSettingsUsers', [function guacSettings
|
||||
return null;
|
||||
|
||||
// For each data source
|
||||
for (var dataSource in $scope.permissions) {
|
||||
var dataSources = _.keys($scope.permissions).sort();
|
||||
for (var i = 0; i < dataSources.length; i++) {
|
||||
|
||||
// Retrieve corresponding permission set
|
||||
var dataSource = dataSources[i];
|
||||
var permissionSet = $scope.permissions[dataSource];
|
||||
|
||||
// Can create users if adminstrator or have explicit permission
|
||||
|
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
a.button.add-user,
|
||||
a.button.add-user-group,
|
||||
a.button.add-connection,
|
||||
a.button.add-connection-group {
|
||||
font-size: 0.8em;
|
||||
@@ -26,6 +27,7 @@ a.button.add-connection-group {
|
||||
}
|
||||
|
||||
a.button.add-user::before,
|
||||
a.button.add-user-group::before,
|
||||
a.button.add-connection::before,
|
||||
a.button.add-connection-group::before {
|
||||
|
||||
@@ -46,6 +48,10 @@ a.button.add-user::before {
|
||||
background-image: url('images/action-icons/guac-user-add.png');
|
||||
}
|
||||
|
||||
a.button.add-user-group::before {
|
||||
background-image: url('images/action-icons/guac-user-group-add.png');
|
||||
}
|
||||
|
||||
a.button.add-connection::before {
|
||||
background-image: url('images/action-icons/guac-monitor-add.png');
|
||||
}
|
||||
|
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.settings.user-groups table.user-group-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.settings.user-groups table.user-group-list th.user-group-name,
|
||||
.settings.user-groups table.user-group-list td.user-group-name {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.settings.user-groups table.user-group-list tr.user td.user-group-name a[href] {
|
||||
display: block;
|
||||
padding: .5em 1em;
|
||||
}
|
||||
|
||||
.settings.user-groups table.user-group-list tr.user td.user-group-name {
|
||||
padding: 0;
|
||||
}
|
@@ -13,6 +13,7 @@
|
||||
|
||||
<!-- Selected tab -->
|
||||
<guac-settings-users ng-if="activeTab === 'users'"></guac-settings-users>
|
||||
<guac-settings-user-groups ng-if="activeTab === 'userGroups'"></guac-settings-user-groups>
|
||||
<guac-settings-connections ng-if="activeTab === 'connections'"></guac-settings-connections>
|
||||
<guac-settings-connection-history ng-if="activeTab === 'history'"></guac-settings-connection-history>
|
||||
<guac-settings-sessions ng-if="activeTab === 'sessions'"></guac-settings-sessions>
|
||||
|
@@ -0,0 +1,48 @@
|
||||
<div class="settings section user-groups" ng-class="{loading: !isLoaded()}">
|
||||
|
||||
<!-- User group management -->
|
||||
<p>{{'SETTINGS_USER_GROUPS.HELP_USER_GROUPS' | translate}}</p>
|
||||
|
||||
|
||||
<!-- User management toolbar -->
|
||||
<div class="toolbar">
|
||||
|
||||
<!-- Form action buttons -->
|
||||
<div class="action-buttons">
|
||||
<a class="add-user-group button" ng-show="canCreateUserGroups()"
|
||||
href="#/manage/{{getDefaultDataSource()}}/userGroups/">{{'SETTINGS_USER_GROUPS.ACTION_NEW_USER_GROUP' | translate}}</a>
|
||||
</div>
|
||||
|
||||
<!-- User group filter -->
|
||||
<guac-filter filtered-items="filteredManageableUserGroups" items="manageableUserGroups"
|
||||
placeholder="'SETTINGS_USER_GROUPS.FIELD_PLACEHOLDER_FILTER' | translate"
|
||||
properties="filteredUserGroupProperties"></guac-filter>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- List of user groups this user has access to -->
|
||||
<table class="sorted user-group-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th guac-sort-order="order" guac-sort-property="'userGroup.identifier'" class="user-group-name">
|
||||
{{'SETTINGS_USER_GROUPS.TABLE_HEADER_USER_GROUP_NAME' | translate}}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ng-class="{loading: !isLoaded()}">
|
||||
<tr ng-repeat="manageableUserGroup in manageableUserGroupPage" class="user-group">
|
||||
<td class="user-group-name">
|
||||
<a ng-href="#/manage/{{manageableUserGroup.dataSource}}/userGroups/{{manageableUserGroup.userGroup.identifier}}">
|
||||
<div class="icon user-group"></div>
|
||||
<span class="name">{{manageableUserGroup.userGroup.identifier}}</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- Pager controls for user group list -->
|
||||
<guac-pager page="manageableUserGroupPage" page-size="25"
|
||||
items="filteredManageableUserGroups | orderBy : order.predicate"></guac-pager>
|
||||
|
||||
</div>
|
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
guacamole/src/main/webapp/images/arrows/right.png
Normal file
BIN
guacamole/src/main/webapp/images/arrows/right.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 264 B |
BIN
guacamole/src/main/webapp/images/user-icons/guac-user-group.png
Normal file
BIN
guacamole/src/main/webapp/images/user-icons/guac-user-group.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
guacamole/src/main/webapp/images/x-red.png
Normal file
BIN
guacamole/src/main/webapp/images/x-red.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 583 B |
@@ -71,8 +71,8 @@
|
||||
<script type="text/javascript" src="relocateParameters.js"></script>
|
||||
|
||||
<!-- Utility libraries -->
|
||||
<script type="text/javascript" src="webjars/jquery/2.1.3/dist/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="webjars/lodash/2.4.1/dist/lodash.min.js"></script>
|
||||
<script type="text/javascript" src="webjars/jquery/3.3.1/dist/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="webjars/lodash/4.17.10/dist/lodash.min.js"></script>
|
||||
|
||||
<!-- AngularJS -->
|
||||
<script type="text/javascript" src="webjars/angular/1.6.9/angular.min.js"></script>
|
||||
|
@@ -21,6 +21,7 @@
|
||||
"ACTION_MANAGE_SETTINGS" : "Settings",
|
||||
"ACTION_MANAGE_SESSIONS" : "Active Sessions",
|
||||
"ACTION_MANAGE_USERS" : "Users",
|
||||
"ACTION_MANAGE_USER_GROUPS" : "Groups",
|
||||
"ACTION_NAVIGATE_BACK" : "Back",
|
||||
"ACTION_NAVIGATE_HOME" : "Home",
|
||||
"ACTION_SAVE" : "Save",
|
||||
@@ -293,6 +294,7 @@
|
||||
"FIELD_HEADER_ADMINISTER_SYSTEM" : "Administer system:",
|
||||
"FIELD_HEADER_CHANGE_OWN_PASSWORD" : "Change own password:",
|
||||
"FIELD_HEADER_CREATE_NEW_USERS" : "Create new users:",
|
||||
"FIELD_HEADER_CREATE_NEW_USER_GROUPS" : "Create new user groups:",
|
||||
"FIELD_HEADER_CREATE_NEW_CONNECTIONS" : "Create new connections:",
|
||||
"FIELD_HEADER_CREATE_NEW_CONNECTION_GROUPS" : "Create new connection groups:",
|
||||
"FIELD_HEADER_CREATE_NEW_SHARING_PROFILES" : "Create new sharing profiles:",
|
||||
@@ -302,17 +304,64 @@
|
||||
|
||||
"FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
|
||||
|
||||
"INFO_READ_ONLY" : "Sorry, but this user account cannot be edited.",
|
||||
"HELP_NO_USER_GROUPS" : "This user does not currently belong to any groups. Expand this section to add groups.",
|
||||
|
||||
"INFO_READ_ONLY" : "Sorry, but this user account cannot be edited.",
|
||||
"INFO_NO_USER_GROUPS_AVAILABLE" : "No groups available.",
|
||||
|
||||
"SECTION_HEADER_ALL_CONNECTIONS" : "All Connections",
|
||||
"SECTION_HEADER_CONNECTIONS" : "Connections",
|
||||
"SECTION_HEADER_CURRENT_CONNECTIONS" : "Current Connections",
|
||||
"SECTION_HEADER_EDIT_USER" : "Edit User",
|
||||
"SECTION_HEADER_PERMISSIONS" : "Permissions",
|
||||
"SECTION_HEADER_USER_GROUPS" : "Groups",
|
||||
|
||||
"TEXT_CONFIRM_DELETE" : "Users cannot be restored after they have been deleted. Are you sure you want to delete this user?"
|
||||
|
||||
},
|
||||
|
||||
"MANAGE_USER_GROUP" : {
|
||||
|
||||
"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",
|
||||
|
||||
"DIALOG_HEADER_CONFIRM_DELETE" : "Delete Group",
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"FIELD_HEADER_ADMINISTER_SYSTEM" : "@:MANAGE_USER.FIELD_HEADER_ADMINISTER_SYSTEM",
|
||||
"FIELD_HEADER_CHANGE_OWN_PASSWORD" : "@:MANAGE_USER.FIELD_HEADER_CHANGE_OWN_PASSWORD",
|
||||
"FIELD_HEADER_CREATE_NEW_USERS" : "@:MANAGE_USER.FIELD_HEADER_CREATE_NEW_USERS",
|
||||
"FIELD_HEADER_CREATE_NEW_USER_GROUPS" : "@:MANAGE_USER.FIELD_HEADER_CREATE_NEW_USER_GROUPS",
|
||||
"FIELD_HEADER_CREATE_NEW_CONNECTIONS" : "@:MANAGE_USER.FIELD_HEADER_CREATE_NEW_CONNECTIONS",
|
||||
"FIELD_HEADER_CREATE_NEW_CONNECTION_GROUPS" : "@:MANAGE_USER.FIELD_HEADER_CREATE_NEW_CONNECTION_GROUPS",
|
||||
"FIELD_HEADER_CREATE_NEW_SHARING_PROFILES" : "@:MANAGE_USER.FIELD_HEADER_CREATE_NEW_SHARING_PROFILES",
|
||||
"FIELD_HEADER_USER_GROUP_NAME" : "Group name:",
|
||||
|
||||
"FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
|
||||
|
||||
"HELP_NO_USER_GROUPS" : "This group does not currently belong to any groups. Expand this section to add groups.",
|
||||
"HELP_NO_MEMBER_USER_GROUPS" : "This group does not currently contain any groups. Expand this section to add groups.",
|
||||
"HELP_NO_MEMBER_USERS" : "This group does not currently contain any users. Expand this section to add users.",
|
||||
|
||||
"INFO_READ_ONLY" : "Sorry, but this group cannot be edited.",
|
||||
"INFO_NO_USER_GROUPS_AVAILABLE" : "@:MANAGE_USER.INFO_NO_USER_GROUPS_AVAILABLE",
|
||||
"INFO_NO_USERS_AVAILABLE" : "No users available.",
|
||||
|
||||
"SECTION_HEADER_ALL_CONNECTIONS" : "@:MANAGE_USER.SECTION_HEADER_ALL_CONNECTIONS",
|
||||
"SECTION_HEADER_CONNECTIONS" : "@:MANAGE_USER.SECTION_HEADER_CONNECTIONS",
|
||||
"SECTION_HEADER_CURRENT_CONNECTIONS" : "@:MANAGE_USER.SECTION_HEADER_CURRENT_CONNECTIONS",
|
||||
"SECTION_HEADER_EDIT_USER_GROUP" : "Edit Group",
|
||||
"SECTION_HEADER_MEMBER_USERS" : "Member Users",
|
||||
"SECTION_HEADER_MEMBER_USER_GROUPS" : "Member Groups",
|
||||
"SECTION_HEADER_PERMISSIONS" : "@:MANAGE_USER.SECTION_HEADER_PERMISSIONS",
|
||||
"SECTION_HEADER_USER_GROUPS" : "Parent Groups",
|
||||
|
||||
"TEXT_CONFIRM_DELETE" : "Groups cannot be restored after they have been deleted. Are you sure you want to delete this group?"
|
||||
|
||||
},
|
||||
|
||||
"PROTOCOL_RDP" : {
|
||||
|
||||
@@ -744,7 +793,26 @@
|
||||
"TABLE_HEADER_USERNAME" : "Username"
|
||||
|
||||
},
|
||||
|
||||
|
||||
"SETTINGS_USER_GROUPS" : {
|
||||
|
||||
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
|
||||
"ACTION_NEW_USER_GROUP" : "New Group",
|
||||
|
||||
"DIALOG_HEADER_ERROR" : "@:APP.DIALOG_HEADER_ERROR",
|
||||
|
||||
"FIELD_PLACEHOLDER_FILTER" : "@:APP.FIELD_PLACEHOLDER_FILTER",
|
||||
|
||||
"FORMAT_DATE" : "@:APP.FORMAT_DATE_TIME_PRECISE",
|
||||
|
||||
"HELP_USER_GROUPS" : "Click or tap on a group below to manage that group. Depending on your access level, groups can be added and deleted, and their member users and groups can be changed.",
|
||||
|
||||
"SECTION_HEADER_USER_GROUPS" : "Groups",
|
||||
|
||||
"TABLE_HEADER_USER_GROUP_NAME" : "Group Name"
|
||||
|
||||
},
|
||||
|
||||
"SETTINGS_SESSIONS" : {
|
||||
|
||||
"ACTION_ACKNOWLEDGE" : "@:APP.ACTION_ACKNOWLEDGE",
|
||||
@@ -790,6 +858,7 @@
|
||||
"ACTION_MANAGE_SESSIONS" : "@:APP.ACTION_MANAGE_SESSIONS",
|
||||
"ACTION_MANAGE_SETTINGS" : "@:APP.ACTION_MANAGE_SETTINGS",
|
||||
"ACTION_MANAGE_USERS" : "@:APP.ACTION_MANAGE_USERS",
|
||||
"ACTION_MANAGE_USER_GROUPS" : "@:APP.ACTION_MANAGE_USER_GROUPS",
|
||||
"ACTION_NAVIGATE_HOME" : "@:APP.ACTION_NAVIGATE_HOME",
|
||||
"ACTION_VIEW_HISTORY" : "@:APP.ACTION_VIEW_HISTORY"
|
||||
|
||||
|
Reference in New Issue
Block a user