You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by vn...@apache.org on 2018/08/14 23:39:41 UTC

[03/13] guacamole-client git commit: GUACAMOLE-220: Add JavaScript service for retrieving/manipulating user groups.

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/master
Commit: 9f01fcb1558b11b52c78bcddee2ea601ab4b102c
Parents: c36d333
Author: Michael Jumper <mj...@apache.org>
Authored: Thu Apr 19 14:38:24 2018 -0700
Committer: Michael Jumper <mj...@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