@superawesome/permissions
Version:
Fine grained permissions / access control with ownerships & attribute picking, done right.
112 lines • 5.08 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.deleteEmptyArrayKeys = exports.isLike = exports.isHash = exports.isArraySetEqual = exports.buildAccessControl = exports.projectPDWithDefaultsToInternal = exports.hasSomeOwnGrant = exports.stringify = void 0;
const _ = require("lodash");
const accesscontrol_re_1 = require("accesscontrol-re");
const types_1 = require("./types");
const logger_1 = require("./logger");
exports.stringify = (obj) => JSON.stringify(obj, null, 2);
exports.hasSomeOwnGrant = (ipd) => _.some(ipd.grant, (attrs, action) => _.endsWith(action, `:${types_1.EPossession.own}`));
exports.projectPDWithDefaultsToInternal = _.curry((defaults, pd) => {
const { isOwner, listOwned, limitOwned, descr } = pd;
const roles = pd.roles || defaults.roles;
const resource = pd.resource || defaults.resource;
// prettier-ignore
if (!roles)
throw new Error(`SA-Permissions: InvalidPermissionDefinitionError: missing "roles" in ${exports.stringify(pd)}.`);
// prettier-ignore
if (!resource)
throw new Error(`SA-Permissions: InvalidPermissionDefinitionError: missing "resource" in ${exports.stringify(pd)}.`);
// prettier-ignore
if (_.isEmpty(pd.grant))
throw new Error(`SA-Permissions: InvalidPermissionDefinitionError: missing or empty "grant" in ${exports.stringify(pd)}`);
// convert ['list', 'ofActions'] to {list: null, ofActions: null}
const grantTempObj = !_.isArray(pd.grant)
? pd.grant
: _.reduce(pd.grant, (acc, cur) => {
acc[cur] = null;
return acc;
}, {});
// apply defaults
const grant = _.reduce(grantTempObj, (acc, possibleAttributes, actionPerhapsWithPossession) => {
const [action, possiblePossession] = actionPerhapsWithPossession.split(':');
const possession = possiblePossession || pd.possession || defaults.possession || types_1.EPossession.any;
const actionWithPossession = `${action}:${possession}`;
acc[actionWithPossession] = possibleAttributes ||
pd.attributes ||
defaults.attributes || ['*'];
return acc;
}, {});
const ipd = {
roles: _.isArray(roles) ? roles : [roles],
resource,
descr,
isOwner,
listOwned,
limitOwned,
grant,
};
return ipd;
});
exports.buildAccessControl = (permissionDefinitions) => {
if (_.isEmpty(permissionDefinitions))
throw new Error('SA-Permissions: cant build with empty permissionDefinitions!');
const acre = new accesscontrol_re_1.AccessControlRe();
// first pass: execute only the grant's
// so all roles are defined
_.each(permissionDefinitions, (pd // {grant, resource, roles}
) => _.each(pd.grant, (attributes, actionPossession) => {
const [action, possession] = actionPossession.split(':');
const accessInfo = {
action,
possession,
attributes,
role: pd.roles,
resource: pd.resource,
};
// @todo: check it's a valid IAccessInfo - use class-validator
// - has at least one Role, one Resource
// - has possession, added either as the default or the grant it self
logger_1.getLogger().debug(`addAccessInfo(${exports.stringify(accessInfo)})`);
acre.addAccessInfo(accessInfo);
}));
// @todo: second pass: extend roles (already defined on 1st pass)
_.each(permissionDefinitions, (pd) => {
if (pd.extend) {
throw new Error('extend is not supported yet, and will not until this is fixed https://github.com/onury/accesscontrol/issues/34#issuecomment-466387586 and the whole extend idea redesigned and tested properly.');
// this._accessControl.grant(pd.role).extend(pd.extend);
}
});
acre.build();
return [acre.accessControl, acre];
};
exports.isArraySetEqual = (ar1, ar2, comparator1, comparator2) => {
if (!comparator1)
comparator1 = (a, b) => a === b;
if (!comparator2)
comparator2 = _.flip(comparator1);
return (_.isArray(ar1) &&
_.isArray(ar2) &&
ar1.length === ar2.length &&
_.isEmpty(_.differenceWith(ar1, ar2, comparator1)) &&
_.isEmpty(_.differenceWith(ar2, ar1, comparator2)));
};
exports.isHash = (o) => _.isObjectLike(o) && !_.isArray(o) && !_.isFunction(o);
// Returns true if the keys+values of `o1` are all _.isEqual to `o2`'s corresponding ones
exports.isLike = (o1 = {}, o2 = {}) => _.isEqual(o1, o2) ||
(exports.isHash(o1) && exports.isHash(o2) && _.every(_.keys(o1), (o1Key) => exports.isLike(o1[o1Key], o2[o1Key])));
// Deletes all object keys with a value of `[]` (empty array)
// note: mutates the object
exports.deleteEmptyArrayKeys = (o) => {
if (exports.isHash(o)) {
_.each(o, (val, key) => {
if (_.isEqual(val, [])) {
delete o[key];
}
else
exports.deleteEmptyArrayKeys(val);
});
}
return o;
};
//# sourceMappingURL=utils.js.map