All files restrict-to-owner.js

89.47% Statements 34/38
81.58% Branches 31/38
100% Functions 3/3
89.19% Lines 33/37
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 801x 1x 1x 1x   1x         15x 15x 15x 1x     14x     13x 1x     12x 4x             8x       8x     1x     7x   7x 1x           6x   6x 5x   5x       5x     5x       5x 2x 2x 1x   3x 2x     2x        
import errors from 'feathers-errors';
import isPlainObject from 'lodash.isplainobject';
import get from 'lodash.get';
import queryWithCurrentUser from './query-with-current-user';
 
const defaults = {
  idField: '_id',
  ownerField: 'userId'
};
 
export default function (options = {}) {
  return function (hook) {
    if (hook.type !== 'before') {
      throw new Error(`The 'restrictToOwner' hook should only be used as a 'before' hook.`);
    }
 
    options = Object.assign({}, defaults, hook.app.get('authentication'), options);
 
    // If it was an internal call then skip this hook
    if (!hook.params.provider) {
      return hook;
    }
 
    if (hook.method === 'find' || hook.id === null) {
      return queryWithCurrentUser({
        idField: options.idField,
        as: options.ownerField
      })(hook);
    }
 
    // Check hook is being called on an allowable method
    Iif (!(hook.method === 'get' || hook.method === 'update' || hook.method === 'patch' || hook.method === 'remove')) {
      throw new errors.MethodNotAllowed(`The 'restrictToOwner' hook should only be used on the 'get', 'update', 'patch' and 'remove' service methods.`);
    }
 
    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(`The current user is missing. You must not be authenticated.`);
    }
 
    const id = get(hook.params.user, options.idField);
 
    if (id === undefined) {
      throw new Error(`'${options.idField} is missing from current user.'`);
    }
 
    // 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 this.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 = field[options.idField];
      }
 
      if (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;
    });
  };
}