@getanthill/datastore
Version:
Event-Sourced Datastore
216 lines • 8.34 kB
JavaScript
;
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