All files restrict-to-roles.js

84.31% Statements 43/51
81.25% Branches 39/48
75% Functions 3/4
85.71% Lines 42/49
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 1071x 1x 1x   1x             19x 19x 2x     17x 17x 1x       16x 1x     15x     1x     14x   14x 14x 14x 14x   14x 1x         13x 1x       12x 2x         12x       12x 7x       7x 1x           6x   6x 6x   6x       6x     6x       6x         6x 3x     3x       5x 1x        
import errors from 'feathers-errors';
import isPlainObject from 'lodash.isplainobject';
import get from 'lodash.get';
 
const defaults = {
  fieldName: 'roles',
  idField: '_id',
  ownerField: 'userId',
  owner: false
};
 
export default function (options = {}) {
  if (!options.roles || !options.roles.length) {
    throw new Error(`You need to provide an array of 'roles' to check against.`);
  }
 
  return function (hook) {
    if (hook.type !== 'before') {
      throw new Error(`The 'restrictToRoles' hook should only be used as a 'before' hook.`);
    }
 
    // If it was an internal call then skip this hook
    if (!hook.params.provider) {
      return hook;
    }
 
    if (!hook.params.user) {
      // TODO (EK): Add a debugger call to remind the dev to check their hook chain
      // as they probably don't have the right hooks in the right order.
      throw new errors.NotAuthenticated();
    }
 
    options = Object.assign({}, defaults, hook.app.get('authentication'), options);
 
    let authorized = false;
    let roles = get(hook.params.user, options.fieldName);
    const id = get(hook.params.user, options.idField);
    const error = new errors.Forbidden('You do not have valid permissions to access this.');
 
    if (id === undefined) {
      throw new Error(`'${options.idField} is missing from current user.'`);
    }
 
    // If the user doesn't even have a `fieldName` field and we're not checking
    // to see if they own the requested resource return Forbidden error
    if (!options.owner && roles === undefined) {
      throw error;
    }
 
    // If the roles is not an array, normalize it
    if (!Array.isArray(roles)) {
      roles = [roles];
    }
 
    // Iterate through all the roles the user may have and check
    // to see if any one of them is in the list of permitted roles.
    authorized = roles.some(role => options.roles.indexOf(role) !== -1);
 
    // If we should allow users that own the resource and they don't already have
    // the permitted roles check to see if they are the owner of the requested resource
    if (options.owner && !authorized) {
      Iif (hook.id === null) {
        throw new errors.BadRequest('Can not verify roles when changing many resources.');
      }
 
      if (!hook.id) {
        throw new errors.MethodNotAllowed(`The 'restrictToRoles' hook should only be used on the 'get', 'update', 'patch' and 'remove' service methods if you are using the 'owner' field.`);
      }
 
      // look up the document and throw a Forbidden error if the user is not an owner
      // Set provider as undefined so we avoid an infinite loop if this hook is
      // set on the resource we are requesting.
      const params = Object.assign({}, hook.params, { provider: undefined });
 
      return hook.service.get(hook.id, params).then(data => {
        Iif (data.toJSON) {
          data = data.toJSON();
        } else Iif (data.toObject) {
          data = data.toObject();
        }
 
        let field = get(data, options.ownerField);
 
        // Handle nested Sequelize or Mongoose models
        Iif (isPlainObject(field)) {
          field = get(field, options.idField);
        }
 
        Iif (Array.isArray(field)) {
          const fieldArray = field.map(idValue => idValue.toString());
          if (fieldArray.length === 0 || fieldArray.indexOf(id.toString()) < 0) {
            throw new errors.Forbidden('You do not have the permissions to access this.');
          }
        } else if (field === undefined || field.toString() !== id.toString()) {
          throw new errors.Forbidden('You do not have the permissions to access this.');
        }
 
        return hook;
      });
    }
 
    if (!authorized) {
      throw error;
    }
  };
}