All files restrict-to-owner.js

78.95% Statements 30/38
68.42% Branches 26/38
66.67% Functions 2/3
81.08% Lines 30/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 80 81 821x 1x 1x 1x   1x           15x 15x 15x 1x     14x     13x 1x     12x 4x               8x       8x     1x     7x   7x 1x           6x   6x 6x   6x       6x     6x       6x         6x 3x     3x        
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',
  expandPaths: true
};
 
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,
        expandPaths: options.expandPaths
      })(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 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 = 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;
    });
  };
}