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/05/05 01:23:28 UTC

[1/2] guacamole-client git commit: GUACAMOLE-220: Expose user groups, group membership, and group permissions via REST.

Repository: guacamole-client
Updated Branches:
  refs/heads/master 242448060 -> fed513329


GUACAMOLE-220: Expose user groups, group membership, and group permissions via REST.


Project: http://git-wip-us.apache.org/repos/asf/guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-client/commit/c26cf60e
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-client/tree/c26cf60e
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-client/diff/c26cf60e

Branch: refs/heads/master
Commit: c26cf60e6bfeb5d6b2644f37964c9764df344a10
Parents: 9b99b18
Author: Michael Jumper <mj...@apache.org>
Authored: Thu Apr 19 14:37:26 2018 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Fri May 4 16:06:41 2018 -0700

----------------------------------------------------------------------
 .../guacamole/rest/RESTServiceModule.java       |   2 +
 .../rest/identifier/RelatedObjectSetPatch.java  |  89 ++++++++++
 .../identifier/RelatedObjectSetResource.java    | 164 +++++++++++++++++++
 .../guacamole/rest/identifier/package-info.java |  24 +++
 .../rest/permission/APIPermissionSet.java       |  34 ++++
 .../rest/permission/PermissionSetResource.java  |  85 ++++++----
 .../guacamole/rest/schema/SchemaResource.java   |  19 +++
 .../rest/session/UserContextResource.java       |  27 +++
 .../guacamole/rest/user/UserResource.java       |  18 ++
 .../guacamole/rest/usergroup/APIUserGroup.java  | 106 ++++++++++++
 .../rest/usergroup/APIUserGroupWrapper.java     | 131 +++++++++++++++
 .../usergroup/UserGroupDirectoryResource.java   |  68 ++++++++
 .../rest/usergroup/UserGroupModule.java         |  63 +++++++
 .../usergroup/UserGroupObjectTranslator.java    |  65 ++++++++
 .../rest/usergroup/UserGroupResource.java       | 141 ++++++++++++++++
 .../guacamole/rest/usergroup/package-info.java  |  23 +++
 16 files changed, 1029 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java
index 98c5c7a..b326fa5 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/RESTServiceModule.java
@@ -46,6 +46,7 @@ import org.apache.guacamole.rest.sharingprofile.SharingProfileModule;
 import org.apache.guacamole.rest.tunnel.TunnelCollectionResourceFactory;
 import org.apache.guacamole.rest.tunnel.TunnelResourceFactory;
 import org.apache.guacamole.rest.user.UserModule;
+import org.apache.guacamole.rest.usergroup.UserGroupModule;
 import org.webjars.servlet.WebjarsServlet;
 
 /**
@@ -107,6 +108,7 @@ public class RESTServiceModule extends ServletModule {
         install(new ConnectionGroupModule());
         install(new SharingProfileModule());
         install(new UserModule());
+        install(new UserGroupModule());
 
         // Set up the servlet and JSON mappings
         bind(GuiceContainer.class);

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/identifier/RelatedObjectSetPatch.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/identifier/RelatedObjectSetPatch.java b/guacamole/src/main/java/org/apache/guacamole/rest/identifier/RelatedObjectSetPatch.java
new file mode 100644
index 0000000..b580748
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/identifier/RelatedObjectSetPatch.java
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+package org.apache.guacamole.rest.identifier;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.RelatedObjectSet;
+
+/**
+ * A set of changes to be applied to a RelatedObjectSet, describing the
+ * objects being added and/or removed.
+ */
+public class RelatedObjectSetPatch {
+
+    /**
+     * A set containing the identifiers of all objects being added.
+     */
+    private final Set<String> addedObjects = new HashSet<String>();
+
+    /**
+     * A set containing the identifiers of all objects being removed.
+     */
+    private final Set<String> removedObjects = new HashSet<String>();
+
+    /**
+     * Queues the object having the given identifier for addition to the
+     * underlying RelatedObjectSet. The add operation will be performed only
+     * when apply() is called.
+     *
+     * @param identifier
+     *     The identifier of the object to add.
+     */
+    public void addObject(String identifier) {
+        addedObjects.add(identifier);
+    }
+
+    /**
+     * Queues the object having the given identifier for removal from the
+     * underlying RelatedObjectSet. The remove operation will be performed only
+     * when apply() is called.
+     *
+     * @param identifier
+     *     The identifier of the object to remove.
+     */
+    public void removeObject(String identifier) {
+        removedObjects.add(identifier);
+    }
+
+    /**
+     * Applies all queued changes to the given RelatedObjectSet.
+     *
+     * @param objects
+     *     The RelatedObjectSet to add and/or remove objects from.
+     *
+     * @throws GuacamoleException
+     *     If any add or remove operation is disallowed by the underlying
+     *     RelatedObjectSet.
+     */
+    public void apply(RelatedObjectSet objects) throws GuacamoleException {
+
+        // Add any added identifiers
+        if (!addedObjects.isEmpty())
+            objects.addObjects(addedObjects);
+
+        // Remove any removed identifiers
+        if (!removedObjects.isEmpty())
+            objects.removeObjects(removedObjects);
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/identifier/RelatedObjectSetResource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/identifier/RelatedObjectSetResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/identifier/RelatedObjectSetResource.java
new file mode 100644
index 0000000..446b045
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/identifier/RelatedObjectSetResource.java
@@ -0,0 +1,164 @@
+/*
+ * 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.
+ */
+
+package org.apache.guacamole.rest.identifier;
+
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.GuacamoleClientException;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.RelatedObjectSet;
+import org.apache.guacamole.rest.APIPatch;
+import org.apache.guacamole.rest.PATCH;
+
+/**
+ * A REST resource which abstracts the operations available on arbitrary sets
+ * of objects which share some common relation.
+ */
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class RelatedObjectSetResource {
+
+    /**
+     * The path of any operation within a JSON patch which adds/removes an
+     * object from the associated set.
+     */
+    private static final String OBJECT_PATH = "/";
+
+    /**
+     * The set of objects represented by this RelatedObjectSetResource.
+     */
+    private final RelatedObjectSet objects;
+
+    /**
+     * Creates a new RelatedObjectSetResource which exposes the operations and
+     * subresources available for the given RelatedObjectSet.
+     *
+     * @param objects
+     *     The RelatedObjectSet exposed by this RelatedObjectSetResource.
+     */
+    public RelatedObjectSetResource(RelatedObjectSet objects) {
+        this.objects = objects;
+    }
+
+    /**
+     * Returns the identifiers of all objects within RelatedObjectSet exposed by
+     * this RelatedObjectSetResource.
+     *
+     * @return
+     *     The identifiers of all objects within RelatedObjectSet exposed by
+     *     this RelatedObjectSetResource.
+     *
+     * @throws GuacamoleException
+     *     If an error occurs while retrieving the objects within the set, or
+     *     if objects cannot be retrieved due to lack of permissions to do so.
+     */
+    @GET
+    public Set<String> getObjects() throws GuacamoleException {
+        return objects.getObjects();
+    }
+
+    /**
+     * Updates the given RelatedObjectSetPatch by queuing an add or remove
+     * operation for the object having the given identifier based on the given
+     * patch operation.
+     *
+     * @param operation
+     *     The patch operation to perform.
+     *
+     * @param relatedObjectSetPatch
+     *     The RelatedObjectSetPatch being modified.
+     *
+     * @param identifier
+     *     The identifier of the object being added or removed from the set.
+     *
+     * @throws GuacamoleException
+     *     If the requested patch operation is not supported.
+     */
+    private void updateRelatedObjectSet(APIPatch.Operation operation,
+            RelatedObjectSetPatch relatedObjectSetPatch, String identifier)
+            throws GuacamoleException {
+
+        // Add or remove object based on operation
+        switch (operation) {
+
+            // Add object
+            case add:
+                relatedObjectSetPatch.addObject(identifier);
+                break;
+
+            // Remove object
+            case remove:
+                relatedObjectSetPatch.removeObject(identifier);
+                break;
+
+            // Unsupported patch operation
+            default:
+                throw new GuacamoleClientException("Unsupported patch operation: \"" + operation + "\"");
+
+        }
+
+    }
+
+    /**
+     * Applies a given list of patches to the RelatedObjectSet exposed by this
+     * RelatedObjectSetResource. Each patch specifies either
+     * an "add" or a "remove" operation for a particular object represented
+     * by its identifier. The path of each operation MUST be "/", with the
+     * identifier of the object being provided within the value of the patch.
+     *
+     * @param patches
+     *     The patches to apply for this request.
+     *
+     * @throws GuacamoleException
+     *     If an error is encountered while modifying the contents of the
+     *     RelatedObjectSet.
+     */
+    @PATCH
+    public void patchObjects(List<APIPatch<String>> patches)
+            throws GuacamoleException {
+
+        // Apply all patch operations individually
+        RelatedObjectSetPatch objectPatch = new RelatedObjectSetPatch();
+        for (APIPatch<String> patch : patches) {
+
+            String path = patch.getPath();
+
+            // Add/remove objects from set
+            if (path.equals(OBJECT_PATH)) {
+                String identifier = patch.getValue();
+                updateRelatedObjectSet(patch.getOp(), objectPatch, identifier);
+            }
+
+            // Otherwise, the path is not supported
+            else
+                throw new GuacamoleClientException("Unsupported patch path: \"" + path + "\"");
+
+        } // end for each patch operation
+
+        // Save changes
+        objectPatch.apply(objects);
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/identifier/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/identifier/package-info.java b/guacamole/src/main/java/org/apache/guacamole/rest/identifier/package-info.java
new file mode 100644
index 0000000..4681bac
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/identifier/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+/**
+ * Classes related to manipulating arbitrary sets of unique identifiers using
+ * the Guacamole REST API.
+ */
+package org.apache.guacamole.rest.identifier;

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/permission/APIPermissionSet.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/permission/APIPermissionSet.java b/guacamole/src/main/java/org/apache/guacamole/rest/permission/APIPermissionSet.java
index 18c38e8..c5b1116 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/permission/APIPermissionSet.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/permission/APIPermissionSet.java
@@ -72,6 +72,12 @@ public class APIPermissionSet {
             new HashMap<String, Set<ObjectPermission.Type>>();
 
     /**
+     * Map of user group ID to the set of granted permissions.
+     */
+    private Map<String, Set<ObjectPermission.Type>> userGroupPermissions =
+            new HashMap<String, Set<ObjectPermission.Type>>();
+
+    /**
      * Set of all granted system-level permissions.
      */
     private Set<SystemPermission.Type> systemPermissions =
@@ -163,6 +169,7 @@ public class APIPermissionSet {
         addObjectPermissions(sharingProfilePermissions,   permissions.getSharingProfilePermissions());
         addObjectPermissions(activeConnectionPermissions, permissions.getActiveConnectionPermissions());
         addObjectPermissions(userPermissions,             permissions.getUserPermissions());
+        addObjectPermissions(userGroupPermissions,        permissions.getUserGroupPermissions());
         
     }
 
@@ -240,6 +247,20 @@ public class APIPermissionSet {
     }
 
     /**
+     * Returns a map of user group IDs to the set of permissions granted for
+     * that user group. If no permissions are granted for a particular user
+     * group, its ID will not be present as a key in the map. This map is
+     * mutable, and changes to to this map will affect the permission set
+     * directly.
+     *
+     * @return
+     *     A map of user IDs to the set of permissions granted for that user.
+     */
+    public Map<String, Set<ObjectPermission.Type>> getUserGroupPermissions() {
+        return userGroupPermissions;
+    }
+
+    /**
      * Returns the set of granted system-level permissions. If no permissions
      * are granted at the system level, this will be an empty set. This set is
      * mutable, and changes to this set will affect the permission set
@@ -317,6 +338,19 @@ public class APIPermissionSet {
     }
 
     /**
+     * Replaces the current map of user group permissions with the given map,
+     * which must map user group ID to its corresponding set of granted
+     * permissions. If a user group has no permissions, its ID must not be
+     * present as a key in the map.
+     *
+     * @param userGroupPermissions
+     *     The map which must replace the currently-stored map of permissions.
+     */
+    public void setUserGroupPermissions(Map<String, Set<ObjectPermission.Type>> userGroupPermissions) {
+        this.userGroupPermissions = userGroupPermissions;
+    }
+
+    /**
      * Replaces the current set of system-level permissions with the given set.
      * If no system-level permissions are granted, the empty set must be
      * specified.

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java
index 20b1b67..739a39c 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/permission/PermissionSetResource.java
@@ -26,7 +26,7 @@ import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import org.apache.guacamole.GuacamoleClientException;
 import org.apache.guacamole.GuacamoleException;
-import org.apache.guacamole.net.auth.User;
+import org.apache.guacamole.net.auth.Permissions;
 import org.apache.guacamole.net.auth.permission.ObjectPermission;
 import org.apache.guacamole.net.auth.permission.Permission;
 import org.apache.guacamole.net.auth.permission.SystemPermission;
@@ -35,78 +35,88 @@ import org.apache.guacamole.rest.PATCH;
 
 /**
  * A REST resource which abstracts the operations available on the permissions
- * granted to an existing User.
+ * granted to an existing User or UserGroup.
  */
 @Produces(MediaType.APPLICATION_JSON)
 @Consumes(MediaType.APPLICATION_JSON)
 public class PermissionSetResource {
 
     /**
-     * The prefix of any path within an operation of a JSON patch which
-     * modifies the permissions of a user regarding a specific connection.
+     * The prefix of any path within an operation of a JSON patch which modifies
+     * the permissions of a user or user group regarding a specific connection.
      */
     private static final String CONNECTION_PERMISSION_PATCH_PATH_PREFIX = "/connectionPermissions/";
 
     /**
-     * The prefix of any path within an operation of a JSON patch which
-     * modifies the permissions of a user regarding a specific connection group.
+     * The prefix of any path within an operation of a JSON patch which modifies
+     * the permissions of a user or user group regarding a specific connection
+     * group.
      */
     private static final String CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX = "/connectionGroupPermissions/";
 
     /**
-     * The prefix of any path within an operation of a JSON patch which
-     * modifies the permissions of a user regarding a specific sharing profile.
+     * The prefix of any path within an operation of a JSON patch which modifies
+     * the permissions of a user or user group regarding a specific sharing
+     * profile.
      */
     private static final String SHARING_PROFILE_PERMISSION_PATCH_PATH_PREFIX = "/sharingProfilePermissions/";
 
     /**
-     * The prefix of any path within an operation of a JSON patch which
-     * modifies the permissions of a user regarding a specific active connection.
+     * The prefix of any path within an operation of a JSON patch which modifies
+     * the permissions of a user or user group regarding a specific active
+     * connection.
      */
     private static final String ACTIVE_CONNECTION_PERMISSION_PATCH_PATH_PREFIX = "/activeConnectionPermissions/";
 
     /**
-     * The prefix of any path within an operation of a JSON patch which
-     * modifies the permissions of a user regarding another, specific user.
+     * The prefix of any path within an operation of a JSON patch which modifies
+     * the permissions of a user or user group regarding a specific user.
      */
     private static final String USER_PERMISSION_PATCH_PATH_PREFIX = "/userPermissions/";
 
     /**
+     * The prefix of any path within an operation of a JSON patch which modifies
+     * the permissions of a user or user group regarding a specific user group.
+     */
+    private static final String USER_GROUP_PERMISSION_PATCH_PATH_PREFIX = "/userGroupPermissions/";
+
+    /**
      * The path of any operation within a JSON patch which modifies the
-     * permissions of a user regarding the entire system.
+     * permissions of a user or user group regarding the entire system.
      */
     private static final String SYSTEM_PERMISSION_PATCH_PATH = "/systemPermissions";
 
     /**
-     * The User object whose permissions are represented by this
-     * PermissionSetResource.
+     * The permissions represented by this PermissionSetResource.
      */
-    private final User user;
+    private final Permissions permissions;
 
     /**
      * Creates a new PermissionSetResource which exposes the operations and
-     * subresources available for permissions of the given User.
+     * subresources available for the given Permissions object.
      *
-     * @param user
-     *     The User whose permissions should be represented by this
+     * @param permissions
+     *     The permissions that should be represented by this
      *     PermissionSetResource.
      */
-    public PermissionSetResource(User user) {
-        this.user = user;
+    public PermissionSetResource(Permissions permissions) {
+        this.permissions = permissions;
     }
 
     /**
-     * Gets a list of permissions for the user with the given username.
+     * Gets a list of all permissions granted to the user or user group
+     * associated with this PermissionSetResource.
      *
      * @return
-     *     A list of all permissions granted to the specified user.
+     *     A list of all permissions granted to the user or user group
+     *     associated with this PermissionSetResource.
      *
      * @throws GuacamoleException
      *     If an error occurs while retrieving permissions.
      */
     @GET
     public APIPermissionSet getPermissions() throws GuacamoleException {
-        return new APIPermissionSet(user);
+        return new APIPermissionSet(permissions);
     }
 
     /**
@@ -177,6 +187,7 @@ public class PermissionSetResource {
         PermissionSetPatch<ObjectPermission> sharingProfilePermissionPatch   = new PermissionSetPatch<ObjectPermission>();
         PermissionSetPatch<ObjectPermission> activeConnectionPermissionPatch = new PermissionSetPatch<ObjectPermission>();
         PermissionSetPatch<ObjectPermission> userPermissionPatch             = new PermissionSetPatch<ObjectPermission>();
+        PermissionSetPatch<ObjectPermission> userGroupPermissionPatch        = new PermissionSetPatch<ObjectPermission>();
         PermissionSetPatch<SystemPermission> systemPermissionPatch           = new PermissionSetPatch<SystemPermission>();
 
         // Apply all patch operations individually
@@ -249,6 +260,19 @@ public class PermissionSetResource {
 
             }
 
+            // Create user group permission if path has user group prefix
+            else if (path.startsWith(USER_GROUP_PERMISSION_PATCH_PATH_PREFIX)) {
+
+                // Get identifier and type from patch operation
+                String identifier = path.substring(USER_GROUP_PERMISSION_PATCH_PATH_PREFIX.length());
+                ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue());
+
+                // Create and update corresponding permission
+                ObjectPermission permission = new ObjectPermission(type, identifier);
+                updatePermissionSet(patch.getOp(), userGroupPermissionPatch, permission);
+
+            }
+
             // Create system permission if path is system path
             else if (path.equals(SYSTEM_PERMISSION_PATCH_PATH)) {
 
@@ -268,12 +292,13 @@ public class PermissionSetResource {
         } // end for each patch operation
 
         // Save the permission changes
-        connectionPermissionPatch.apply(user.getConnectionPermissions());
-        connectionGroupPermissionPatch.apply(user.getConnectionGroupPermissions());
-        sharingProfilePermissionPatch.apply(user.getSharingProfilePermissions());
-        activeConnectionPermissionPatch.apply(user.getActiveConnectionPermissions());
-        userPermissionPatch.apply(user.getUserPermissions());
-        systemPermissionPatch.apply(user.getSystemPermissions());
+        connectionPermissionPatch.apply(permissions.getConnectionPermissions());
+        connectionGroupPermissionPatch.apply(permissions.getConnectionGroupPermissions());
+        sharingProfilePermissionPatch.apply(permissions.getSharingProfilePermissions());
+        activeConnectionPermissionPatch.apply(permissions.getActiveConnectionPermissions());
+        userPermissionPatch.apply(permissions.getUserPermissions());
+        userGroupPermissionPatch.apply(permissions.getUserGroupPermissions());
+        systemPermissionPatch.apply(permissions.getSystemPermissions());
 
     }
 

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/schema/SchemaResource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/schema/SchemaResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/schema/SchemaResource.java
index 3261835..211885d 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/schema/SchemaResource.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/schema/SchemaResource.java
@@ -78,6 +78,25 @@ public class SchemaResource {
     }
 
     /**
+     * Retrieves the possible attributes of a user group object.
+     *
+     * @return
+     *     A collection of forms which describe the possible attributes of a
+     *     user group object.
+     *
+     * @throws GuacamoleException
+     *     If an error occurs while retrieving the possible attributes.
+     */
+    @GET
+    @Path("userGroupAttributes")
+    public Collection<Form> getUserGroupAttributes() throws GuacamoleException {
+
+        // Retrieve all possible user group attributes
+        return userContext.getUserGroupAttributes();
+
+    }
+
+    /**
      * Retrieves the possible attributes of a connection object.
      *
      * @return

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java
index 29b443f..edaf9f4 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/session/UserContextResource.java
@@ -35,6 +35,7 @@ import org.apache.guacamole.net.auth.ConnectionGroup;
 import org.apache.guacamole.net.auth.SharingProfile;
 import org.apache.guacamole.net.auth.User;
 import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.net.auth.UserGroup;
 import org.apache.guacamole.rest.activeconnection.APIActiveConnection;
 import org.apache.guacamole.rest.connection.APIConnection;
 import org.apache.guacamole.rest.connectiongroup.APIConnectionGroup;
@@ -44,6 +45,7 @@ import org.apache.guacamole.rest.history.HistoryResource;
 import org.apache.guacamole.rest.schema.SchemaResource;
 import org.apache.guacamole.rest.sharingprofile.APISharingProfile;
 import org.apache.guacamole.rest.user.APIUser;
+import org.apache.guacamole.rest.usergroup.APIUserGroup;
 
 /**
  * A REST resource which exposes the contents of a particular UserContext.
@@ -103,6 +105,13 @@ public class UserContextResource {
     private DirectoryResourceFactory<User, APIUser> userDirectoryResourceFactory;
 
     /**
+     * Factory for creating DirectoryResources which expose a given
+     * UserGroup Directory.
+     */
+    @Inject
+    private DirectoryResourceFactory<UserGroup, APIUserGroup> userGroupDirectoryResourceFactory;
+
+    /**
      * Creates a new UserContextResource which exposes the data within the
      * given UserContext.
      *
@@ -226,6 +235,24 @@ public class UserContextResource {
     }
 
     /**
+     * Returns a new resource which represents the UserGroup Directory contained
+     * within the UserContext exposed by this UserContextResource.
+     *
+     * @return
+     *     A new resource which represents the UserGroup Directory contained
+     *     within the UserContext exposed by this UserContextResource.
+     *
+     * @throws GuacamoleException
+     *     If an error occurs while retrieving the UserGroup Directory.
+     */
+    @Path("userGroups")
+    public DirectoryResource<UserGroup, APIUserGroup> getUserGroupDirectoryResource()
+            throws GuacamoleException {
+        return userGroupDirectoryResourceFactory.create(userContext,
+                userContext.getUserGroupDirectory());
+    }
+
+    /**
      * Returns a new resource which represents historical data contained
      * within the UserContext exposed by this UserContextResource.
      *

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/user/UserResource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/user/UserResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/user/UserResource.java
index 4e7ab92..db0aba5 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/user/UserResource.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/user/UserResource.java
@@ -43,6 +43,7 @@ import org.apache.guacamole.net.auth.credentials.GuacamoleCredentialsException;
 import org.apache.guacamole.rest.directory.DirectoryObjectResource;
 import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
 import org.apache.guacamole.rest.history.APIActivityRecord;
+import org.apache.guacamole.rest.identifier.RelatedObjectSetResource;
 import org.apache.guacamole.rest.permission.APIPermissionSet;
 import org.apache.guacamole.rest.permission.PermissionSetResource;
 
@@ -211,4 +212,21 @@ public class UserResource
         return new APIPermissionSet(user.getEffectivePermissions());
     }
 
+    /**
+     * Returns a resource which abstracts operations available on the set of
+     * user groups of which the User represented by this UserResource is a
+     * member.
+     *
+     * @return
+     *     A resource which represents the set of user groups of which the
+     *     User represented by this UserResource is a member.
+     *
+     * @throws GuacamoleException
+     *     If the group membership for this user cannot be retrieved.
+     */
+    @Path("userGroups")
+    public RelatedObjectSetResource getUserGroups() throws GuacamoleException {
+        return new RelatedObjectSetResource(user.getUserGroups());
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/APIUserGroup.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/APIUserGroup.java b/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/APIUserGroup.java
new file mode 100644
index 0000000..c2087b8
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/APIUserGroup.java
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+package org.apache.guacamole.rest.usergroup;
+
+import java.util.Map;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.apache.guacamole.net.auth.UserGroup;
+
+/**
+ * A simple UserGroup to expose through the REST endpoints.
+ */
+@JsonIgnoreProperties(ignoreUnknown = true)
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+public class APIUserGroup {
+
+    /**
+     * The identifier of this user group.
+     */
+    private String identifier;
+
+    /**
+     * Map of all associated attributes by attribute identifier.
+     */
+    private Map<String, String> attributes;
+
+    /**
+     * Construct a new empty APIUserGroup.
+     */
+    public APIUserGroup() {}
+
+    /**
+     * Construct a new APIUserGroup from the provided UserGroup.
+     *
+     * @param group
+     *     The UserGroup to construct the APIUserGroup from.
+     */
+    public APIUserGroup(UserGroup group) {
+        this.identifier = group.getIdentifier();
+        this.attributes = group.getAttributes();
+    }
+
+    /**
+     * Returns the unique string which identifies this group relative to other
+     * groups.
+     *
+     * @return
+     *     The unique string which identifies this group.
+     */
+    public String getIdentifier() {
+        return identifier;
+    }
+
+    /**
+     * Sets the unique string which identifies this group relative to other
+     * groups.
+     *
+     * @param identifier
+     *     The unique string which identifies this group.
+     */
+    public void setIdentifier(String identifier) {
+        this.identifier = identifier;
+    }
+
+    /**
+     * Returns a map of all attributes associated with this user group. Each
+     * entry key is the attribute identifier, while each value is the attribute
+     * value itself.
+     *
+     * @return
+     *     The attribute map for this user group.
+     */
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+
+    /**
+     * Sets the map of all attributes associated with this user group. Each
+     * entry key is the attribute identifier, while each value is the attribute
+     * value itself.
+     *
+     * @param attributes
+     *     The attribute map for this user group.
+     */
+    public void setAttributes(Map<String, String> attributes) {
+        this.attributes = attributes;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/APIUserGroupWrapper.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/APIUserGroupWrapper.java b/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/APIUserGroupWrapper.java
new file mode 100644
index 0000000..5dc6d7b
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/APIUserGroupWrapper.java
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+package org.apache.guacamole.rest.usergroup;
+
+import java.util.Map;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleUnsupportedException;
+import org.apache.guacamole.net.auth.RelatedObjectSet;
+import org.apache.guacamole.net.auth.UserGroup;
+import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
+import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
+
+/**
+ * A wrapper to make an APIUserGroup look like a UserGroup. Useful where an
+ * org.apache.guacamole.net.auth.UserGroup is required. As a simple wrapper for
+ * APIUserGroup, access to permissions, groups, and members is not provided.
+ * Any attempt to access or manipulate permissions or group membership via an
+ * APIUserGroupWrapper will result in an exception.
+ */
+public class APIUserGroupWrapper implements UserGroup {
+
+    /**
+     * The wrapped APIUserGroup.
+     */
+    private final APIUserGroup apiUserGroup;
+
+    /**
+     * Creates a new APIUserGroupWrapper which wraps the given APIUserGroup,
+     * exposing its properties through the UserGroup interface.
+     *
+     * @param apiUserGroup
+     *     The APIUserGroup to wrap.
+     */
+    public APIUserGroupWrapper(APIUserGroup apiUserGroup) {
+        this.apiUserGroup = apiUserGroup;
+    }
+
+    @Override
+    public String getIdentifier() {
+        return apiUserGroup.getIdentifier();
+    }
+
+    @Override
+    public void setIdentifier(String identifier) {
+        apiUserGroup.setIdentifier(identifier);
+    }
+
+    @Override
+    public Map<String, String> getAttributes() {
+        return apiUserGroup.getAttributes();
+    }
+
+    @Override
+    public void setAttributes(Map<String, String> attributes) {
+        apiUserGroup.setAttributes(attributes);
+    }
+
+    @Override
+    public SystemPermissionSet getSystemPermissions()
+            throws GuacamoleException {
+        throw new GuacamoleUnsupportedException("APIUserGroupWrapper does not provide permission access.");
+    }
+
+    @Override
+    public ObjectPermissionSet getConnectionPermissions()
+            throws GuacamoleException {
+        throw new GuacamoleUnsupportedException("APIUserGroupWrapper does not provide permission access.");
+    }
+
+    @Override
+    public ObjectPermissionSet getConnectionGroupPermissions()
+            throws GuacamoleException {
+        throw new GuacamoleUnsupportedException("APIUserGroupWrapper does not provide permission access.");
+    }
+
+    @Override
+    public ObjectPermissionSet getSharingProfilePermissions() throws GuacamoleException {
+        throw new GuacamoleUnsupportedException("APIUserGroupWrapper does not provide permission access.");
+    }
+
+    @Override
+    public ObjectPermissionSet getUserPermissions()
+            throws GuacamoleException {
+        throw new GuacamoleUnsupportedException("APIUserGroupWrapper does not provide permission access.");
+    }
+
+    @Override
+    public ObjectPermissionSet getUserGroupPermissions()
+            throws GuacamoleException {
+        throw new GuacamoleUnsupportedException("APIUserGroupWrapper does not provide permission access.");
+    }
+
+    @Override
+    public ObjectPermissionSet getActiveConnectionPermissions()
+            throws GuacamoleException {
+        throw new GuacamoleUnsupportedException("APIUserGroupWrapper does not provide permission access.");
+    }
+
+    @Override
+    public RelatedObjectSet getUserGroups() throws GuacamoleException {
+        throw new GuacamoleUnsupportedException("APIUserGroupWrapper does not provide group access.");
+    }
+
+    @Override
+    public RelatedObjectSet getMemberUsers() throws GuacamoleException {
+        throw new GuacamoleUnsupportedException("APIUserGroupWrapper does not provide member access.");
+    }
+
+    @Override
+    public RelatedObjectSet getMemberUserGroups() throws GuacamoleException {
+        throw new GuacamoleUnsupportedException("APIUserGroupWrapper does not provide member access.");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupDirectoryResource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupDirectoryResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupDirectoryResource.java
new file mode 100644
index 0000000..b89db6d
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupDirectoryResource.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package org.apache.guacamole.rest.usergroup;
+
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.net.auth.UserGroup;
+import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory;
+import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
+import org.apache.guacamole.rest.directory.DirectoryResource;
+
+/**
+ * A REST resource which abstracts the operations available on a Directory of
+ * UserGroups.
+ */
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class UserGroupDirectoryResource extends DirectoryResource<UserGroup, APIUserGroup> {
+
+    /**
+     * Creates a new UserGroupDirectoryResource which exposes the operations
+     * and subresources available for the given UserGroup Directory.
+     *
+     * @param userContext
+     *     The UserContext associated with the given Directory.
+     *
+     * @param directory
+     *     The Directory being exposed.
+     *
+     * @param translator
+     *     A DirectoryObjectTranslator implementation which handles
+     *     UserGroups.
+     *
+     * @param resourceFactory
+     *     A factory which can be used to create instances of resources
+     *     representing UserGroups.
+     */
+    @AssistedInject
+    public UserGroupDirectoryResource(@Assisted UserContext userContext,
+            @Assisted Directory<UserGroup> directory,
+            DirectoryObjectTranslator<UserGroup, APIUserGroup> translator,
+            DirectoryObjectResourceFactory<UserGroup, APIUserGroup> resourceFactory) {
+        super(userContext, directory, translator, resourceFactory);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupModule.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupModule.java b/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupModule.java
new file mode 100644
index 0000000..3e81a2d
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupModule.java
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+package org.apache.guacamole.rest.usergroup;
+
+import com.google.inject.AbstractModule;
+import org.apache.guacamole.rest.directory.DirectoryObjectResourceFactory;
+import org.apache.guacamole.rest.directory.DirectoryResourceFactory;
+import com.google.inject.TypeLiteral;
+import com.google.inject.assistedinject.FactoryModuleBuilder;
+import org.apache.guacamole.net.auth.UserGroup;
+import org.apache.guacamole.rest.directory.DirectoryObjectResource;
+import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
+import org.apache.guacamole.rest.directory.DirectoryResource;
+
+/**
+ * Guice Module which configures injections required for handling UserGroup
+ * resources via the REST API.
+ */
+public class UserGroupModule extends AbstractModule {
+
+    @Override
+    protected void configure() {
+
+        // Create the required DirectoryResourceFactory implementation
+        install(new FactoryModuleBuilder()
+                .implement(
+                    new TypeLiteral<DirectoryResource<UserGroup, APIUserGroup>>() {},
+                    UserGroupDirectoryResource.class
+                )
+                .build(new TypeLiteral<DirectoryResourceFactory<UserGroup, APIUserGroup>>() {}));
+
+        // Create the required DirectoryObjectResourceFactory implementation
+        install(new FactoryModuleBuilder()
+                .implement(
+                    new TypeLiteral<DirectoryObjectResource<UserGroup, APIUserGroup>>() {},
+                    UserGroupResource.class
+                )
+                .build(new TypeLiteral<DirectoryObjectResourceFactory<UserGroup, APIUserGroup>>() {}));
+
+        // Bind translator for converting between UserGroup and APIUserGroup
+        bind(new TypeLiteral<DirectoryObjectTranslator<UserGroup, APIUserGroup>>() {})
+                .to(UserGroupObjectTranslator.class);
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupObjectTranslator.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupObjectTranslator.java b/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupObjectTranslator.java
new file mode 100644
index 0000000..721156a
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupObjectTranslator.java
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+package org.apache.guacamole.rest.usergroup;
+
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.net.auth.UserGroup;
+import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
+
+/**
+ * Translator which converts between UserGroup objects and APIUserGroup
+ * objects.
+ */
+public class UserGroupObjectTranslator
+        extends DirectoryObjectTranslator<UserGroup, APIUserGroup> {
+
+    @Override
+    public APIUserGroup toExternalObject(UserGroup object)
+            throws GuacamoleException {
+        return new APIUserGroup(object);
+    }
+
+    @Override
+    public UserGroup toInternalObject(APIUserGroup object)
+            throws GuacamoleException {
+        return new APIUserGroupWrapper(object);
+    }
+
+    @Override
+    public void applyExternalChanges(UserGroup existingObject,
+            APIUserGroup object) throws GuacamoleException {
+
+        // Update user attributes
+        existingObject.setAttributes(object.getAttributes());
+
+    }
+
+    @Override
+    public void filterExternalObject(UserContext userContext, APIUserGroup object)
+            throws GuacamoleException {
+
+        // Filter object attributes by defined schema
+        object.setAttributes(filterAttributes(userContext.getUserAttributes(),
+                object.getAttributes()));
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupResource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupResource.java
new file mode 100644
index 0000000..350b59f
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/UserGroupResource.java
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+package org.apache.guacamole.rest.usergroup;
+
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.net.auth.UserGroup;
+import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.UserContext;
+import org.apache.guacamole.rest.directory.DirectoryObjectResource;
+import org.apache.guacamole.rest.directory.DirectoryObjectTranslator;
+import org.apache.guacamole.rest.identifier.RelatedObjectSetResource;
+import org.apache.guacamole.rest.permission.PermissionSetResource;
+
+/**
+ * A REST resource which abstracts the operations available on an existing
+ * UserGroup.
+ */
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class UserGroupResource
+        extends DirectoryObjectResource<UserGroup, APIUserGroup> {
+
+    /**
+     * The UserGroup object represented by this UserGroupResource.
+     */
+    private final UserGroup userGroup;
+
+    /**
+     * Creates a new UserGroupResource which exposes the operations and
+     * subresources available for the given UserGroup.
+     *
+     * @param userContext
+     *     The UserContext associated with the given Directory.
+     *
+     * @param directory
+     *     The Directory which contains the given UserGroup.
+     *
+     * @param userGroup
+     *     The UserGroup that this UserGroupResource should represent.
+     *
+     * @param translator
+     *     A DirectoryObjectTranslator implementation which handles Users.
+     */
+    @AssistedInject
+    public UserGroupResource(@Assisted UserContext userContext,
+            @Assisted Directory<UserGroup> directory,
+            @Assisted UserGroup userGroup,
+            DirectoryObjectTranslator<UserGroup, APIUserGroup> translator) {
+        super(userContext, directory, userGroup, translator);
+        this.userGroup = userGroup;
+    }
+
+    /**
+     * Returns a resource which abstracts operations available on the set of
+     * user groups of which the UserGroup represented by this UserGroupResource
+     * is a member.
+     *
+     * @return
+     *     A resource which represents the set of user groups of which the
+     *     UserGroup represented by this UserGroupResource is a member.
+     *
+     * @throws GuacamoleException
+     *     If the group membership for this user group cannot be retrieved.
+     */
+    @Path("userGroups")
+    public RelatedObjectSetResource getUserGroups() throws GuacamoleException {
+        return new RelatedObjectSetResource(userGroup.getUserGroups());
+    }
+
+    /**
+     * Returns a resource which abstracts operations available on the set of
+     * users which are members of the UserGroup represented by this
+     * UserGroupResource.
+     *
+     * @return
+     *     A resource which represents the set of users which are members of
+     *     the UserGroup represented by this UserGroupResource.
+     *
+     * @throws GuacamoleException
+     *     If the members of this user group cannot be retrieved.
+     */
+    @Path("memberUsers")
+    public RelatedObjectSetResource getMemberUsers() throws GuacamoleException {
+        return new RelatedObjectSetResource(userGroup.getMemberUsers());
+    }
+
+    /**
+     * Returns a resource which abstracts operations available on the set of
+     * user groups which are members of the UserGroup represented by this
+     * UserGroupResource.
+     *
+     * @return
+     *     A resource which represents the set of user groups which are
+     *     members of the UserGroup represented by this UserGroupResource.
+     *
+     * @throws GuacamoleException
+     *     If the members of this user group cannot be retrieved.
+     */
+    @Path("memberUserGroups")
+    public RelatedObjectSetResource getMemberUserGroups() throws GuacamoleException {
+        return new RelatedObjectSetResource(userGroup.getMemberUserGroups());
+    }
+
+    /**
+     * Returns a resource which abstracts operations available on the overall
+     * permissions granted directly to the UserGroup represented by this
+     * UserGroupResource.
+     *
+     * @return
+     *     A resource which represents the permissions granted to the
+     *     UserGroup represented by this UserGroupResource.
+     */
+    @Path("permissions")
+    public PermissionSetResource getPermissions() {
+        return new PermissionSetResource(userGroup);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/c26cf60e/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/package-info.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/package-info.java b/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/package-info.java
new file mode 100644
index 0000000..06ab479
--- /dev/null
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/usergroup/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+/**
+ * Classes related to manipulating user groups via the Guacamole REST API.
+ */
+package org.apache.guacamole.rest.usergroup;


[2/2] guacamole-client git commit: GUACAMOLE-220: Merge user group REST API.

Posted by vn...@apache.org.
GUACAMOLE-220: Merge user group REST API.


Project: http://git-wip-us.apache.org/repos/asf/guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-client/commit/fed51332
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-client/tree/fed51332
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-client/diff/fed51332

Branch: refs/heads/master
Commit: fed51332952a23c5e9a5ddab38ded23f092299b8
Parents: 2424480 c26cf60
Author: Nick Couchman <vn...@apache.org>
Authored: Fri May 4 21:22:38 2018 -0400
Committer: Nick Couchman <vn...@apache.org>
Committed: Fri May 4 21:22:38 2018 -0400

----------------------------------------------------------------------
 .../guacamole/rest/RESTServiceModule.java       |   2 +
 .../rest/identifier/RelatedObjectSetPatch.java  |  89 ++++++++++
 .../identifier/RelatedObjectSetResource.java    | 164 +++++++++++++++++++
 .../guacamole/rest/identifier/package-info.java |  24 +++
 .../rest/permission/APIPermissionSet.java       |  34 ++++
 .../rest/permission/PermissionSetResource.java  |  85 ++++++----
 .../guacamole/rest/schema/SchemaResource.java   |  19 +++
 .../rest/session/UserContextResource.java       |  27 +++
 .../guacamole/rest/user/UserResource.java       |  18 ++
 .../guacamole/rest/usergroup/APIUserGroup.java  | 106 ++++++++++++
 .../rest/usergroup/APIUserGroupWrapper.java     | 131 +++++++++++++++
 .../usergroup/UserGroupDirectoryResource.java   |  68 ++++++++
 .../rest/usergroup/UserGroupModule.java         |  63 +++++++
 .../usergroup/UserGroupObjectTranslator.java    |  65 ++++++++
 .../rest/usergroup/UserGroupResource.java       | 141 ++++++++++++++++
 .../guacamole/rest/usergroup/package-info.java  |  23 +++
 16 files changed, 1029 insertions(+), 30 deletions(-)
----------------------------------------------------------------------