All files owner-or-restrict-changes.js

80% Statements 48/60
71.7% Branches 38/53
83.33% Functions 5/6
80.7% Lines 46/57
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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 1231x 1x   1x           4x     4x   4x   4x   5x         4x       2x 2x 2x 1x       2x     8x 8x 8x 1x     7x       7x 1x       6x 1x     5x   5x 2x   2x 1x   1x       3x   3x   3x         3x     3x   3x 3x 3x         3x     3x       3x                     3x 2x   2x 1x   1x       1x          
import errors from 'feathers-errors';
import _ from 'lodash';
 
const defaults = {
  idField: '_id',
  ownerField: 'userId'
};
 
function getRestrictions (restrictions, user, data) {
  const name = _.get(user, 'name') || _.get(user, 'email') || 'You';
 
  // Make restrictions an array if it's not
  restrictions = _.castArray(restrictions);
 
  var includeS = '';
 
  const restrictionsString = _
    .chain(restrictions)
    .map(restriction => _.has(data, restriction) ? restriction : null)
    .compact()
    .value()
    .join(', ');
 
  if (!restrictionsString) { return null; }
 
  var restrictionsStringModified;
 
  Eif (restrictionsString.split(',').length > 0) {
    restrictionsStringModified = restrictionsString.replace(/,(?!.*,)/gmi, ' or');
    if (restrictionsString.split(',').length > 1) {
      includeS = 's';
    }
  }
 
  return `${name} ${name === 'You' ? 'are' : 'is'} not permitted to update the ${restrictionsStringModified} field${includeS}.`;
}
 
export default function (options = {}) {
  return function (hook) {
    if (hook.type !== 'before') {
      throw new Error(`The 'ownerOrRestrictChanges' hook should only be used as a 'before' hook.`);
    }
 
    Iif (hook.method !== 'update' && hook.method !== 'patch') {
      throw new errors.MethodNotAllowed(`The 'ownerOrRestrictChanges' hook should only be used on the 'update' or 'patch' service methods.`);
    }
 
    if (!hook.id) {
      throw new errors.MethodNotAllowed(`The hook.id for the item must be supplied.`);
    }
 
    // If it was an internal call then skip this hook
    if (!hook.params.provider) {
      return hook;
    }
 
    let restrictionMessage = '';
 
    if (!hook.params.user) {
      restrictionMessage = getRestrictions(options.restrictOn, _.get(hook, 'params.user'), hook.data);
 
      if (restrictionMessage) {
        throw new errors.Forbidden(restrictionMessage);
      } else {
        return hook;
      }
    }
 
    options = Object.assign({}, defaults, hook.app.get('auth'), options);
 
    const id = hook.params.user[options.idField];
 
    Iif (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 and tries to change restricted fields
    return new Promise((resolve, reject) => {
      // 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 => {
        Eif (data.toJSON) {
          data = data.toJSON();
        } else if (data.toObject) {
          data = data.toObject();
        }
 
        let field = 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) {
            restrictionMessage = getRestrictions(options.restrictOn, _.get(hook, 'params.user'), hook.data);
 
            if (restrictionMessage) {
              throw new errors.Forbidden(restrictionMessage);
            } else {
              return resolve(hook);
            }
          }
        } else if (field === undefined || field.toString() !== id.toString()) {
          restrictionMessage = getRestrictions(options.restrictOn, _.get(hook, 'params.user'), hook.data);
 
          if (restrictionMessage) {
            throw new errors.Forbidden(restrictionMessage);
          } else {
            return resolve(hook);
          }
        }
 
        resolve(hook);
      }).catch(reject);
    });
  };
}