UNPKG

rab-access

Version:

A TypeScript library for Role-Based Access Control (RBAC) that provides a flexible and powerful permission system with conditional grants, field-level access control, and validation capabilities.

264 lines (257 loc) 8.24 kB
'use strict'; function _extends() { _extends = Object.assign || function assign(target) { for(var i = 1; i < arguments.length; i++){ var source = arguments[i]; for(var key in source)if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key]; } return target; }; return _extends.apply(this, arguments); } class RabGrant { columns(columns) { this._columns = columns; return this; } lookupFilters(filters) { this._lookupFilters = filters; return this; } filters(filters) { this._filters = filters; return this; } ifEqual(fieldOne, fieldTwo) { if (!fieldOne || !fieldTwo) { throw new Error('Rab:ifEqual requires at least two arguments'); } this._checks = this._checks || []; this._checks.push({ operation: 'ifEqual', fields: [ fieldOne, fieldTwo ] }); return this; } ifContains(fieldOne, fieldTwo) { if (!fieldOne || !fieldTwo) { throw new Error('Rab:ifContains requires at two arguments'); } this._checks = this._checks || []; this._checks.push({ operation: 'ifContains', fields: [ fieldOne, fieldTwo ] }); return this; } validator(method) { this._checks = this._checks || []; this._checks.push({ operation: 'validator', method }); return this; } extend(role, permission) { this._extends = { permission, role }; return this; } get json() { return { role: this.role, lookupFilters: this._lookupFilters, columns: this._columns, filters: this._filters, check: this._checks, extends: this._extends }; } get getColumns() { return this._columns; } get extendsConfig() { return this._extends; } constructor(role){ this.role = role; } } function getValueFromPath(object, path) { let result = object; if (path.length == 0) return undefined; for (const key of path){ if (result == null || !result[key]) { return undefined; } result = result[key]; } return result; } const resolvePath = (configObject, payload)=>{ const result = {}; if (!configObject) return result; for(const key in configObject){ if (payload) { const value = getValueFromPath(payload, configObject[key]); if (value !== undefined) { result[key] = value; } } } return result; }; function mergeArray(firstElement, secondElement) { const merged = [ ...firstElement != null ? firstElement : [], ...secondElement != null ? secondElement : [] ]; return merged.length > 0 ? merged : undefined; } function mergeObject(firstElement, secondElement, fallback) { const merged = _extends({}, firstElement, secondElement); return Object.keys(merged).length > 0 ? merged : undefined; } const evaluatePermission = async (options)=>{ const { request, grant, validations } = options; const getCheckOperands = (check)=>{ return resolvePath({ fieldOne: check.fields[0], fieldTwo: check.fields[1] }, request); }; if (grant == null ? void 0 : grant.check) { for (const check of grant.check){ switch(check.operation){ case 'ifEqual': { const operands = getCheckOperands(check); if (operands.fieldOne !== operands.fieldTwo) { return { isAuthorized: false }; } break; } case 'ifContains': { const operands = getCheckOperands(check); if (Array.isArray(operands.fieldTwo) && operands.fieldTwo.includes(operands.fieldOne)) { break; } return { isAuthorized: false }; } case 'validator': { const method = check == null ? void 0 : check.method; const funCall = method ? validations == null ? void 0 : validations[method] : undefined; if (funCall) { await funCall(request); } else { console.error('Missing Validation function function', method); return { isAuthorized: false }; } break; } default: return { isAuthorized: false }; } } } return { isAuthorized: true, fields: grant.columns, filters: resolvePath(grant == null ? void 0 : grant.filters, request), lookupFilters: (grant == null ? void 0 : grant.lookupFilters) ? Object.keys(grant.lookupFilters).reduce((acc, key)=>_extends({}, acc, { [key]: resolvePath(grant.lookupFilters[key], request) }), {}) : undefined }; }; class Rab { get permissions() { return this.permissionMap; } static schema(config) { return new Rab(config); } static grant(role) { return new RabGrant(role); } static auth(path) { return [ 'auth', ...Array.isArray(path) ? path : [ path ] ]; } static params(path) { return [ 'params', ...Array.isArray(path) ? path : [ path ] ]; } static query(path) { return [ 'query', ...Array.isArray(path) ? path : [ path ] ]; } resolvePermissions(permission, role) { var _mergeArray; const grants = this.permissionMap[permission] || []; const grandInstance = grants.find((el)=>el.role == role); if (!grandInstance) throw new Error('Rab: permission not found for role: ' + role + ', permission:' + permission); const currentGrant = grandInstance.json; const extendedGrand = currentGrant.extends ? this.resolvePermissions(currentGrant.extends.permission, currentGrant.extends.role) : undefined; const columns = (_mergeArray = mergeArray(currentGrant.columns)) == null ? void 0 : _mergeArray.map((el)=>{ if (typeof el == 'string') return el; return el.field; }); return _extends({}, currentGrant, { columns: mergeArray(columns, extendedGrand == null ? void 0 : extendedGrand.columns), check: mergeArray(currentGrant.check, extendedGrand == null ? void 0 : extendedGrand.check), filters: mergeObject(extendedGrand == null ? void 0 : extendedGrand.filters, currentGrant.filters), lookupFilters: mergeObject(extendedGrand == null ? void 0 : extendedGrand.lookupFilters, currentGrant.lookupFilters) }); } async getGrant(options) { const { permission, request, validations, role } = options; try { const grant = this.resolvePermissions(permission, role); return await evaluatePermission({ grant, validations, request }); } catch (e) { console.error(e); return { isAuthorized: false }; } } constructor(config){ this.permissionMap = {}; this.permissionMap = config; } } exports.Rab = Rab;