jinaga
Version:
Data management for web and mobile applications.
103 lines • 5.39 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthorizationEngine = exports.Forbidden = void 0;
const hash_1 = require("../fact/hash");
const sorter_1 = require("../fact/sorter");
const storage_1 = require("../storage");
const fn_1 = require("../util/fn");
const trace_1 = require("../util/trace");
class Forbidden extends Error {
constructor(message) {
const trueProto = new.target.prototype;
super(message);
this.__proto__ = trueProto;
}
}
exports.Forbidden = Forbidden;
class AuthorizationEngine {
constructor(authorizationRules, store) {
this.authorizationRules = authorizationRules;
this.store = store;
}
authorizeFacts(factEnvelopes, userFact) {
return __awaiter(this, void 0, void 0, function* () {
const facts = factEnvelopes.map(e => e.fact);
const existing = yield this.store.whichExist(facts);
const sorter = new sorter_1.TopologicalSorter();
const userKeys = (userFact && userFact.fields.hasOwnProperty("publicKey"))
? [userFact.fields.publicKey]
: [];
const results = yield (0, fn_1.mapAsync)(sorter.sort(facts, (p, f) => this.visit(p, f, userKeys, facts, factEnvelopes, existing)), x => x);
const rejected = results.filter(r => r.verdict === "Reject");
if (rejected.length > 0) {
const distinctTypes = rejected
.map(r => r.fact.type)
.filter(fn_1.distinct)
.join(", ");
const count = rejected.length === 1 ? "1 fact" : `${rejected.length} facts`;
const message = `Rejected ${count} of type ${distinctTypes}.`;
throw new Forbidden(message);
}
return results;
});
}
visit(predecessors, fact, userKeys, factRecords, factEnvelopes, existing) {
return __awaiter(this, void 0, void 0, function* () {
const predecessorResults = yield (0, fn_1.mapAsync)(predecessors, p => p);
if (predecessorResults.some(p => p.verdict === "Reject")) {
const predecessor = predecessorResults
.filter(p => p.verdict === "Reject")
.map(p => p.fact.type)
.join(', ');
trace_1.Trace.warn(`The fact ${fact.type} cannot be authorized because its predecessor ${predecessor} is not authorized.`);
return { fact, verdict: "Reject" };
}
if (!(0, hash_1.verifyHash)(fact)) {
const computedHash = (0, hash_1.computeHash)(fact.fields, fact.predecessors);
trace_1.Trace.warn(`The hash of ${fact.type} does not match: computed ${computedHash}, provided ${fact.hash}.`);
return { fact, verdict: "Reject" };
}
if (existing.some((0, storage_1.factReferenceEquals)(fact))) {
return { fact, verdict: "Existing" };
}
const envelope = factEnvelopes.find((0, storage_1.factEnvelopeEquals)(fact));
const envelopeKeys = envelope ? envelope.signatures.map(s => s.publicKey) : [];
const candidateKeys = envelopeKeys.concat(userKeys);
const population = yield this.authorizationRules.getAuthorizedPopulation(candidateKeys, fact, factRecords, this.store);
if (population.quantifier === "none") {
if (this.authorizationRules.hasRule(fact.type)) {
trace_1.Trace.warn(`The user is not authorized to create a fact of type ${fact.type}.`);
}
else {
trace_1.Trace.warn(`The fact ${fact.type} has no authorization rules.`);
}
return { fact, verdict: "Reject" };
}
else if (population.quantifier === "some") {
if (population.authorizedKeys.length === 0) {
trace_1.Trace.warn(`The user is not authorized to create a fact of type ${fact.type}.`);
return { fact, verdict: "Reject" };
}
return { fact, verdict: "Accept", newPublicKeys: population.authorizedKeys };
}
else if (population.quantifier === "everyone") {
return { fact, verdict: "Accept", newPublicKeys: [] };
}
else {
const _exhaustiveCheck = population;
throw new Error(`Unknown quantifier ${_exhaustiveCheck.quantifier}.`);
}
});
}
}
exports.AuthorizationEngine = AuthorizationEngine;
//# sourceMappingURL=authorization-engine.js.map