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/04/22 19:46:42 UTC

[2/8] guacamole-client git commit: GUACAMOLE-220: Use effective permissions to test user access to resources.

GUACAMOLE-220: Use effective permissions to test user access to resources.


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

Branch: refs/heads/master
Commit: 393e1ab647e5e8f766f9485cf411651ac82f41d0
Parents: dfb07ed
Author: Michael Jumper <mj...@apache.org>
Authored: Thu Apr 19 14:18:02 2018 -0700
Committer: Michael Jumper <mj...@apache.org>
Committed: Thu Apr 19 15:29:11 2018 -0700

----------------------------------------------------------------------
 .../rest/connection/ConnectionResource.java     |   7 +-
 .../connectiongroup/ConnectionGroupTree.java    |   8 +-
 .../rest/directory/DirectoryResource.java       |   6 +-
 .../rest/permission/APIPermissionSet.java       |  41 +++---
 .../sharingprofile/SharingProfileResource.java  |   7 +-
 .../guacamole/rest/user/UserResource.java       |  21 ++-
 .../controllers/manageConnectionController.js   |   2 +-
 .../manageConnectionGroupController.js          |   2 +-
 .../manageSharingProfileController.js           |   2 +-
 .../manage/controllers/manageUserController.js  |   2 +-
 .../app/navigation/services/userPageService.js  |   4 +-
 .../app/rest/services/permissionService.js      | 137 ++++++++++++-------
 .../directives/guacSettingsConnections.js       |   2 +-
 .../directives/guacSettingsPreferences.js       |   2 +-
 .../settings/directives/guacSettingsUsers.js    |   2 +-
 15 files changed, 155 insertions(+), 90 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/393e1ab6/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java
index 0b63b66..ac0a282 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connection/ConnectionResource.java
@@ -35,6 +35,7 @@ import org.apache.guacamole.GuacamoleSecurityException;
 import org.apache.guacamole.net.auth.Connection;
 import org.apache.guacamole.net.auth.ConnectionRecord;
 import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.Permissions;
 import org.apache.guacamole.rest.directory.DirectoryView;
 import org.apache.guacamole.net.auth.SharingProfile;
 import org.apache.guacamole.net.auth.User;
@@ -119,11 +120,13 @@ public class ConnectionResource extends DirectoryObjectResource<Connection, APIC
     public Map<String, String> getConnectionParameters()
             throws GuacamoleException {
 
+        // Pull effective permissions
         User self = userContext.self();
+        Permissions effective = self.getEffectivePermissions();
 
         // Retrieve permission sets
-        SystemPermissionSet systemPermissions = self.getSystemPermissions();
-        ObjectPermissionSet connectionPermissions = self.getConnectionPermissions();
+        SystemPermissionSet systemPermissions = effective.getSystemPermissions();
+        ObjectPermissionSet connectionPermissions = effective.getConnectionPermissions();
 
         // Deny access if adminstrative or update permission is missing
         String identifier = connection.getIdentifier();

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/393e1ab6/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java
index a850966..1a3cb5f 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/connectiongroup/ConnectionGroupTree.java
@@ -29,8 +29,8 @@ import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.net.auth.Connection;
 import org.apache.guacamole.net.auth.ConnectionGroup;
 import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.Permissions;
 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.permission.ObjectPermission;
 import org.apache.guacamole.net.auth.permission.ObjectPermissionSet;
@@ -356,9 +356,9 @@ public class ConnectionGroupTree {
         retrievedGroups.put(root.getIdentifier(), this.rootAPIGroup);
 
         // Store user's current permissions
-        User self = userContext.self();
-        this.connectionPermissions = self.getConnectionPermissions();
-        this.sharingProfilePermissions = self.getSharingProfilePermissions();
+        Permissions effective = userContext.self().getEffectivePermissions();
+        this.connectionPermissions = effective.getConnectionPermissions();
+        this.sharingProfilePermissions = effective.getSharingProfilePermissions();
 
         // Store required directories
         this.connectionDirectory = userContext.getConnectionDirectory();

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/393e1ab6/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java
index 3cbd481..b0d93a5 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/directory/DirectoryResource.java
@@ -37,6 +37,7 @@ import org.apache.guacamole.GuacamoleResourceNotFoundException;
 import org.apache.guacamole.GuacamoleUnsupportedException;
 import org.apache.guacamole.net.auth.Directory;
 import org.apache.guacamole.net.auth.Identifiable;
+import org.apache.guacamole.net.auth.Permissions;
 import org.apache.guacamole.net.auth.User;
 import org.apache.guacamole.net.auth.UserContext;
 import org.apache.guacamole.net.auth.permission.ObjectPermission;
@@ -143,13 +144,14 @@ public abstract class DirectoryResource<InternalType extends Identifiable, Exter
 
         // An admin user has access to all objects
         User self = userContext.self();
-        SystemPermissionSet systemPermissions = self.getSystemPermissions();
+        Permissions effective = self.getEffectivePermissions();
+        SystemPermissionSet systemPermissions = effective.getSystemPermissions();
         boolean isAdmin = systemPermissions.hasPermission(SystemPermission.Type.ADMINISTER);
 
         // Filter objects, if requested
         Collection<String> identifiers = directory.getIdentifiers();
         if (!isAdmin && permissions != null && !permissions.isEmpty()) {
-            ObjectPermissionSet objectPermissions = self.getUserPermissions();
+            ObjectPermissionSet objectPermissions = effective.getUserPermissions();
             identifiers = objectPermissions.getAccessibleObjects(permissions, identifiers);
         }
 

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/393e1ab6/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 5c15a2b..18c38e8 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
@@ -24,20 +24,20 @@ import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 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.ObjectPermissionSet;
 import org.apache.guacamole.net.auth.permission.SystemPermission;
 import org.apache.guacamole.net.auth.permission.SystemPermissionSet;
 
 /**
- * The set of permissions which are granted to a specific user, organized by
- * object type and, if applicable, identifier. This object can be constructed
- * with arbitrary permissions present, or manipulated after creation through
- * the manipulation or replacement of its collections of permissions, but is
- * otherwise not intended for internal use as a data structure for permissions.
- * Its primary purpose is as a hierarchical format for exchanging granted
- * permissions with REST clients.
+ * The set of permissions which are granted to a specific user or user group,
+ * organized by object type and, if applicable, identifier. This object can be
+ * constructed with arbitrary permissions present, or manipulated after creation
+ * through the manipulation or replacement of its collections of permissions,
+ * but is otherwise not intended for internal use as a data structure for
+ * permissions. Its primary purpose is as a hierarchical format for exchanging
+ * granted permissions with REST clients.
  */
 public class APIPermissionSet {
 
@@ -146,24 +146,23 @@ public class APIPermissionSet {
     
     /**
      * Creates a new permission set containing all permissions currently
-     * granted to the given user.
+     * granted within the given Permissions object.
      *
-     * @param user
-     *     The user whose permissions should be stored within this permission
-     *     set.
+     * @param permissions
+     *     The permissions that should be stored within this permission set.
      *
      * @throws GuacamoleException
-     *     If an error occurs while retrieving the user's permissions.
+     *     If an error occurs while retrieving the permissions.
      */
-    public APIPermissionSet(User user) throws GuacamoleException {
+    public APIPermissionSet(Permissions permissions) throws GuacamoleException {
 
         // Add all permissions from the provided user
-        addSystemPermissions(systemPermissions,           user.getSystemPermissions());
-        addObjectPermissions(connectionPermissions,       user.getConnectionPermissions());
-        addObjectPermissions(connectionGroupPermissions,  user.getConnectionGroupPermissions());
-        addObjectPermissions(sharingProfilePermissions,   user.getSharingProfilePermissions());
-        addObjectPermissions(activeConnectionPermissions, user.getActiveConnectionPermissions());
-        addObjectPermissions(userPermissions,             user.getUserPermissions());
+        addSystemPermissions(systemPermissions,           permissions.getSystemPermissions());
+        addObjectPermissions(connectionPermissions,       permissions.getConnectionPermissions());
+        addObjectPermissions(connectionGroupPermissions,  permissions.getConnectionGroupPermissions());
+        addObjectPermissions(sharingProfilePermissions,   permissions.getSharingProfilePermissions());
+        addObjectPermissions(activeConnectionPermissions, permissions.getActiveConnectionPermissions());
+        addObjectPermissions(userPermissions,             permissions.getUserPermissions());
         
     }
 
@@ -229,7 +228,7 @@ public class APIPermissionSet {
 
     /**
      * Returns a map of user IDs to the set of permissions granted for that
-     * user. If no permissions are granted to a particular user, its ID will
+     * user. If no permissions are granted for a particular user, 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.
      *

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/393e1ab6/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileResource.java
----------------------------------------------------------------------
diff --git a/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileResource.java b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileResource.java
index 7b96de9..93187f2 100644
--- a/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileResource.java
+++ b/guacamole/src/main/java/org/apache/guacamole/rest/sharingprofile/SharingProfileResource.java
@@ -30,6 +30,7 @@ import javax.ws.rs.core.MediaType;
 import org.apache.guacamole.GuacamoleException;
 import org.apache.guacamole.GuacamoleSecurityException;
 import org.apache.guacamole.net.auth.Directory;
+import org.apache.guacamole.net.auth.Permissions;
 import org.apache.guacamole.net.auth.SharingProfile;
 import org.apache.guacamole.net.auth.User;
 import org.apache.guacamole.net.auth.UserContext;
@@ -103,11 +104,13 @@ public class SharingProfileResource
     public Map<String, String> getParameters()
             throws GuacamoleException {
 
+        // Pull effective permissions
         User self = userContext.self();
+        Permissions effective = self.getEffectivePermissions();
 
         // Retrieve permission sets
-        SystemPermissionSet systemPermissions = self.getSystemPermissions();
-        ObjectPermissionSet sharingProfilePermissions = self.getSharingProfilePermissions();
+        SystemPermissionSet systemPermissions = effective.getSystemPermissions();
+        ObjectPermissionSet sharingProfilePermissions = effective.getSharingProfilePermissions();
 
         // Deny access if adminstrative or update permission is missing
         String identifier = sharingProfile.getIdentifier();

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/393e1ab6/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 06bab9f..72c9162 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.permission.APIPermissionSet;
 import org.apache.guacamole.rest.permission.PermissionSetResource;
 
 /**
@@ -181,7 +182,8 @@ public class UserResource
 
     /**
      * Returns a resource which abstracts operations available on the overall
-     * permissions granted to the User represented by this UserResource.
+     * permissions granted directly to the User represented by this
+     * UserResource.
      *
      * @return
      *     A resource which representing the permissions granted to the User
@@ -192,4 +194,21 @@ public class UserResource
         return new PermissionSetResource(user);
     }
 
+    /**
+     * Returns a read-only view of the permissions effectively granted to this
+     * user, including permissions which may be inherited or implied.
+     *
+     * @return
+     *     A read-only view of the permissions effectively granted to this
+     *     user.
+     *
+     * @throws GuacamoleException
+     *     If the effective permissions for this user cannot be retrieved.
+     */
+    @GET
+    @Path("effectivePermissions")
+    public APIPermissionSet getEffectivePermissions() throws GuacamoleException {
+        return new APIPermissionSet(user.getEffectivePermissions());
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/393e1ab6/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js
----------------------------------------------------------------------
diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js
index 8562207..2220e4d 100644
--- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js
+++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionController.js
@@ -199,7 +199,7 @@ angular.module('manage').controller('manageConnectionController', ['$scope', '$i
     });
     
     // Query the user's permissions for the current connection
-    permissionService.getPermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
+    permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
     .success(function permissionsReceived(permissions) {
                 
         $scope.permissions = permissions;

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/393e1ab6/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js
----------------------------------------------------------------------
diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js
index bafe591..f342f4d 100644
--- a/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js
+++ b/guacamole/src/main/webapp/app/manage/controllers/manageConnectionGroupController.js
@@ -134,7 +134,7 @@ angular.module('manage').controller('manageConnectionGroupController', ['$scope'
     });
 
     // Query the user's permissions for the current connection group
-    permissionService.getPermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
+    permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
     .success(function permissionsReceived(permissions) {
                 
         $scope.permissions = permissions;

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/393e1ab6/guacamole/src/main/webapp/app/manage/controllers/manageSharingProfileController.js
----------------------------------------------------------------------
diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageSharingProfileController.js b/guacamole/src/main/webapp/app/manage/controllers/manageSharingProfileController.js
index c532975..0583a69 100644
--- a/guacamole/src/main/webapp/app/manage/controllers/manageSharingProfileController.js
+++ b/guacamole/src/main/webapp/app/manage/controllers/manageSharingProfileController.js
@@ -175,7 +175,7 @@ angular.module('manage').controller('manageSharingProfileController', ['$scope',
     });
 
     // Query the user's permissions for the current sharing profile
-    permissionService.getPermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
+    permissionService.getEffectivePermissions($scope.selectedDataSource, authenticationService.getCurrentUsername())
     .success(function permissionsReceived(permissions) {
 
         $scope.permissions = permissions;

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/393e1ab6/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js
----------------------------------------------------------------------
diff --git a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js
index dd69391..48519d5 100644
--- a/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js
+++ b/guacamole/src/main/webapp/app/manage/controllers/manageUserController.js
@@ -680,7 +680,7 @@ angular.module('manage').controller('manageUserController', ['$scope', '$injecto
     
     // Query the user's permissions for the current user
     dataSourceService.apply(
-        permissionService.getPermissions,
+        permissionService.getEffectivePermissions,
         dataSources,
         currentUsername
     )

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/393e1ab6/guacamole/src/main/webapp/app/navigation/services/userPageService.js
----------------------------------------------------------------------
diff --git a/guacamole/src/main/webapp/app/navigation/services/userPageService.js b/guacamole/src/main/webapp/app/navigation/services/userPageService.js
index 749564b..24d340b 100644
--- a/guacamole/src/main/webapp/app/navigation/services/userPageService.js
+++ b/guacamole/src/main/webapp/app/navigation/services/userPageService.js
@@ -329,7 +329,7 @@ angular.module('navigation').factory('userPageService', ['$injector',
 
         // Retrieve current permissions
         dataSourceService.apply(
-            permissionService.getPermissions,
+            permissionService.getEffectivePermissions,
             authenticationService.getAvailableDataSources(),
             authenticationService.getCurrentUsername() 
         )
@@ -422,7 +422,7 @@ angular.module('navigation').factory('userPageService', ['$injector',
 
         // Retrieve current permissions
         dataSourceService.apply(
-            permissionService.getPermissions,
+            permissionService.getEffectivePermissions,
             authenticationService.getAvailableDataSources(),
             authenticationService.getCurrentUsername()
         )

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/393e1ab6/guacamole/src/main/webapp/app/rest/services/permissionService.js
----------------------------------------------------------------------
diff --git a/guacamole/src/main/webapp/app/rest/services/permissionService.js b/guacamole/src/main/webapp/app/rest/services/permissionService.js
index a803775..f108dfa 100644
--- a/guacamole/src/main/webapp/app/rest/services/permissionService.js
+++ b/guacamole/src/main/webapp/app/rest/services/permissionService.js
@@ -36,8 +36,11 @@ angular.module('rest').factory('permissionService', ['$injector',
 
     /**
      * Returns the URL for the REST resource most appropriate for accessing
-     * the permissions of the user having the given username.
-     * 
+     * the effective permissions of the user having the given username.
+     * Effective permissions differ from the permissions returned via
+     * getPermissions() in that permissions which are not directly granted to
+     * the user are included.
+     *
      * It is important to note that a particular data source can authenticate
      * and provide permissions for a user, even if that user does not exist
      * within that data source (and thus cannot be found beneath
@@ -56,7 +59,7 @@ angular.module('rest').factory('permissionService', ['$injector',
      *     The URL for the REST resource representing the user having the given
      *     username.
      */
-    var getPermissionsResourceURL = function getPermissionsResourceURL(dataSource, username) {
+    var getEffectivePermissionsResourceURL = function getEffectivePermissionsResourceURL(dataSource, username) {
 
         // Create base URL for data source
         var base = 'api/session/data/' + encodeURIComponent(dataSource);
@@ -65,19 +68,21 @@ angular.module('rest').factory('permissionService', ['$injector',
         // user actually existing (they may not). Access their permissions via
         // "self" rather than the collection of defined users.
         if (username === authenticationService.getCurrentUsername())
-            return base + '/self/permissions';
+            return base + '/self/effectivePermissions';
 
         // Otherwise, the user must exist for their permissions to be
         // accessible. Use the collection of defined users.
-        return base + '/users/' + encodeURIComponent(username) + '/permissions';
+        return base + '/users/' + encodeURIComponent(username) + '/effectivePermissions';
 
     };
 
     /**
-     * Makes a request to the REST API to get the list of permissions for a
-     * given user, returning a promise that provides an array of
-     * @link{Permission} objects if successful.
-     * 
+     * Makes a request to the REST API to get the list of effective permissions
+     * for a given user, returning a promise that provides an array of
+     * @link{Permission} objects if successful. Effective permissions differ
+     * from the permissions returned via getPermissions() in that permissions
+     * which are not directly granted to the user are included.
+     *
      * @param {String} dataSource
      *     The unique identifier of the data source containing the user whose
      *     permissions should be retrieved. This identifier corresponds to an
@@ -85,12 +90,12 @@ angular.module('rest').factory('permissionService', ['$injector',
      *
      * @param {String} userID
      *     The ID of the user to retrieve the permissions for.
-     *                          
+     *
      * @returns {Promise.<PermissionSet>}
      *     A promise which will resolve with a @link{PermissionSet} upon
      *     success.
      */
-    service.getPermissions = function getPermissions(dataSource, userID) {
+    service.getEffectivePermissions = function getEffectivePermissions(dataSource, userID) {
 
         // Build HTTP parameters set
         var httpParameters = {
@@ -101,58 +106,89 @@ angular.module('rest').factory('permissionService', ['$injector',
         return $http({
             cache   : cacheService.users,
             method  : 'GET',
-            url     : getPermissionsResourceURL(dataSource, userID),
+            url     : getEffectivePermissionsResourceURL(dataSource, userID),
             params  : httpParameters
         });
 
     };
 
     /**
-     * Makes a request to the REST API to add permissions for a given user,
-     * returning a promise that can be used for processing the results of the
-     * call.
+     * Returns the URL for the REST resource most appropriate for accessing
+     * the permissions of the user having the given identifier. The permissions
+     * retrieved differ from effective permissions (those returned by
+     * getEffectivePermissions()) in that only permissions which are directly
+     * granted to the user are included.
      * 
+     * It is important to note that a particular data source can authenticate
+     * and provide permissions for a user, even if that user does not exist
+     * within that data source (and thus cannot be found beneath
+     * "api/session/data/{dataSource}/users")
+     *
      * @param {String} dataSource
      *     The unique identifier of the data source containing the user whose
-     *     permissions should be modified. This identifier corresponds to an
+     *     permissions should be retrieved. This identifier corresponds to an
      *     AuthenticationProvider within the Guacamole web application.
      *
-     * @param {String} userID
-     *     The ID of the user to modify the permissions of.
-     *                          
-     * @param {PermissionSet} permissions
-     *     The set of permissions to add.
-     *                          
-     * @returns {Promise}
-     *     A promise for the HTTP call which will succeed if and only if the
-     *     add operation is successful.
+     * @param {String} identifier
+     *     The identifier of the user for which the URL of the proper REST
+     *     resource should be derived.
+     *
+     * @returns {String}
+     *     The URL for the REST resource representing the user having the given
+     *     identifier.
      */
-    service.addPermissions = function addPermissions(dataSource, userID, permissions) {
-        return service.patchPermissions(dataSource, userID, permissions, null);
+    var getPermissionsResourceURL = function getPermissionsResourceURL(dataSource, identifier) {
+
+        // Create base URL for data source
+        var base = 'api/session/data/' + encodeURIComponent(dataSource);
+
+        // If the username is that of the current user, do not rely on the
+        // user actually existing (they may not). Access their permissions via
+        // "self" rather than the collection of defined users.
+        if (identifier === authenticationService.getCurrentUsername())
+            return base + '/self/permissions';
+
+        // Otherwise, the user must exist for their permissions to be
+        // accessible. Use the collection of defined users.
+        return base + '/users/' + encodeURIComponent(identifier) + '/permissions';
+
     };
-    
+
     /**
-     * Makes a request to the REST API to remove permissions for a given user,
-     * returning a promise that can be used for processing the results of the
-     * call.
+     * Makes a request to the REST API to get the list of permissions for a
+     * given user, returning a promise that provides an array of
+     * @link{Permission} objects if successful. The permissions retrieved
+     * differ from effective permissions (those returned by
+     * getEffectivePermissions()) in that only permissions which are directly
+     * granted to the user included.
      * 
      * @param {String} dataSource
      *     The unique identifier of the data source containing the user whose
-     *     permissions should be modified. This identifier corresponds to an
+     *     permissions should be retrieved. This identifier corresponds to an
      *     AuthenticationProvider within the Guacamole web application.
      *
-     * @param {String} userID
-     *     The ID of the user to modify the permissions of.
-     *                          
-     * @param {PermissionSet} permissions
-     *     The set of permissions to remove.
-     *                          
-     * @returns {Promise}
-     *     A promise for the HTTP call which will succeed if and only if the
-     *     remove operation is successful.
+     * @param {String} identifier
+     *     The identifier of the user to retrieve the permissions for.
+     *
+     * @returns {Promise.<PermissionSet>}
+     *     A promise which will resolve with a @link{PermissionSet} upon
+     *     success.
      */
-    service.removePermissions = function removePermissions(dataSource, userID, permissions) {
-        return service.patchPermissions(dataSource, userID, null, permissions);
+    service.getPermissions = function getPermissions(dataSource, identifier) {
+
+        // Build HTTP parameters set
+        var httpParameters = {
+            token : authenticationService.getCurrentToken()
+        };
+
+        // Retrieve user permissions
+        return $http({
+            cache   : cacheService.users,
+            method  : 'GET',
+            url     : getPermissionsResourceURL(dataSource, identifier),
+            params  : httpParameters
+        });
+
     };
 
     /**
@@ -240,27 +276,30 @@ angular.module('rest').factory('permissionService', ['$injector',
     /**
      * Makes a request to the REST API to modify the permissions for a given
      * user, returning a promise that can be used for processing the results of
-     * the call.
+     * the call. This request affects only the permissions directly granted to
+     * the user, and may not affect permissions inherited through other means
+     * (effective permissions).
      * 
      * @param {String} dataSource
      *     The unique identifier of the data source containing the user whose
      *     permissions should be modified. This identifier corresponds to an
      *     AuthenticationProvider within the Guacamole web application.
      *
-     * @param {String} userID
-     *     The ID of the user to modify the permissions of.
+     * @param {String} identifier
+     *     The identifier of the user to modify the permissions of.
      *                          
      * @param {PermissionSet} [permissionsToAdd]
      *     The set of permissions to add, if any.
      *
      * @param {PermissionSet} [permissionsToRemove]
      *     The set of permissions to remove, if any.
-     *                          
+     *
      * @returns {Promise}
      *     A promise for the HTTP call which will succeed if and only if the
      *     patch operation is successful.
      */
-    service.patchPermissions = function patchPermissions(dataSource, userID, permissionsToAdd, permissionsToRemove) {
+    service.patchPermissions = function patchPermissions(dataSource, identifier,
+            permissionsToAdd, permissionsToRemove) {
 
         var permissionPatch = [];
         
@@ -278,7 +317,7 @@ angular.module('rest').factory('permissionService', ['$injector',
         // Patch user permissions
         return $http({
             method  : 'PATCH', 
-            url     : getPermissionsResourceURL(dataSource, userID),
+            url     : getPermissionsResourceURL(dataSource, identifier),
             params  : httpParameters,
             data    : permissionPatch
         })

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/393e1ab6/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js
----------------------------------------------------------------------
diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js
index 6616853..a245fb2 100644
--- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js
+++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsConnections.js
@@ -404,7 +404,7 @@ angular.module('settings').directive('guacSettingsConnections', [function guacSe
             };
 
             // Retrieve current permissions
-            permissionService.getPermissions($scope.dataSource, currentUsername)
+            permissionService.getEffectivePermissions($scope.dataSource, currentUsername)
             .success(function permissionsRetrieved(permissions) {
 
                 // Store retrieved permissions

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/393e1ab6/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js
----------------------------------------------------------------------
diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js
index d906c3d..655f233 100644
--- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js
+++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsPreferences.js
@@ -185,7 +185,7 @@ angular.module('settings').directive('guacSettingsPreferences', [function guacSe
             });
 
             // Retrieve current permissions
-            permissionService.getPermissions(dataSource, username)
+            permissionService.getEffectivePermissions(dataSource, username)
             .success(function permissionsRetrieved(permissions) {
 
                 // Add action for changing password if permission is granted

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/393e1ab6/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js
----------------------------------------------------------------------
diff --git a/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js b/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js
index 1ac9fad..c143399 100644
--- a/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js
+++ b/guacamole/src/main/webapp/app/settings/directives/guacSettingsUsers.js
@@ -232,7 +232,7 @@ angular.module('settings').directive('guacSettingsUsers', [function guacSettings
 
             // Retrieve current permissions
             dataSourceService.apply(
-                permissionService.getPermissions,
+                permissionService.getEffectivePermissions,
                 dataSources,
                 currentUsername
             )