diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java index 67fb66258..0ec05e3f0 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connection/ConnectionRESTService.java @@ -45,10 +45,10 @@ import org.glyptodon.guacamole.net.auth.ConnectionRecord; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; import org.glyptodon.guacamole.net.auth.UserContext; -import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission; import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; -import org.glyptodon.guacamole.net.auth.permission.Permission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; import org.glyptodon.guacamole.net.auth.permission.SystemPermission; +import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; import org.glyptodon.guacamole.net.basic.rest.ObjectRetrievalService; import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; @@ -71,12 +71,6 @@ public class ConnectionRESTService { */ private static final Logger logger = LoggerFactory.getLogger(ConnectionRESTService.class); - /** - * System administration permission. - */ - private static final Permission SYSTEM_PERMISSION = - new SystemPermission(SystemPermission.Type.ADMINISTER); - /** * A service for authenticating users from auth tokens. */ @@ -143,9 +137,13 @@ public class ConnectionRESTService { UserContext userContext = authenticationService.getUserContext(authToken); User self = userContext.self(); + // Retrieve permission sets + SystemPermissionSet systemPermissions = self.getSystemPermissions(); + ObjectPermissionSet connectionPermissions = self.getConnectionPermissions(); + // Deny access if adminstrative or update permission is missing - if (!self.hasPermission(SYSTEM_PERMISSION) - && !self.hasPermission(new ConnectionPermission(ObjectPermission.Type.UPDATE, connectionID))) + if (!systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER) + && !connectionPermissions.hasPermission(ObjectPermission.Type.UPDATE, connectionID)) throw new GuacamoleSecurityException("Permission to read connection parameters denied."); // Retrieve the requested connection diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java index 049a9d1bc..651ddbd85 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/connectiongroup/ConnectionGroupRESTService.java @@ -45,10 +45,10 @@ import org.glyptodon.guacamole.net.auth.ConnectionGroup; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; import org.glyptodon.guacamole.net.auth.UserContext; -import org.glyptodon.guacamole.net.auth.permission.ConnectionGroupPermission; -import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission; import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; import org.glyptodon.guacamole.net.auth.permission.SystemPermission; +import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; import org.glyptodon.guacamole.net.basic.rest.AuthProviderRESTExposure; import org.glyptodon.guacamole.net.basic.rest.ObjectRetrievalService; import org.glyptodon.guacamole.net.basic.rest.auth.AuthenticationService; @@ -102,14 +102,14 @@ public class ConnectionGroupRESTService { */ private boolean hasConnectionPermission(User user, String identifier, List permissions) throws GuacamoleException { + + // Retrieve connection permissions + ObjectPermissionSet connectionPermissions = user.getConnectionPermissions(); // Determine whether user has at least one of the given permissions for (ObjectPermission.Type permission : permissions) { - - ConnectionPermission connectionPermission = new ConnectionPermission(permission, identifier); - if (user.hasPermission(connectionPermission)) + if (connectionPermissions.hasPermission(permission, identifier)) return true; - } // None of the given permissions were present @@ -137,13 +137,13 @@ public class ConnectionGroupRESTService { private boolean hasConnectionGroupPermission(User user, String identifier, List permissions) throws GuacamoleException { + // Retrieve connection group permissions + ObjectPermissionSet connectionGroupPermissions = user.getConnectionGroupPermissions(); + // Determine whether user has at least one of the given permissions for (ObjectPermission.Type permission : permissions) { - - ConnectionGroupPermission connectionGroupPermission = new ConnectionGroupPermission(permission, identifier); - if (user.hasPermission(connectionGroupPermission)) + if (connectionGroupPermissions.hasPermission(permission, identifier)) return true; - } // None of the given permissions were present @@ -185,7 +185,8 @@ public class ConnectionGroupRESTService { User self = userContext.self(); // An admin user has access to any connection or connection group - boolean isAdmin = self.hasPermission(new SystemPermission(SystemPermission.Type.ADMINISTER)); + SystemPermissionSet systemPermissions = self.getSystemPermissions(); + boolean isAdmin = systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER); // Retrieve specified connection group ConnectionGroup connectionGroup; diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermissionSet.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermissionSet.java index 450cbc823..1d4a14774 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermissionSet.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/permission/APIPermissionSet.java @@ -24,17 +24,14 @@ package org.glyptodon.guacamole.net.basic.rest.permission; import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; import org.glyptodon.guacamole.GuacamoleException; -import org.glyptodon.guacamole.GuacamoleServerException; -import org.glyptodon.guacamole.net.auth.permission.ConnectionGroupPermission; -import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission; +import org.glyptodon.guacamole.net.auth.User; import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; -import org.glyptodon.guacamole.net.auth.permission.Permission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; import org.glyptodon.guacamole.net.auth.permission.SystemPermission; -import org.glyptodon.guacamole.net.auth.permission.UserPermission; +import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; /** * The set of permissions which are granted to a specific user, organized by @@ -50,97 +47,26 @@ public class APIPermissionSet { /** * Map of connection ID to the set of granted permissions. */ - private Map> connectionPermissions = new HashMap>(); + private Map> connectionPermissions = + new HashMap>(); /** * Map of connection group ID to the set of granted permissions. */ - private Map> connectionGroupPermissions = new HashMap>(); + private Map> connectionGroupPermissions = + new HashMap>(); /** * Map of user ID to the set of granted permissions. */ - private Map> userPermissions = new HashMap>(); + private Map> userPermissions = + new HashMap>(); /** * Set of all granted system-level permissions. */ - private EnumSet systemPermissions = EnumSet.noneOf(SystemPermission.Type.class); - - /** - * Adds the given object permission to the given map of object identifier - * to permission set. - * - * @param permissions - * The map to add the given permission to. - * - * @param permission - * The permission to add. - */ - private void addPermission(Map> permissions, ObjectPermission permission) { - - // Pull set of permissions for given object - String id = permission.getObjectIdentifier(); - EnumSet types = permissions.get(id); - - // If set does not yet exist, create it - if (types == null) { - types = EnumSet.of(permission.getType()); - permissions.put(id, types); - } - - // Otherwise, add the specified permission - else - types.add(permission.getType()); - - } - - /** - * Adds the given system-level permission to the given set of granted - * system permissions. - * - * @param permissions - * The set of system permissions to add the given permission to. - * - * @param permission - * The permission to add. - */ - private void addPermission(EnumSet permissions, SystemPermission permission) { - permissions.add(permission.getType()); - } - - /** - * Adds the given permission to the appropriate type-specific set or map of - * permissions based on the permission class. Only connection, connection - * group, user, and system permissions are supported. Unsupported - * permission types will result in a GuacamoleException being thrown. - * - * @param permission The permission to add. - * @throws GuacamoleException If the permission is of an unsupported type. - */ - private void addPermission(Permission permission) throws GuacamoleException { - - // Connection permissions - if (permission instanceof ConnectionPermission) - addPermission(connectionPermissions, (ConnectionPermission) permission); - - // Connection group permissions - else if (permission instanceof ConnectionGroupPermission) - addPermission(connectionGroupPermissions, (ConnectionGroupPermission) permission); - - // User permissions - else if (permission instanceof UserPermission) - addPermission(userPermissions, (UserPermission) permission); - - // System permissions - else if (permission instanceof SystemPermission) - addPermission(systemPermissions, (SystemPermission) permission); - - // Unknown / unsupported permission type - else - throw new GuacamoleServerException("Serialization of permission type \"" + permission.getClass() + "\" not implemented."); - - } + private Set systemPermissions = + EnumSet.noneOf(SystemPermission.Type.class); /** * Creates a new permission set which contains no granted permissions. Any @@ -151,37 +77,83 @@ public class APIPermissionSet { } /** - * Creates a new permission set having the given permissions. + * Adds the system permissions from the given SystemPermissionSet to the + * Set of system permissions provided. * * @param permissions - * The permissions to initially store within the permission set. + * The Set to add system permissions to. + * + * @param permSet + * The SystemPermissionSet containing the system permissions to add. * * @throws GuacamoleException - * If any of the given permissions are of an unsupported type. + * If an error occurs while retrieving system permissions from the + * SystemPermissionSet. */ - public APIPermissionSet(Iterable permissions) throws GuacamoleException { + private void addSystemPermissions(Set permissions, + SystemPermissionSet permSet) throws GuacamoleException { - // Add all provided permissions - for (Permission permission : permissions) - addPermission(permission); + // Add all provided system permissions + for (SystemPermission permission : permSet.getPermissions()) + permissions.add(permission.getType()); } - + /** - * Creates a new permission set having the given permissions. + * Adds the object permissions from the given ObjectPermissionSet to the + * Map of object permissions provided. * * @param permissions - * The permissions to initially store within the permission set. + * The Map to add object permissions to. + * + * @param permSet + * The ObjectPermissionSet containing the object permissions to add. * * @throws GuacamoleException - * If any of the given permissions are of an unsupported type. + * If an error occurs while retrieving object permissions from the + * ObjectPermissionSet. */ - public APIPermissionSet(Permission... permissions) throws GuacamoleException { + private void addObjectPermissions(Map> permissions, + ObjectPermissionSet permSet) throws GuacamoleException { - // Add all provided permissions - for (Permission permission : permissions) - addPermission(permission); + // Add all provided object permissions + for (ObjectPermission permission : permSet.getPermissions()) { + // Get associated set of permissions + String identifier = permission.getObjectIdentifier(); + Set objectPermissions = permissions.get(identifier); + + // Create new set if none yet exists + if (objectPermissions == null) + permissions.put(identifier, EnumSet.of(permission.getType())); + + // Otherwise add to existing set + else + objectPermissions.add(permission.getType()); + + } + + } + + /** + * Creates a new permission set containing all permissions currently + * granted to the given user. + * + * @param user + * The user whose permissions should be stored within this permission + * set. + * + * @throws GuacamoleException + * If an error occurs while retrieving the user's permissions. + */ + public APIPermissionSet(User user) throws GuacamoleException { + + // Add all permissions from the provided user + addSystemPermissions(systemPermissions, user.getSystemPermissions()); + addObjectPermissions(connectionPermissions, user.getConnectionPermissions()); + addObjectPermissions(connectionGroupPermissions, user.getConnectionGroupPermissions()); + addObjectPermissions(userPermissions, user.getUserPermissions()); + } /** @@ -195,7 +167,7 @@ public class APIPermissionSet { * A map of connection IDs to the set of permissions granted for that * connection. */ - public Map> getConnectionPermissions() { + public Map> getConnectionPermissions() { return connectionPermissions; } @@ -210,7 +182,7 @@ public class APIPermissionSet { * A map of connection group IDs to the set of permissions granted for * that connection group. */ - public Map> getConnectionGroupPermissions() { + public Map> getConnectionGroupPermissions() { return connectionGroupPermissions; } @@ -223,7 +195,7 @@ public class APIPermissionSet { * @return * A map of user IDs to the set of permissions granted for that user. */ - public Map> getUserPermissions() { + public Map> getUserPermissions() { return userPermissions; } @@ -236,7 +208,7 @@ public class APIPermissionSet { * @return * The set of granted system-level permissions. */ - public EnumSet getSystemPermissions() { + public Set getSystemPermissions() { return systemPermissions; } @@ -249,7 +221,7 @@ public class APIPermissionSet { * @param connectionPermissions * The map which must replace the currently-stored map of permissions. */ - public void setConnectionPermissions(Map> connectionPermissions) { + public void setConnectionPermissions(Map> connectionPermissions) { this.connectionPermissions = connectionPermissions; } @@ -262,7 +234,7 @@ public class APIPermissionSet { * @param connectionGroupPermissions * The map which must replace the currently-stored map of permissions. */ - public void setConnectionGroupPermissions(Map> connectionGroupPermissions) { + public void setConnectionGroupPermissions(Map> connectionGroupPermissions) { this.connectionGroupPermissions = connectionGroupPermissions; } @@ -274,7 +246,7 @@ public class APIPermissionSet { * @param userPermissions * The map which must replace the currently-stored map of permissions. */ - public void setUserPermissions(Map> userPermissions) { + public void setUserPermissions(Map> userPermissions) { this.userPermissions = userPermissions; } @@ -286,7 +258,7 @@ public class APIPermissionSet { * @param systemPermissions * The set which must replace the currently-stored set of permissions. */ - public void setSystemPermissions(EnumSet systemPermissions) { + public void setSystemPermissions(Set systemPermissions) { this.systemPermissions = systemPermissions; } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUserWrapper.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUserWrapper.java index dec5a9a65..d6032bbd2 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUserWrapper.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/APIUserWrapper.java @@ -22,15 +22,17 @@ package org.glyptodon.guacamole.net.basic.rest.user; -import java.util.Collections; -import java.util.Set; import org.glyptodon.guacamole.GuacamoleException; +import org.glyptodon.guacamole.GuacamoleUnsupportedException; import org.glyptodon.guacamole.net.auth.User; -import org.glyptodon.guacamole.net.auth.permission.Permission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; +import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; /** - * A wrapper to make an APIConnection look like a User. Useful where a - * org.glyptodon.guacamole.net.auth.User is required. + * A wrapper to make an APIUser look like a User. Useful where an + * org.glyptodon.guacamole.net.auth.User is required. As a simple wrapper for + * APIUser, access to permissions is not provided. Any attempt to access or + * manipulate permissions on an APIUserWrapper will result in an exception. * * @author James Muehlner */ @@ -41,12 +43,6 @@ public class APIUserWrapper implements User { */ private final APIUser apiUser; - /** - * The set of permissions for this user. - * NOTE: Not exposed by the REST endpoints. - */ - private Set permissionSet = Collections.EMPTY_SET; - /** * Wrap a given APIUser to expose as a User. * @param apiUser The APIUser to wrap. @@ -55,16 +51,6 @@ public class APIUserWrapper implements User { this.apiUser = apiUser; } - /** - * Wrap a given APIUser to expose as a User, with the given permission set. - * @param apiUser The APIUser to wrap. - * @param permissionSet The set of permissions for the wrapped user. - */ - public APIUserWrapper(APIUser apiUser, Set permissionSet) { - this.apiUser = apiUser; - this.permissionSet = permissionSet; - } - @Override public String getUsername() { return apiUser.getUsername(); @@ -86,23 +72,27 @@ public class APIUserWrapper implements User { } @Override - public Set getPermissions() throws GuacamoleException { - return permissionSet; + public SystemPermissionSet getSystemPermissions() + throws GuacamoleException { + throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access."); } @Override - public boolean hasPermission(Permission permission) throws GuacamoleException { - return permissionSet.contains(permission); + public ObjectPermissionSet getConnectionPermissions() + throws GuacamoleException { + throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access."); } @Override - public void addPermission(Permission permission) throws GuacamoleException { - throw new UnsupportedOperationException("Operation not supported."); + public ObjectPermissionSet getConnectionGroupPermissions() + throws GuacamoleException { + throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access."); } @Override - public void removePermission(Permission permission) throws GuacamoleException { - throw new UnsupportedOperationException("Operation not supported."); + public ObjectPermissionSet getUserPermissions() + throws GuacamoleException { + throw new GuacamoleUnsupportedException("APIUserWrapper does not provide permission access."); } } diff --git a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java index 8f12aff47..a0537656b 100644 --- a/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java +++ b/guacamole/src/main/java/org/glyptodon/guacamole/net/basic/rest/user/UserRESTService.java @@ -24,6 +24,7 @@ package org.glyptodon.guacamole.net.basic.rest.user; import com.google.inject.Inject; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.UUID; import javax.ws.rs.Consumes; @@ -43,12 +44,12 @@ import org.glyptodon.guacamole.GuacamoleResourceNotFoundException; import org.glyptodon.guacamole.net.auth.Directory; import org.glyptodon.guacamole.net.auth.User; import org.glyptodon.guacamole.net.auth.UserContext; -import org.glyptodon.guacamole.net.auth.permission.ConnectionGroupPermission; -import org.glyptodon.guacamole.net.auth.permission.ConnectionPermission; import org.glyptodon.guacamole.net.auth.permission.ObjectPermission; +import org.glyptodon.guacamole.net.auth.permission.ObjectPermissionSet; import org.glyptodon.guacamole.net.auth.permission.Permission; +import org.glyptodon.guacamole.net.auth.permission.PermissionSet; import org.glyptodon.guacamole.net.auth.permission.SystemPermission; -import org.glyptodon.guacamole.net.auth.permission.UserPermission; +import org.glyptodon.guacamole.net.auth.permission.SystemPermissionSet; import org.glyptodon.guacamole.net.basic.rest.APIPatch; import static org.glyptodon.guacamole.net.basic.rest.APIPatch.Operation.add; import static org.glyptodon.guacamole.net.basic.rest.APIPatch.Operation.remove; @@ -132,13 +133,13 @@ public class UserRESTService { private boolean hasUserPermission(User user, String username, List permissions) throws GuacamoleException { + // Retrieve user permissions + ObjectPermissionSet userPermissions = user.getUserPermissions(); + // Determine whether user has at least one of the given permissions for (ObjectPermission.Type permission : permissions) { - - UserPermission userPermission = new UserPermission(permission, username); - if (user.hasPermission(userPermission)) + if (userPermissions.hasPermission(permission, username)) return true; - } // None of the given permissions were present @@ -181,7 +182,8 @@ public class UserRESTService { permissions = null; // An admin user has access to any user - boolean isAdmin = self.hasPermission(new SystemPermission(SystemPermission.Type.ADMINISTER)); + SystemPermissionSet systemPermissions = self.getSystemPermissions(); + boolean isAdmin = systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER); // Get the directory Directory userDirectory = userContext.getUserDirectory(); @@ -379,7 +381,53 @@ public class UserRESTService { throw new GuacamoleResourceNotFoundException("No such user: \"" + username + "\""); } - return new APIPermissionSet(user.getPermissions()); + return new APIPermissionSet(user); + + } + + /** + * Updates the given permission set by adding or removing the given + * permission based on the given patch operation. + * + * @param + * The type of permission stored within the permission set. + * + * @param operation + * The patch operation to perform. + * + * @param permissionSet + * The permission set being modified. + * + * @param permission + * The permission being added or removed from the set. + * + * @throws GuacamoleException + * If an error occurs while modifying the permission set. + */ + private void updatePermissionSet( + APIPatch.Operation operation, + PermissionSet permissionSet, + PermissionType permission) throws GuacamoleException { + + // Add or remove permission based on operation + switch (operation) { + + // Add permission + case add: + permissionSet.addPermissions(Collections.singleton(permission)); + break; + + // Remove permission + case remove: + permissionSet.removePermissions(Collections.singleton(permission)); + break; + + // Unsupported patch operation + default: + throw new HTTPException(Status.BAD_REQUEST, + "Unsupported patch operation: \"" + operation + "\""); + + } } @@ -423,8 +471,6 @@ public class UserRESTService { // Apply all patch operations individually for (APIPatch patch : patches) { - Permission permission; - String path = patch.getPath(); // Create connection permission if path has connection prefix @@ -434,8 +480,9 @@ public class UserRESTService { String identifier = path.substring(CONNECTION_PERMISSION_PATCH_PATH_PREFIX.length()); ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue()); - // Create corresponding permission - permission = new ConnectionPermission(type, identifier); + // Create and update corresponding permission + ObjectPermission permission = new ObjectPermission(type, identifier); + updatePermissionSet(patch.getOp(), user.getConnectionPermissions(), permission); } @@ -446,8 +493,9 @@ public class UserRESTService { String identifier = path.substring(CONNECTION_GROUP_PERMISSION_PATCH_PATH_PREFIX.length()); ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue()); - // Create corresponding permission - permission = new ConnectionGroupPermission(type, identifier); + // Create and update corresponding permission + ObjectPermission permission = new ObjectPermission(type, identifier); + updatePermissionSet(patch.getOp(), user.getConnectionGroupPermissions(), permission); } @@ -458,9 +506,10 @@ public class UserRESTService { String identifier = path.substring(USER_PERMISSION_PATCH_PATH_PREFIX.length()); ObjectPermission.Type type = ObjectPermission.Type.valueOf(patch.getValue()); - // Create corresponding permission - permission = new UserPermission(type, identifier); - + // Create and update corresponding permission + ObjectPermission permission = new ObjectPermission(type, identifier); + updatePermissionSet(patch.getOp(), user.getUserPermissions(), permission); + } // Create system permission if path is system path @@ -469,8 +518,9 @@ public class UserRESTService { // Get identifier and type from patch operation SystemPermission.Type type = SystemPermission.Type.valueOf(patch.getValue()); - // Create corresponding permission - permission = new SystemPermission(type); + // Create and update corresponding permission + SystemPermission permission = new SystemPermission(type); + updatePermissionSet(patch.getOp(), user.getSystemPermissions(), permission); } @@ -478,26 +528,6 @@ public class UserRESTService { else throw new HTTPException(Status.BAD_REQUEST, "Unsupported patch path: \"" + path + "\""); - // Add or remove permission based on operation - switch (patch.getOp()) { - - // Add permission - case add: - user.addPermission(permission); - break; - - // Remove permission - case remove: - user.removePermission(permission); - break; - - // Unsupported patch operation - default: - throw new HTTPException(Status.BAD_REQUEST, - "Unsupported patch operation: \"" + patch.getOp() + "\""); - - } - } // end for each patch operation // Save the permission changes