GUACAMOLE-152: Merge changes adding support for explicitly specifying the zoom level.
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.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 @@
install(new ConnectionGroupModule());
install(new SharingProfileModule());
install(new UserModule());
+ install(new UserGroupModule());
// Set up the servlet and JSON mappings
bind(GuiceContainer.class);
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);
+
+ }
+
+}
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);
+
+ }
+
+}
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;
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 @@
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 @@
addObjectPermissions(sharingProfilePermissions, permissions.getSharingProfilePermissions());
addObjectPermissions(activeConnectionPermissions, permissions.getActiveConnectionPermissions());
addObjectPermissions(userPermissions, permissions.getUserPermissions());
+ addObjectPermissions(userGroupPermissions, permissions.getUserGroupPermissions());
}
@@ -240,6 +247,20 @@
}
/**
+ * 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 @@
}
/**
+ * 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.
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.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 @@
/**
* 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 @@
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 @@
}
+ // 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 @@
} // 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());
}
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 @@
}
/**
+ * 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
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.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.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 @@
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 @@
}
/**
+ * 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.
*
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..db0aba5a 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.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 @@
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());
+ }
+
}
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;
+ }
+
+}
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.");
+ }
+
+}
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);
+ }
+
+}
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);
+
+ }
+
+}
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()));
+
+ }
+
+}
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);
+ }
+
+}
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;
diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js
index 1679886..e4a91db 100644
--- a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js
+++ b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js
@@ -416,10 +416,8 @@
}
// Upon success, save any changed permissions
- return permissionService.patchPermissions($scope.dataSource, $scope.user.username, $scope.permissionsAdded, $scope.permissionsRemoved)
- .then(function patchedUserPermissions() {
- $location.url('/settings/users');
- });
+ return permissionService.patchPermissions($scope.dataSource, $scope.user.username,
+ $scope.permissionsAdded, $scope.permissionsRemoved);
});