All files / lib/hooks/authorize authorize.hook.utils.ts

0% Statements 0/48
0% Branches 0/32
0% Functions 0/12
0% Lines 0/45

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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                                                                                                                                                                                           
import _get from "lodash/get";
import _set from "lodash/set";
import _unset from "lodash/unset";
 
import { Forbidden } from "@feathersjs/errors";
 
import { Ability, PureAbility, subject } from "@casl/ability";
import { HookContext } from "@feathersjs/feathers";
 
import {
  isMulti
} from "@artesa/feathers-utils";
 
import { 
  AuthorizeHookOptions,
  Path
} from "../../types";
 
export const makeDefaultOptions = (options?: AuthorizeHookOptions): AuthorizeHookOptions => {
  options = options || {} as AuthorizeHookOptions;
  options.getModelName = options.getModelName || (context => {
    const { service } = context;
    const modelName = service.modelName || service.Model && service.Model.name;
    return modelName;
  });
  options.subjectHelper = options.subjectHelper || subject;
  options.checkMultiActions = false;
  options.actionOnForbidden = options.actionOnForbidden || (() => {
    throw new Forbidden("You're not allowed to make this request");
  });
  return options;
};
 
export const getAbility = (context: HookContext, options?: AuthorizeHookOptions): Promise<PureAbility|undefined> => {
  options = options || {};
  if (!context.params) { return Promise.resolve(undefined); }
  if (context.params.ability) { return Promise.resolve(context.params.ability); }
  if (options.getAbility && typeof options.getAbility === "function") {
    const ability = options.getAbility(context);
    return Promise.resolve(ability);
  }
  return Promise.resolve(undefined);
};
 
const move = (context: HookContext, fromPath: Path, toPath: Path) => {
  const val = _get(context, fromPath);
 
  if (val !== undefined) {
    _unset(context, fromPath);
    _set(context, toPath, val);
  }
  return val;
};
 
export const throwUnlessCan = (ability: Ability, method: string, resource: string|Record<string, unknown>, modelName: string): void => {
  if (ability.cannot(method, resource)) {
    throw new Forbidden(`You are not allowed to ${method} ${modelName}`);
  }
};
 
export const checkMulti = (context: HookContext, ability: PureAbility, modelName: string): boolean => {
  const { method } = context;
  const currentIsMulti = isMulti(context);
  if (!currentIsMulti) { return true; }
  if (
    (method === "find" && ability.can(method, modelName)) ||
    (ability.can(`${method}-multi`, modelName))
  ) {
    return true;
  }
  
  throw new Forbidden(`You're not allowed to multi-${method} ${modelName}`);
};
 
export const hide$select = (context: HookContext): unknown => {
  return move(context, "params.query.$select", "params.casl.$select");
};
 
export const restore$select = (context: HookContext): unknown[]|undefined => {
  return move(context, "params.casl.$select", "params.query.$select");
};
 
export const get$select = (context: HookContext): unknown => {
  return getPersistedConfig(context, "$select");
};
 
export const setPersistedConfig = (context: HookContext, key: Path, val: unknown): HookContext => {
  return _set(context, `params.casl.${key}`, val);
};
 
export const getPersistedConfig = (context: HookContext, key: Path): unknown => {
  return _get(context, `params.casl.${key}`);
};