Repository: auth0/node-auth0
Pull request #344 (2019-03-14 21:30:39)

Add support for roles and permissions
### Changes

- Added RolesManager for managing roles and the permissions within those roles
- Changed UserManager to add user specific roles and permission tasks
-Changed management/index.js

### References

### Testing

Add `test/management/roles.tests.js` and modified `test/management/users.tests.js`

- [x] This change adds unit test coverage

- [ ] This change adds integration test coverage

### Checklist

- [x] I have read the [Auth0 general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md)

- [x] I have read the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md)

- [ ] All existing and new tests complete without errors

All new tests complete without errors but some old tests seem to be erroring out



Patch:
diff --git a/src/management/RolesManager.js b/src/management/RolesManager.js
new file mode 100644
index 000000000..37ef30ce4
--- /dev/null
+++ b/src/management/RolesManager.js
@@ -0,0 +1,355 @@
+var ArgumentError = require('rest-facade').ArgumentError;
+var utils = require('../utils');
+var Auth0RestClient = require('../Auth0RestClient');
+var RetryRestClient = require('../RetryRestClient');
+
+/**
+ * Simple facade for consuming a REST API endpoint.
+ * @external RestClient
+ * @see https://github.com/ngonzalvez/rest-facade
+ */
+
+/**
+ * @class RolesManager
+ * The role class provides a simple abstraction for performing CRUD operations
+ * on Auth0 RolesManager.
+ * @constructor
+ * @memberOf module:management
+ *
+ * @param {Object} options            The client options.
+ * @param {String} options.baseUrl    The URL of the API.
+ * @param {Object} [options.headers]  Headers to be included in all requests.
+ * @param {Object} [options.retry]    Retry Policy Config
+ */
+var RolesManager = function(options) {
+  if (options === null || typeof options !== 'object') {
+    throw new ArgumentError('Must provide manager options');
+  }
+
+  if (options.baseUrl === null || options.baseUrl === undefined) {
+    throw new ArgumentError('Must provide a base URL for the API');
+  }
+
+  if ('string' !== typeof options.baseUrl || options.baseUrl.length === 0) {
+    throw new ArgumentError('The provided base URL is invalid');
+  }
+
+  /**
+   * Options object for the Rest Client instance.
+   *
+   * @type {Object}
+   */
+  var clientOptions = {
+    headers: options.headers,
+    query: { repeatParams: false }
+  };
+
+  /**
+   * Provides an abstraction layer for performing CRUD operations on
+   * {@link https://auth0.com/docs/api/v2#!/RolesManager Auth0 RolesManagers}.
+   *
+   * @type {external:RestClient}
+   */
+  var auth0RestClient = new Auth0RestClient(
+    options.baseUrl + '/roles/:id',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.resource = new RetryRestClient(auth0RestClient, options.retry);
+
+  var permissionsInRoleClient = new Auth0RestClient(
+    options.baseUrl + '/roles/:id/permissions',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.permissions = new RetryRestClient(permissionsInRoleClient, options.retry);
+
+  var usersInRoleClient = new Auth0RestClient(
+    options.baseUrl + '/roles/:id/users',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.users = new RetryRestClient(usersInRoleClient, options.retry);
+};
+
+/**
+ * Create a new role.
+ *
+ * @method    create
+ * @memberOf  module:management.RolesManager.prototype
+ *
+ * @example
+ * management.roles.create(data, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // Role created.
+ * });
+ *
+ * @param   {Object}    data     Role data object.
+ * @param   {Function}  [cb]     Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(RolesManager, 'create', 'resource.create');
+
+/**
+ * Get all roles.
+ *
+ * @method    getAll
+ * @memberOf  module:management.RolesManager.prototype
+ *
+ * @example <caption>
+ *   This method takes an optional object as first argument that may be used to
+ *   specify pagination settings. If pagination options are not present,
+ *   the first page of a limited number of results will be returned.
+ * </caption>
+ *
+ * // Pagination settings.
+ * var params = {
+ *   per_page: 10,
+ *   page: 0
+ * };
+ *
+ * management.roles.getAll(params, function (err, roles) {
+ *   console.log(roles.length);
+ * });
+ *
+ * @param   {Object}    [params]          Roles parameters.
+ * @param   {Number}    [params.per_page] Number of results per page.
+ * @param   {Number}    [params.page]     Page number, zero indexed.
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(RolesManager, 'getAll', 'resource.getAll');
+
+/**
+ * Get an Auth0 role.
+ *
+ * @method    get
+ * @memberOf  module:management.RolesManager.prototype
+ *
+ * @example
+ * management.roles.get({ id: ROLE_ID }, function (err, role) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   console.log(role);
+ * });
+ *
+ * @param   {Object}    params        Role parameters.
+ * @param   {String}    params.id     Role ID.
+ * @param   {Function}  [cb]          Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(RolesManager, 'get', 'resource.get');
+
+/**
+ * Update an existing role.
+ *
+ * @method    update
+ * @memberOf  module:management.RolesManager.prototype
+ *
+ * @example
+ * var data = { name: 'New name' };
+ * var params = { id: ROLE_ID };
+ *
+ * // Using auth0 instance.
+ * management.updateRole(params, data, function (err, role) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   console.log(role.name);  // 'New name'
+ * });
+ *
+ * // Using the roles manager directly.
+ * management.roles.update(params, data, function (err, role) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   console.log(role.name);  // 'New name'
+ * });
+ *
+ * @param   {Object}    params        Role parameters.
+ * @param   {String}    params.id     Role ID.
+ * @param   {Object}    data          Updated role data.
+ * @param   {Function}  [cb]          Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(RolesManager, 'update', 'resource.patch');
+
+/**
+ * Delete an existing role.
+ *
+ * @method    delete
+ * @memberOf  module:management.RolesManager.prototype
+ *
+ * @example
+ * management.roles.delete({ id: ROLE_ID }, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // Role deleted.
+ * });
+ *
+ * @param   {Object}    params        Role parameters.
+ * @param   {String}    params.id     Role ID.
+ * @param   {Function}  [cb]          Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(RolesManager, 'delete', 'resource.delete');
+
+/**
+ * Get Permissions in a Role
+ *
+ * @method    getPermissionsInRole
+ * @memberOf  module:management.RolesManager.prototype
+ *
+ * @example
+ * var params = {id : 'ROLE_ID'}
+ * @example <caption>
+ *   This method takes a first argument as the roleId and returns the permissions within that role
+ * </caption>
+ *
+ * management.roles.getPermissions( {id : 'ROLE_ID'}, function (err, permissions) {
+ *   console.log(permissions);
+ * });
+ *
+ * @param   {String}    [email]           Email address of user(s) to find
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+RolesManager.prototype.getPermissions = function(params, callback) {
+  return this.permissions.getAll(params, callback);
+};
+
+/**
+ * Add permissions in a role
+ *
+ * @method    addPermissions
+ * @memberOf  module:management.RolesManager.prototype
+ *
+ * @example
+ * var params =  { id :'ROLE_ID'};
+ * var data = { "permissions" : [{"permission_name" :"do:something" ,"resource_server_identifier" :"test123" }]};
+ *
+ * management.roles.addPermissions(params, data, function (err, user) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // permissions added.
+ * });
+ *
+ * @param   {String}    params.id                ID of the Role.
+ * @param   {Object}    data                permissions data
+ * @param   {String}    data.permissions    Array of permissions
+ * @param   {String}    data.permissions.permission_name  Name of a permission
+ * @param   {String}    data.permissions.resource_server_identifier  Identifier for a resource
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+
+RolesManager.prototype.addPermissions = function(params, data, cb) {
+  data = data || {};
+  params = params || {};
+
+  // Require a user ID.
+  if (!params.id) {
+    throw new ArgumentError('The roleId passed in params cannot be null or undefined');
+  }
+  if (typeof params.id !== 'string') {
+    throw new ArgumentError('The role Id has to be a string');
+  }
+
+  if (cb && cb instanceof Function) {
+    return this.permissions.create(params, data, cb);
+  }
+
+  return this.permissions.create(params, data);
+};
+
+/**
+ * Remove permissions from a role
+ *
+ * @method    removePermissions
+ * @memberOf  module:management.RolesManager.prototype
+ *
+ * @example
+ * var params =  { id :'ROLE_ID'};
+ * var data = { "permissions" : [{"permission_name" :"do:something" ,"resource_server_identifier" :"test123" }]};
+ *
+ * management.roles.removePermissions(params, data, function (err, user) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // permissions added.
+ * });
+ *
+ * @param   {String}    params.id                ID of the Role.
+ * @param   {Object}    data                permissions data
+ * @param   {String}    data.permissions    Array of permissions
+ * @param   {String}    data.permissions.permission_name  Name of a permission
+ * @param   {String}    data.permissions.resource_server_identifier  Identifier for a resource
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+
+RolesManager.prototype.removePermissions = function(params, data, cb) {
+  data = data || {};
+  params = params || {};
+
+  // Require a user ID.
+  if (!params.id) {
+    throw new ArgumentError('The roleId passed in params cannot be null or undefined');
+  }
+  if (typeof params.id !== 'string') {
+    throw new ArgumentError('The role Id has to be a string');
+  }
+
+  if (cb && cb instanceof Function) {
+    return this.permissions.delete(params, data, cb);
+  }
+
+  return this.permissions.delete(params, data);
+};
+
+/**
+ * Get Users in a Role
+ *
+ * @method    getUsers
+ * @memberOf  module:management.RolesManager.prototype
+ *
+ * @example
+ * params = {id : 'ROLE_ID'}
+ * @example <caption>
+ *   This method takes a roleId and returns the users with that role assigned
+ * </caption>
+ *
+ * management.roles.getUsers( {id : 'ROLE_ID'}, function (err, users) {
+ *   console.log(users);
+ * });
+ *
+ * @param   {String}    [email]           Email address of user(s) to find
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+RolesManager.prototype.getUsers = function(params, callback) {
+  return this.users.getAll(params, callback);
+};
+
+module.exports = RolesManager;
diff --git a/src/management/UsersManager.js b/src/management/UsersManager.js
index 18f162515..f5469204d 100644
--- a/src/management/UsersManager.js
+++ b/src/management/UsersManager.js
@@ -121,6 +121,30 @@ var UsersManager = function(options) {
     recoveryCodeRegenerationAuth0RestClients,
     options.retry
   );
+
+  /**
+   * Provides an abstraction layer for CRD on roles for a user
+   *
+   * @type {external:RestClient}
+   */
+  var userRolesClient = new Auth0RestClient(
+    options.baseUrl + '/users/:id/roles',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.roles = new RetryRestClient(userRolesClient, options.retry);
+
+  /**
+   * Provides an abstraction layer for CRD on permissions directly on a user
+   *
+   * @type {external:RestClient}
+   */
+  var userPermissionsClient = new Auth0RestClient(
+    options.baseUrl + '/users/:id/permissions',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.permissions = new RetryRestClient(userPermissionsClient, options.retry);
 };
 
 /**
@@ -623,4 +647,230 @@ UsersManager.prototype.regenerateRecoveryCode = function(params, cb) {
   return this.recoveryCodeRegenerations.create(params, {});
 };
 
+/**
+ * Get a list of roles for a user.
+ *
+ * @method    getUserRoles
+ * @memberOf  module:management.UsersManager.prototype
+ *
+ * @example
+ * management.users.getRoles({ id: USER_ID }, function (err, roles) {
+ *   console.log(roles);
+ * });
+ *
+ * @param   {Object}    data      The user data object.
+ * @param   {String}    data.id   The user id.
+ * @param   {Function}  [cb]      Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+UsersManager.prototype.getRoles = function() {
+  return this.roles.getAll.apply(this.roles, arguments);
+};
+
+/**
+ * Assign roles to a user
+ *
+ * @method    assignRoles
+ * @memberOf  module:management.RolesManager.prototype
+ *
+ * @example
+ * var params =  { id : 'USER_ID';
+ * var data = { "roles" : ["roleId1", "roleID2"]};
+ *
+ * management.users.assignRoles(params, data, function (err, user) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // roles added.
+ * });
+ *
+ * @param   {Object}    params       params object
+ * @param   {String}    params.id    user_id
+ * @param   {String}    data         data object containing list of role IDs
+ * @param   {String}    data.roles  Array of role IDs
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+
+UsersManager.prototype.assignRoles = function(params, data, cb) {
+  var query = params || {};
+  data = data || {};
+
+  // Require a user ID.
+  if (!params.id) {
+    throw new ArgumentError('The user_id cannot be null or undefined');
+  }
+  if (typeof params.id !== 'string') {
+    throw new ArgumentError('The user_id has to be a string');
+  }
+
+  if (cb && cb instanceof Function) {
+    return this.roles.create(query, data, cb);
+  }
+
+  return this.roles.create(query, data);
+};
+
+/**
+ * Remove roles from a user
+ *
+ * @method    removeRoles
+ * @memberOf  module:management.RolesManager.prototype
+ *
+ * @example
+ * var params =  { id : 'USER_ID';
+ * var data = { "roles" : ["roleId1", "roleID2"]};
+ *
+ * management.users.removeRoles(params, data, function (err, user) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // roles removed.
+ * });
+ *
+ * @param   {Object}    params       params object
+ * @param   {String}    params.id    user_id
+ * @param   {String}    data         data object containing list of role IDs
+ * @param   {String}    data.roles  Array of role IDs
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+
+UsersManager.prototype.removeRoles = function(params, data, cb) {
+  var query = params || {};
+  data = data || {};
+
+  // Require a user ID.
+  if (!params.id) {
+    throw new ArgumentError('The user_id cannot be null or undefined');
+  }
+  if (typeof params.id !== 'string') {
+    throw new ArgumentError('The user_id has to be a string');
+  }
+
+  if (cb && cb instanceof Function) {
+    return this.roles.delete(query, data, cb);
+  }
+
+  return this.roles.delete(query, data);
+};
+
+/**
+ * Get a list of permissions for a user.
+ *
+ * @method    getPermissions
+ * @memberOf  module:management.UsersManager.prototype
+ *
+ * @example
+ * management.users.getPermissions({ id: USER_ID }, function (err, permissions) {
+ *   console.log(permissions);
+ * });
+ *
+ * @param   {Object}    data      The user data object.
+ * @param   {String}    data.id   The user id.
+ * @param   {Function}  [cb]      Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+UsersManager.prototype.getPermissions = function() {
+  return this.permissions.getAll.apply(this.permissions, arguments);
+};
+
+/**
+ * Assign permissions to a user
+ *
+ * @method    assignPermissions
+ * @memberOf  module:management.permissionsManager.prototype
+ *
+ * @example
+ * var params =  { id : 'USER_ID';
+ * var data = { "permissions" : [{"permission_name" :"do:something" ,"resource_server_identifier" :"test123" }]};
+ *
+ * management.users.assignPermissions(params, data, function (err, user) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // permissions added.
+ * });
+ *
+ * @param   {Object}    params       params object
+ * @param   {String}    params.id    user_id
+ * @param   {String}    data         data object containing list of permissions
+ * @param   {String}    data.permissions  Array of permission IDs
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+
+UsersManager.prototype.assignPermissions = function(params, data, cb) {
+  var query = params || {};
+  data = data || {};
+
+  // Require a user ID.
+  if (!params.id) {
+    throw new ArgumentError('The user_id cannot be null or undefined');
+  }
+  if (typeof params.id !== 'string') {
+    throw new ArgumentError('The user_id has to be a string');
+  }
+
+  if (cb && cb instanceof Function) {
+    return this.permissions.create(query, data, cb);
+  }
+
+  return this.permissions.create(query, data);
+};
+
+/**
+ * Remove permissions from a user
+ *
+ * @method    removePermissions
+ * @memberOf  module:management.permissionsManager.prototype
+ *
+ * @example
+ * var params =  { id : 'USER_ID';
+ * var data = { "permissions" : [{"permission_name" :"do:something" ,"resource_server_identifier" :"test123" }]};
+ *
+ * management.users.removePermissions(params, data, function (err, user) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // permissions removed.
+ * });
+ *
+ * @param   {Object}    params       params object
+ * @param   {String}    params.id    user_id
+ * @param   {String}    data         data object containing list of permission IDs
+ * @param   {String}    data.permissions  Array of permission IDs
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+
+UsersManager.prototype.removePermissions = function(params, data, cb) {
+  var query = params || {};
+  data = data || {};
+
+  // Require a user ID.
+  if (!params.id) {
+    throw new ArgumentError('The user_id cannot be null or undefined');
+  }
+  if (typeof params.id !== 'string') {
+    throw new ArgumentError('The user_id has to be a string');
+  }
+
+  if (cb && cb instanceof Function) {
+    return this.permissions.delete(query, data, cb);
+  }
+
+  return this.permissions.delete(query, data);
+};
+
 module.exports = UsersManager;
diff --git a/src/management/index.js b/src/management/index.js
index 106128d6a..647172b82 100644
--- a/src/management/index.js
+++ b/src/management/index.js
@@ -29,6 +29,7 @@ var RulesConfigsManager = require('./RulesConfigsManager');
 var EmailTemplatesManager = require('./EmailTemplatesManager');
 var GuardianManager = require('./GuardianManager');
 var CustomDomainsManager = require('./CustomDomainsManager');
+var RolesManager = require('./RolesManager');
 
 var BASE_URL_FORMAT = 'https://%s/api/v2';
 var MANAGEMENT_API_AUD_FORMAT = 'https://%s/api/v2/';
@@ -279,6 +280,14 @@ var ManagementClient = function(options) {
    * @type {RulesConfigsManager}
    */
   this.rulesConfigs = new RulesConfigsManager(managerOptions);
+
+  /**
+   * Simple abstraction for performing CRUD operations on the
+   * roles endpoint.
+   *
+   * @type {RolesManager}
+   */
+  this.roles = new RolesManager(managerOptions);
 };
 
 /**
@@ -1306,6 +1315,176 @@ utils.wrapPropertyMethod(ManagementClient, 'linkUsers', 'users.link');
  */
 utils.wrapPropertyMethod(ManagementClient, 'getUserLogs', 'users.logs');
 
+/**
+ * Get user's roles
+ *
+ * @method    getUserRoles
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * var params = { id: USER_ID, page: 0, per_page: 50, sort: 'date:-1', include_totals: true };
+ *
+ * management.getUserRoles(params, function (err, logs) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   console.log(logs);
+ * });
+ *
+ * @param   {Object}    params                Get roles data.
+ * @param   {String}    params.id             User id.
+ * @param   {Number}    params.per_page       Number of results per page.
+ * @param   {Number}    params.page           Page number, zero indexed.
+ * @param   {String}    params.sort           The field to use for sorting. Use field:order where order is 1 for ascending and -1 for descending. For example date:-1.
+ * @param   {Boolean}   params.include_totals true if a query summary must be included in the result, false otherwise. Default false;
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'getUserRoles', 'users.getRoles');
+
+/**
+ * Asign roles to a user
+ *
+ * @method    assignRolestoUser
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * var parms =  { id : 'USER_ID'};
+ * var data = { "roles" :["role1"]};
+ *
+ * management.assignRolestoUser(params, data, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // User assigned roles.
+ * });
+ *
+ * @param   {Object}    params       params object
+ * @param   {String}    params.id    user_id
+ * @param   {String}    data         data object containing list of role IDs
+ * @param   {String}    data.roles  Array of role IDs
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'assignRolestoUser', 'users.assignRoles');
+
+/**
+ * Remove roles from a user
+ *
+ * @method    removeRolesFromUser
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * var parms =  { id : 'USER_ID'};
+ * var data = { "roles" :["role1"]};
+ *
+ * management.removeRolesFromUser(params, data, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // User assigned roles.
+ * });
+ *
+ * @param   {Object}    params       params object
+ * @param   {String}    params.id    user_id
+ * @param   {String}    data         data object containing list of role IDs
+ * @param   {String}    data.roles  Array of role IDs
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'removeRolesFromUser', 'users.removeRoles');
+
+/**
+ * Get user's permissions
+ *
+ * @method    getUserPermissions
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * var params = { id: USER_ID, page: 0, per_page: 50, sort: 'date:-1', include_totals: true };
+ *
+ * management.getUserPermissions(params, function (err, logs) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   console.log(logs);
+ * });
+ *
+ * @param   {Object}    params                Get permissions data.
+ * @param   {String}    params.id             User id.
+ * @param   {Number}    params.per_page       Number of results per page.
+ * @param   {Number}    params.page           Page number, zero indexed.
+ * @param   {String}    params.sort           The field to use for sorting. Use field:order where order is 1 for ascending and -1 for descending. For example date:-1.
+ * @param   {Boolean}   params.include_totals true if a query summary must be included in the result, false otherwise. Default false;
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'getUserPermissions', 'users.getPermissions');
+
+/**
+ * Asign permissions to a user
+ *
+ * @method    assignPermissionsToUser
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * var parms =  { id : 'USER_ID'};
+ * var data = { "permissions" : [{"permission_name" :"do:something" ,"resource_server_identifier" :"test123" }]};
+ *
+ * management.assignPermissionsToUser(params, data, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // User assigned permissions.
+ * });
+ *
+ * @param   {Object}    params       params object
+ * @param   {String}    params.id    user_id
+ * @param   {String}    data         data object containing list of permissions
+ * @param   {String}    data.permissions  Array of permission IDs
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'assignPermissionsToUser', 'users.assignPermissions');
+
+/**
+ * Remove permissions from a user
+ *
+ * @method    removePermissionsFromUser
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * var parms =  { id : 'USER_ID'};
+ * var data = { "permissions" : [{"permission_name" :"do:something" ,"resource_server_identifier" :"test123" }]};
+ *
+ * management.removePermissionsFromUser(params, data, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // User assigned permissions.
+ * });
+ *
+ * @param   {Object}    params       params object
+ * @param   {String}    params.id    user_id
+ * @param   {String}    data         data object containing list of permission IDs
+ * @param   {String}    data.permissions  Array of permission IDs
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'removePermissionsFromUser', 'users.removePermissions');
+
 /**
  * Get a list of a user's Guardian enrollments.
  *
@@ -2319,4 +2498,230 @@ utils.wrapPropertyMethod(
  */
 utils.wrapPropertyMethod(ManagementClient, 'updateGuardianFactor', 'guardian.factors.update');
 
+/**
+ * Get all roles.
+ *
+ * @method    getRoles
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example <caption>
+ *   This method takes an optional object as first argument that may be used to
+ *   specify pagination settings. If pagination options are not present,
+ *   the first page of a limited number of results will be returned.
+ * </caption>
+ *
+ * // Pagination settings.
+ * var params = {
+ *   per_page: 10,
+ *   page: 0
+ * };
+ *
+ * management.getRoles(params, function (err, roles) {
+ *   console.log(roles.length);
+ * });
+ *
+ * @param   {Object}    [params]          Roles parameters.
+ * @param   {Number}    [params.per_page] Number of results per page.
+ * @param   {Number}    [params.page]     Page number, zero indexed.
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'getRoles', 'roles.getAll');
+
+/**
+ * Create a new role.
+ *
+ * @method    createRole
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * data = {"name": "test1","description": "123"}
+ * management.createRole(data, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // Role created.
+ * });
+ *
+ * @param   {Object}    data     Role data object.
+ * @param   {Function}  [cb]     Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'createRole', 'roles.create');
+
+/**
+ * Get an Auth0 role.
+ *
+ * @method    getRole
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * management.getRole({ id: ROLE_ID }, function (err, role) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   console.log(role);
+ * });
+ *
+ * @param   {Object}    params        Role parameters.
+ * @param   {String}    params.id     Role ID.
+ * @param   {Function}  [cb]          Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'getRole', 'roles.get');
+
+/**
+ * Delete an existing role.
+ *
+ * @method    deleteRole
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * management.deleteRole({ id: ROLE_ID }, function (err) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   // Role deleted.
+ * });
+ *
+ * @param   {Object}    params        Role parameters.
+ * @param   {String}    params.id     Role ID.
+ * @param   {Function}  [cb]          Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'deleteRole', 'roles.delete');
+
+/**
+ * Update an existing role.
+ *
+ * @method    updateRole
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * var params = { id: ROLE_ID };
+ * var data = { name: 'my-role'};
+ * management.updateRole(params, data, function (err, role) {
+ *   if (err) {
+ *     // Handle error.
+ *   }
+ *
+ *   console.log(role.name); // 'my-role'.
+ * });
+ *
+ * @param   {Object}    params        Role parameters.
+ * @param   {String}    params.id     Role ID.
+ * @param   {Object}    data          Updated role data.
+ * @param   {Function}  [cb]          Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'updateRole', 'roles.update');
+
+/**
+ * Get permissions for a given role
+ *
+ * @method    getPermissionsInRole
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * var params =  { id :'ROLE_ID'};
+ * @example <caption>
+ *   This method takes a roleId and
+ *   returns all permissions within that role
+ *
+ * </caption>
+ *
+ * management.getPermissionsInRole(params, function (err, permissions) {
+ *   console.log(permissions);
+ * });
+ *
+ * @param   {String}    [roleId]           Id of the role
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'getPermissionsInRole', 'roles.getPermissions');
+
+/**
+ * Add permissions in a role
+ *
+ * @method    addPermissionsInRole
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * var params = { id :'ROLE_ID'};
+ * var data = { "permissions" : [{"permission_name" :"do:something" ,"resource_server_identifier" :"test123" }]};
+ *
+ * management.addPermissionsInRole(params, data, function (err, permissions) {
+ *   console.log(permissions);
+ * });
+ *
+ * @param   {String}    params.id                ID of the Role.
+ * @param   {Object}    data                permissions data
+ * @param   {String}    data.permissions    Array of permissions
+ * @param   {String}    data.permissions.permission_name  Name of a permission
+ * @param   {String}    data.permissions.resource_server_identifier  Identifier for a resource
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'addPermissionsInRole', 'roles.addPermissions');
+
+/**
+ * Remove permissions from a role
+ *
+ * @method    removePermissionsFromRole
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * var params = { id :'ROLE_ID'};
+ * var data = { "permissions" : [{"permission_name" :"do:something" ,"resource_server_identifier" :"test123" }]};
+ *
+ * management.removePermissionsFromRole(params, data, function (err, permissions) {
+ *   console.log(permissions);
+ * });
+ *
+ * @param   {String}    params.id                ID of the Role.
+ * @param   {Object}    data                permissions data
+ * @param   {String}    data.permissions    Array of permissions
+ * @param   {String}    data.permissions.permission_name  Name of a permission
+ * @param   {String}    data.permissions.resource_server_identifier  Identifier for a resource
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'removePermissionsFromRole', 'roles.removePermissions');
+
+/**
+ * Get users in a given role
+ *
+ * @method    getUsersInRole
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * var params =  { id :'ROLE_ID'};
+ * @example <caption>
+ *   This method takes a roleId and
+ *   returns all users within that role
+ *
+ * </caption>
+ *
+ * management.getUsersInRole(params, function (err, users) {
+ *   console.log(users);
+ * });
+ *
+ * @param   {String}    [roleId]           Id of the role
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'getUsersInRole', 'roles.getUsers');
+
 module.exports = ManagementClient;


Test patch:
diff --git a/test/management/roles.tests.js b/test/management/roles.tests.js
new file mode 100644
index 000000000..d5687af26
--- /dev/null
+++ b/test/management/roles.tests.js
@@ -0,0 +1,703 @@
+var expect = require('chai').expect;
+var nock = require('nock');
+
+var SRC_DIR = '../../src';
+var API_URL = 'https://tenant.auth0.com';
+
+var RolesManager = require(SRC_DIR + '/management/RolesManager');
+var ArgumentError = require('rest-facade').ArgumentError;
+
+describe('RolesManager', function() {
+  before(function() {
+    this.token = 'TOKEN';
+    this.roles = new RolesManager({
+      headers: { authorization: 'Bearer ' + this.token },
+      baseUrl: API_URL
+    });
+  });
+
+  describe('instance', function() {
+    var methods = [
+      'get',
+      'getAll',
+      'create',
+      'update',
+      'delete',
+      'getPermissions',
+      'addPermissions',
+      'removePermissions',
+      'getUsers'
+    ];
+
+    methods.forEach(function(method) {
+      it('should have a ' + method + ' method', function() {
+        expect(this.roles[method]).to.exist.to.be.an.instanceOf(Function);
+      });
+    });
+  });
+
+  describe('#constructor', function() {
+    it('should error when no options are provided', function() {
+      expect(RolesManager).to.throw(ArgumentError, 'Must provide manager options');
+    });
+
+    it('should throw an error when no base URL is provided', function() {
+      var client = RolesManager.bind(null, {});
+
+      expect(client).to.throw(ArgumentError, 'Must provide a base URL for the API');
+    });
+
+    it('should throw an error when the base URL is invalid', function() {
+      var client = RolesManager.bind(null, { baseUrl: '' });
+
+      expect(client).to.throw(ArgumentError, 'The provided base URL is invalid');
+    });
+  });
+
+  describe('#getAll', function() {
+    beforeEach(function() {
+      this.request = nock(API_URL)
+        .get('/roles')
+        .reply(200);
+    });
+
+    it('should accept a callback', function(done) {
+      this.roles.getAll(function() {
+        done();
+      });
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.roles
+        .getAll()
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/roles')
+        .reply(500);
+
+      this.roles.getAll().catch(function(err) {
+        expect(err).to.exist;
+        done();
+      });
+    });
+
+    it('should pass the body of the response to the "then" handler', function(done) {
+      nock.cleanAll();
+
+      var data = [{ test: true }];
+      var request = nock(API_URL)
+        .get('/roles')
+        .reply(200, data);
+
+      this.roles.getAll().then(function(credentials) {
+        expect(credentials).to.be.an.instanceOf(Array);
+
+        expect(credentials.length).to.equal(data.length);
+
+        expect(credentials[0].test).to.equal(data[0].test);
+
+        done();
+      });
+    });
+
+    it('should perform a GET request to /api/v2/roles', function(done) {
+      var request = this.request;
+
+      this.roles.getAll().then(function() {
+        expect(request.isDone()).to.be.true;
+        done();
+      });
+    });
+
+    it('should include the token in the Authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/roles')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.roles.getAll().then(function() {
+        expect(request.isDone()).to.be.true;
+        done();
+      });
+    });
+
+    it('should pass the parameters in the query-string', function(done) {
+      nock.cleanAll();
+
+      var params = {
+        include_fields: true,
+        fields: 'test'
+      };
+      var request = nock(API_URL)
+        .get('/roles')
+        .query(params)
+        .reply(200);
+
+      this.roles.getAll(params).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#get', function() {
+    beforeEach(function() {
+      this.data = {
+        id: 'rol_ID',
+        name: 'My role',
+        description: 'This is my role'
+      };
+
+      this.request = nock(API_URL)
+        .get('/roles/' + this.data.id)
+        .reply(200, this.data);
+    });
+
+    it('should accept a callback', function(done) {
+      var params = { id: this.data.id };
+
+      this.roles.get(params, done.bind(null, null));
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.roles
+        .get({ id: this.data.id })
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should perform a POST request to /api/v2/roles/rol_ID', function(done) {
+      var request = this.request;
+
+      this.roles.get({ id: this.data.id }).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/roles/' + this.data.id)
+        .reply(500);
+
+      this.roles.get({ id: this.data.id }).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should include the token in the Authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/roles/' + this.data.id)
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.roles.get({ id: this.data.id }).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#create', function() {
+    var data = {
+      id: 'rol_ID',
+      name: 'My role',
+      description: 'This is my role'
+    };
+
+    beforeEach(function() {
+      this.request = nock(API_URL)
+        .post('/roles')
+        .reply(200);
+    });
+
+    it('should accept a callback', function(done) {
+      this.roles.create(data, function() {
+        done();
+      });
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.roles
+        .create(data)
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/roles')
+        .reply(500);
+
+      this.roles.create(data).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should perform a POST request to /api/v2/roles', function(done) {
+      var request = this.request;
+
+      this.roles.create(data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass the data in the body of the request', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/roles', data)
+        .reply(200);
+
+      this.roles.create(data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should include the token in the Authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/roles')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.roles.create(data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#update', function() {
+    beforeEach(function() {
+      this.data = { id: 'rol_ID' };
+
+      this.request = nock(API_URL)
+        .patch('/roles/' + this.data.id)
+        .reply(200, this.data);
+    });
+
+    it('should accept a callback', function(done) {
+      this.roles.update({ id: 'rol_ID' }, {}, done.bind(null, null));
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.roles
+        .update({ id: 'rol_ID' }, {})
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should perform a PATCH request to /api/v2/roles/rol_ID', function(done) {
+      var request = this.request;
+
+      this.roles.update({ id: 'rol_ID' }, {}).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should include the new data in the body of the request', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .patch('/roles/' + this.data.id, this.data)
+        .reply(200);
+
+      this.roles.update({ id: 'rol_ID' }, this.data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .patch('/roles/' + this.data.id)
+        .reply(500);
+
+      this.roles.update({ id: this.data.id }, this.data).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+  });
+
+  describe('#delete', function() {
+    var id = 'rol_ID';
+
+    beforeEach(function() {
+      this.request = nock(API_URL)
+        .delete('/roles/' + id)
+        .reply(200);
+    });
+
+    it('should accept a callback', function(done) {
+      this.roles.delete({ id: id }, done.bind(null, null));
+    });
+
+    it('should return a promise when no callback is given', function(done) {
+      this.roles.delete({ id: id }).then(done.bind(null, null));
+    });
+
+    it('should perform a delete request to /roles/' + id, function(done) {
+      var request = this.request;
+
+      this.roles.delete({ id: id }).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .delete('/roles/' + id)
+        .reply(500);
+
+      this.roles.delete({ id: id }).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should include the token in the authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .delete('/roles/' + id)
+        .matchHeader('authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.roles.delete({ id: id }).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#getPermissions', function() {
+    var data = {
+      id: 'role_id'
+    };
+
+    beforeEach(function() {
+      this.request = nock(API_URL)
+        .get('/roles/' + data.id + '/permissions')
+        .reply(200);
+    });
+
+    it('should accept a callback', function(done) {
+      this.roles.getPermissions(data, done.bind(null, null));
+    });
+
+    it('should return a promise when no callback is given', function(done) {
+      this.roles.getPermissions(data).then(done.bind(null, null));
+    });
+
+    it('should perform a GET request to /api/v2/roles/rol_ID/permissions', function(done) {
+      var request = this.request;
+
+      this.roles.getPermissions(data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/roles/' + data.id + '/permissions')
+        .reply(500);
+
+      this.roles.getPermissions(data).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should include the token in the authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/roles/' + data.id + '/permissions')
+        .matchHeader('authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.roles.getPermissions(data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#addPermissions', function() {
+    beforeEach(function() {
+      this.data = {
+        id: 'rol_ID'
+      };
+      this.body = { permission_name: 'My Permission', resource_server_identifier: 'test123' };
+
+      this.request = nock(API_URL)
+        .post('/roles/' + this.data.id + '/permissions')
+        .reply(200);
+    });
+
+    it('should accept a callback', function(done) {
+      this.roles.addPermissions(this.data, {}, function() {
+        done();
+      });
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.roles
+        .addPermissions(this.data, {})
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/roles/' + this.data.id + '/permissions')
+        .reply(500);
+
+      this.roles.addPermissions(this.data, {}).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should perform a POST request to /api/v2/roles/rol_ID/permissions', function(done) {
+      var request = this.request;
+
+      this.roles.addPermissions(this.data, {}).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass the data in the body of the request', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/roles/' + this.data.id + '/permissions', this.body)
+        .reply(200);
+
+      this.roles.addPermissions(this.data, this.body).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should include the token in the Authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/roles/' + this.data.id + '/permissions')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.roles.addPermissions(this.data, {}).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#removePermissions', function() {
+    beforeEach(function() {
+      this.data = {
+        id: 'rol_ID'
+      };
+      this.body = { permission_name: 'My Permission', resource_server_identifier: 'test123' };
+
+      this.request = nock(API_URL)
+        .delete('/roles/' + this.data.id + '/permissions', {})
+        .reply(200);
+    });
+
+    it('should validate empty roleId', function() {
+      var _this = this;
+      expect(function() {
+        _this.roles.removePermissions({ id: null }, _this.body, function() {});
+      }).to.throw('The roleId passed in params cannot be null or undefined');
+    });
+
+    it('should validate non-string roleId', function() {
+      var _this = this;
+      expect(function() {
+        _this.roles.removePermissions({ id: 123 }, _this.body, function() {});
+      }).to.throw('The role Id has to be a string');
+    });
+
+    it('should accept a callback', function(done) {
+      this.roles.removePermissions(this.data, {}, function() {
+        done();
+      });
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.roles
+        .removePermissions(this.data, {})
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/roles/' + this.data.id + '/permissions')
+        .reply(500);
+
+      this.roles.removePermissions(this.data, {}).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should perform a DELETE request to /api/v2/roles/rol_ID/permissions', function(done) {
+      var request = this.request;
+
+      this.roles.removePermissions(this.data, {}).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass the data in the body of the request', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .delete('/roles/' + this.data.id + '/permissions', this.body)
+        .reply(200);
+
+      this.roles.removePermissions(this.data, this.body).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should include the token in the Authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .delete('/roles/' + this.data.id + '/permissions')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.roles.removePermissions(this.data, {}).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#getUsers', function() {
+    var data = {
+      id: 'role_id'
+    };
+
+    beforeEach(function() {
+      this.request = nock(API_URL)
+        .get('/roles/' + data.id + '/users')
+        .reply(200);
+    });
+
+    it('should accept a callback', function(done) {
+      this.roles.getUsers(data, done.bind(null, null));
+    });
+
+    it('should return a promise when no callback is given', function(done) {
+      this.roles.getUsers(data).then(done.bind(null, null));
+    });
+
+    it('should perform a GET request to /api/v2/roles/rol_Id/users', function(done) {
+      var request = this.request;
+
+      this.roles.getUsers(data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/roles/' + data.id + '/users')
+        .reply(500);
+
+      this.roles.getUsers(data).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should include the token in the authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/roles/' + data.id + '/users')
+        .matchHeader('authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.roles.getUsers(data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+});
diff --git a/test/management/users.tests.js b/test/management/users.tests.js
index 7d55bb7b1..72b8156c5 100644
--- a/test/management/users.tests.js
+++ b/test/management/users.tests.js
@@ -31,7 +31,13 @@ describe('UsersManager', function() {
       'updateUserMetadata',
       'updateAppMetadata',
       'getGuardianEnrollments',
-      'regenerateRecoveryCode'
+      'regenerateRecoveryCode',
+      'getRoles',
+      'assignRoles',
+      'removeRoles',
+      'getPermissions',
+      'assignPermissions',
+      'removePermissions'
     ];
 
     methods.forEach(function(method) {
@@ -1035,4 +1041,494 @@ describe('UsersManager', function() {
       });
     });
   });
+
+  describe('#getRoles', function() {
+    var data = {
+      id: 'user_id'
+    };
+
+    beforeEach(function() {
+      this.request = nock(API_URL)
+        .get('/users/' + data.id + '/roles')
+        .reply(200);
+    });
+
+    it('should accept a callback', function(done) {
+      this.users.getRoles(data, done.bind(null, null));
+    });
+
+    it('should return a promise when no callback is given', function(done) {
+      this.users.getRoles(data).then(done.bind(null, null));
+    });
+
+    it('should perform a GET request to /api/v2/users/user_id/roles', function(done) {
+      var request = this.request;
+
+      this.users.getRoles(data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/users/' + data.id + '/roles')
+        .reply(500);
+
+      this.users.getRoles(data).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should include the token in the authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/users/' + data.id + '/roles')
+        .matchHeader('authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.users.getRoles(data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#assignRoles', function() {
+    beforeEach(function() {
+      this.data = {
+        id: 'user_id'
+      };
+      this.body = { roles: ['role1', 'role2', 'role3'] };
+
+      this.request = nock(API_URL)
+        .post('/users/' + this.data.id + '/roles')
+        .reply(200);
+    });
+
+    it('should validate empty user_id', function() {
+      var _this = this;
+      expect(function() {
+        _this.users.assignRoles({ id: null }, _this.body, function() {});
+      }).to.throw('The user_id cannot be null or undefined');
+    });
+
+    it('should validate non-string user_id', function() {
+      var _this = this;
+      expect(function() {
+        _this.users.assignRoles({ id: 127 }, _this.body, function() {});
+      }).to.throw('The user_id has to be a string');
+    });
+
+    it('should accept a callback', function(done) {
+      this.users.assignRoles(this.data, {}, function() {
+        done();
+      });
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.users
+        .assignRoles(this.data, {})
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/users/' + this.data.id + '/roles')
+        .reply(500);
+
+      this.users.assignRoles(this.data, {}).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should perform a POST request to /api/v2/users/user_id/roles', function(done) {
+      var request = this.request;
+
+      this.users.assignRoles(this.data, {}).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass the data in the body of the request', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/users/' + this.data.id + '/roles', this.body)
+        .reply(200);
+
+      this.users.assignRoles(this.data, this.body).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should include the token in the Authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/users/' + this.data.id + '/roles')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.users.assignRoles(this.data, {}).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#removeRoles', function() {
+    beforeEach(function() {
+      this.data = {
+        id: 'user_id'
+      };
+      this.body = { roles: ['role1', 'role2', 'role3'] };
+
+      this.request = nock(API_URL)
+        .delete('/users/' + this.data.id + '/roles', {})
+        .reply(200);
+    });
+
+    it('should validate empty user_id', function() {
+      var _this = this;
+      expect(function() {
+        _this.users.removeRoles({ id: null }, this.body, function() {});
+      }).to.throw('The user_id cannot be null or undefined');
+    });
+
+    it('should validate non-string user_id', function() {
+      var _this = this;
+      expect(function() {
+        _this.users.removeRoles({ id: 123 }, _this.body, function() {});
+      }).to.throw('The user_id has to be a string');
+    });
+
+    it('should accept a callback', function(done) {
+      this.users.removeRoles(this.data, {}, function() {
+        done();
+      });
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.users
+        .removeRoles(this.data, {})
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/users/' + this.data.id + '/roles')
+        .reply(500);
+
+      this.users.removeRoles(this.data, {}).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should perform a DELETE request to /api/v2/users/user_id/roles', function(done) {
+      var request = this.request;
+
+      this.users.removeRoles(this.data, {}).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass the data in the body of the request', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .delete('/users/' + this.data.id + '/roles', this.body)
+        .reply(200);
+
+      this.users.removeRoles(this.data, this.body).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should include the token in the Authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .delete('/users/' + this.data.id + '/roles')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.users.removeRoles(this.data, {}).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#getPermissions', function() {
+    var data = {
+      id: 'user_id'
+    };
+
+    beforeEach(function() {
+      this.request = nock(API_URL)
+        .get('/users/' + data.id + '/permissions')
+        .reply(200);
+    });
+
+    it('should accept a callback', function(done) {
+      this.users.getPermissions(data, done.bind(null, null));
+    });
+
+    it('should return a promise when no callback is given', function(done) {
+      this.users.getPermissions(data).then(done.bind(null, null));
+    });
+
+    it('should perform a GET request to /api/v2/users/user_id/permissions', function(done) {
+      var request = this.request;
+
+      this.users.getPermissions(data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/users/' + data.id + '/permissions')
+        .reply(500);
+
+      this.users.getPermissions(data).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should include the token in the authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/users/' + data.id + '/permissions')
+        .matchHeader('authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.users.getPermissions(data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#assignPermissions', function() {
+    beforeEach(function() {
+      this.data = {
+        id: 'user_id'
+      };
+      this.body = { permission_name: 'My Permission', resource_server_identifier: 'test123' };
+
+      this.request = nock(API_URL)
+        .post('/users/' + this.data.id + '/permissions')
+        .reply(200);
+    });
+
+    it('should validate empty user_id', function() {
+      var _this = this;
+      expect(function() {
+        _this.users.assignPermissions({ id: null }, this.body, function() {});
+      }).to.throw('The user_id cannot be null or undefined');
+    });
+
+    it('should validate non-string user_id', function() {
+      var _this = this;
+      expect(function() {
+        _this.users.assignPermissions({ id: 123 }, _this.body, function() {});
+      }).to.throw('The user_id has to be a string');
+    });
+
+    it('should accept a callback', function(done) {
+      this.users.assignPermissions(this.data, {}, function() {
+        done();
+      });
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.users
+        .assignPermissions(this.data, {})
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/users/' + this.data.id + '/permissions')
+        .reply(500);
+
+      this.users.assignPermissions(this.data, {}).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should perform a POST request to /api/v2/users/user_id/permissions', function(done) {
+      var request = this.request;
+
+      this.users.assignPermissions(this.data, {}).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass the data in the body of the request', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/users/' + this.data.id + '/permissions', this.body)
+        .reply(200);
+
+      this.users.assignPermissions(this.data, this.body).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should include the token in the Authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/users/' + this.data.id + '/permissions')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.users.assignPermissions(this.data, {}).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#removePermissions', function() {
+    beforeEach(function() {
+      this.data = {
+        id: 'user_id'
+      };
+      this.body = { permission_name: 'My Permission', resource_server_identifier: 'test123' };
+
+      this.request = nock(API_URL)
+        .delete('/users/' + this.data.id + '/permissions', {})
+        .reply(200);
+    });
+
+    it('should validate empty user_id', function() {
+      var _this = this;
+      expect(function() {
+        _this.users.removePermissions({ id: null }, this.body, function() {});
+      }).to.throw('The user_id cannot be null or undefined');
+    });
+
+    it('should validate non-string user_id', function() {
+      var _this = this;
+      expect(function() {
+        _this.users.removePermissions({ id: 123 }, _this.body, function() {});
+      }).to.throw('The user_id has to be a string');
+    });
+
+    it('should accept a callback', function(done) {
+      this.users.removePermissions(this.data, {}, function() {
+        done();
+      });
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.users
+        .removePermissions(this.data, {})
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .post('/users/' + this.data.id + '/permissions')
+        .reply(500);
+
+      this.users.removePermissions(this.data, {}).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should perform a DELETE request to /api/v2/users/user_id/permissions', function(done) {
+      var request = this.request;
+
+      this.users.removePermissions(this.data, {}).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass the data in the body of the request', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .delete('/users/' + this.data.id + '/permissions', this.body)
+        .reply(200);
+
+      this.users.removePermissions(this.data, this.body).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should include the token in the Authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .delete('/users/' + this.data.id + '/permissions')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.users.removePermissions(this.data, {}).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
 });