GUACAMOLE-220: Add JavaScript service for retrieving/manipulating user groups.
Project: http://git-wip-us.apache.org/repos/asf/guacamole-client/repo Commit: http://git-wip-us.apache.org/repos/asf/guacamole-client/commit/9f01fcb1 Tree: http://git-wip-us.apache.org/repos/asf/guacamole-client/tree/9f01fcb1 Diff: http://git-wip-us.apache.org/repos/asf/guacamole-client/diff/9f01fcb1 Branch: refs/heads/staging/1.0.0 Commit: 9f01fcb1558b11b52c78bcddee2ea601ab4b102c Parents: c36d333 Author: Michael Jumper <mjum...@apache.org> Authored: Thu Apr 19 14:38:24 2018 -0700 Committer: Michael Jumper <mjum...@apache.org> Committed: Wed Aug 8 09:00:06 2018 -0700 ---------------------------------------------------------------------- .../webapp/app/rest/services/cacheService.js | 3 +- .../app/rest/services/membershipService.js | 385 +++++++++++++++++++ .../app/rest/services/userGroupService.js | 223 +++++++++++ .../webapp/app/rest/types/RelatedObjectPatch.js | 85 ++++ .../src/main/webapp/app/rest/types/UserGroup.js | 59 +++ 5 files changed, 754 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/9f01fcb1/guacamole/src/main/webapp/app/rest/services/cacheService.js ---------------------------------------------------------------------- diff --git a/guacamole/src/main/webapp/app/rest/services/cacheService.js b/guacamole/src/main/webapp/app/rest/services/cacheService.js index 55b7fc1..9a32004 100644 --- a/guacamole/src/main/webapp/app/rest/services/cacheService.js +++ b/guacamole/src/main/webapp/app/rest/services/cacheService.js @@ -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 */ http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/9f01fcb1/guacamole/src/main/webapp/app/rest/services/membershipService.js ---------------------------------------------------------------------- diff --git a/guacamole/src/main/webapp/app/rest/services/membershipService.js b/guacamole/src/main/webapp/app/rest/services/membershipService.js new file mode 100644 index 0000000..58181c8 --- /dev/null +++ b/guacamole/src/main/webapp/app/rest/services/membershipService.js @@ -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; + +}]); http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/9f01fcb1/guacamole/src/main/webapp/app/rest/services/userGroupService.js ---------------------------------------------------------------------- diff --git a/guacamole/src/main/webapp/app/rest/services/userGroupService.js b/guacamole/src/main/webapp/app/rest/services/userGroupService.js new file mode 100644 index 0000000..ad29837 --- /dev/null +++ b/guacamole/src/main/webapp/app/rest/services/userGroupService.js @@ -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; + +}]); http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/9f01fcb1/guacamole/src/main/webapp/app/rest/types/RelatedObjectPatch.js ---------------------------------------------------------------------- diff --git a/guacamole/src/main/webapp/app/rest/types/RelatedObjectPatch.js b/guacamole/src/main/webapp/app/rest/types/RelatedObjectPatch.js new file mode 100644 index 0000000..bb82def --- /dev/null +++ b/guacamole/src/main/webapp/app/rest/types/RelatedObjectPatch.js @@ -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; + +}]); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/9f01fcb1/guacamole/src/main/webapp/app/rest/types/UserGroup.js ---------------------------------------------------------------------- diff --git a/guacamole/src/main/webapp/app/rest/types/UserGroup.js b/guacamole/src/main/webapp/app/rest/types/UserGroup.js new file mode 100644 index 0000000..03b73e2 --- /dev/null +++ b/guacamole/src/main/webapp/app/rest/types/UserGroup.js @@ -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 = {}; + + }; + + return UserGroup; + +}]); \ No newline at end of file