UNPKG

@getanthill/datastore

Version:

Event-Sourced Datastore

216 lines 8.34 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const typings_1 = require("../typings"); const ajv_1 = __importDefault(require("ajv")); const jsonpatch = __importStar(require("fast-json-patch")); const get_1 = __importDefault(require("lodash/get")); const pick_1 = __importDefault(require("lodash/pick")); const authorizations_1 = require("../typings/authorizations"); class Authz { constructor(config, services) { this.config = { noPolicyVerb: typings_1.authorizations.AUTHORIZATION_VERB_ALLOW, }; this.config = { ...this.config, ...config, }; this.services = services; this.validator = new ajv_1.default({ strict: false, useDefaults: false, coerceTypes: true, }); } static getScopes(scope) { return scope.reduce((s, c, i) => [ ...s, i === 0 ? [c] : [...s[i - 1], c], ], []); } applyObligations(obj, obligations) { for (const obligation of obligations) { if (obj[obligation.source] === undefined) { continue; } if (obligation.type === 'patch') { const patch = obligation.value.map((p) => { if (p.value?.[0] === '{') { p.value = (0, get_1.default)(obj, p.value.slice(1, -1)); } return p; }); obj[obligation.source] = jsonpatch.applyPatch(obj[obligation.source], patch).newDocument; } if (obligation.type === 'pick') { obj[obligation.source] = Array.isArray(obj[obligation.source]) ? obj[obligation.source].map((entity) => (0, pick_1.default)(entity, obligation.value)) : (0, pick_1.default)(obj[obligation.source], obligation.value); } } return obj; } static areRulesValidated(validations) { return validations.reduce((s, c) => s === true && c.action.is_valid === true && c.subject.is_valid === true && c.object.is_valid === true && c.context.is_valid === true, validations.length > 0); } static getDecision(decisions, config) { return decisions.reduce((s, decision) => { s.verb = s.verb === authorizations_1.AUTHORIZATION_VERB_DENY ? s.verb : decision.verb; s.validations.push(...decision.validations); s.obligations = s.verb === authorizations_1.AUTHORIZATION_VERB_DENY ? [] : [...s.obligations, ...decision.obligations]; return s; }, { verb: (decisions.length > 0 ? authorizations_1.AUTHORIZATION_VERB_ALLOW : config.noPolicyVerb), obligations: [], validations: [], }); } static isAllowed(decisions) { return decisions.reduce((s, decision) => { if (s === false) { return s; } if (Authz.areRulesValidated(decision.validations) === true) { return decision.verb === authorizations_1.AUTHORIZATION_VERB_ALLOW; } return decision.verb === authorizations_1.AUTHORIZATION_VERB_DENY; }, decisions.length > 0); } static validateScope(validator, schema, obj) { const schemaId = schema.$id; let validate; if (!schema.$id) { validate = validator.compile(schema); } else { validator.addSchema(schema, schemaId); validate = validator.getSchema(schema.$id); } const isValid = validate(obj); const errors = validate.errors ?? []; return { is_valid: isValid, errors, }; } static validateRules(validator, request, attributes, rules) { return rules.map((rule) => { return { action: Authz.validateScope(validator, rule.action, { ...request.action, _attributes: attributes.action, }), subject: Authz.validateScope(validator, rule.subject, { ...request.subject, _attributes: attributes.subject, }), object: Authz.validateScope(validator, rule.object, { ...request.object, _attributes: attributes.object, }), context: Authz.validateScope(validator, rule.context, { ...request.context, _attributes: attributes.context, }), }; }); } static validatePolicy(validator, request, attributes, policy) { const validations = Authz.validateRules(validator, request, attributes, policy.rules); if (Authz.areRulesValidated(validations) === false) { return { verb: authorizations_1.AUTHORIZATION_VERB_ALLOW, validations, obligations: [], }; } return { verb: policy.verb, validations, obligations: policy.obligations, }; } static validatePolicies(validator, request, attributes, policies, config) { const decisions = policies.map((policy) => Authz.validatePolicy(validator, request, attributes, policy)); return Authz.getDecision(decisions, config); } async getScopeAttributes(scope) { return this.services.models .getModel('attributes') .find(this.services.mongodb, { is_enabled: true, scope: { $in: Authz.getScopes(scope), }, }) .toArray(); } async getRequestAttributes(request) { return { action: await this.getScopeAttributes(request.action.scope), subject: await this.getScopeAttributes(request.subject.scope), object: await this.getScopeAttributes(request.object.scope), context: await this.getScopeAttributes(request.context.scope), }; } async getRequestPolicies(request) { return this.services.models .getModel('policies') .find(this.services.mongodb, { is_enabled: true, scope: { $in: [ ...Authz.getScopes(request.action.scope), ...Authz.getScopes(request.subject.scope), ...Authz.getScopes(request.object.scope), ...Authz.getScopes(request.context.scope), ], }, }) .toArray(); } async authorize(request) { const [attributes, policies] = await Promise.all([ this.getRequestAttributes(request), this.getRequestPolicies(request), ]); return Authz.validatePolicies(this.validator, request, attributes, policies, this.config); } } exports.default = Authz; //# sourceMappingURL=authz.js.map