@clickup/ent-framework
Version:
A PostgreSQL graph-database-alike library with microsharding and row-level security
74 lines • 3.13 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IncomingEdgeFromVCExists = void 0;
const misc_1 = require("../../internal/misc");
const types_1 = require("../../types");
const Predicate_1 = require("./Predicate");
/**
* An ent may represent not necessarily a node in the graph, but also an edge
* between two nodes. Consider EntMember in the below example:
*
* vc.principal <--- EntMember[user_id, company_id] ---> EntCompany
*
* This predicate verifies that for a e.g. given EntCompany row and a given VC,
* an EntMember row exists (and optionally matches some criterion) in the
* database.
*
* - entEdgeVCField = user_id in the above example
* - entEdgeFKField = company_id in the above example
* - if an EntMember object exists, it must also match entEdgeFilter()
*/
class IncomingEdgeFromVCExists {
constructor(EntEdge, entEdgeVCField, entEdgeFKField, entEdgeFilter) {
this.EntEdge = EntEdge;
this.entEdgeVCField = entEdgeVCField;
this.entEdgeFKField = entEdgeFKField;
this.entEdgeFilter = entEdgeFilter;
this.instanceID = (0, misc_1.localUniqueInt)();
this.name =
this.constructor.name +
"(" +
this.EntEdge.name +
"[" +
`${this.entEdgeVCField}=vc, ` +
`${this.entEdgeFKField}=row.${types_1.ID}` +
"]" +
")";
}
async check(vc, row) {
const cache = vc.cache(Predicate_1.IDsCacheCanReadIncomingEdge);
const cacheKey = (0, misc_1.nullthrows)(row[types_1.ID]) + ":" + this.instanceID;
if (cache.has(cacheKey)) {
return true;
}
const where = {
[this.entEdgeFKField]: row[types_1.ID],
[this.entEdgeVCField]: vc.principal,
};
let allow;
if (this.entEdgeFilter) {
// We use an omni VC here to avoid cyclic references where the edge ent
// delegates permission checks to the row ent, and row ent loads the edge
// ent to run the edgeEntFilter function. It's safe, because:
// 1. Omni VC is always demoted to the current user's VC (which is vc
// since we filter by vc.principal above) or to a guest VC if it cannot
// find a user_id field in the edge ent (which also never happens).
// 2. The edgeEntFilter function is synchronous, so it can't physically
// access the database anyway.
const ents = await this.EntEdge.select(vc.toOmniDangerous(), where, 1);
const filtered = ents.filter((ent) => this.entEdgeFilter(ent));
allow = filtered.length > 0;
}
else {
// Exists is not privacy-checked (it doesn't fetch any row to be checked).
allow = await this.EntEdge.exists(vc, where);
}
if (allow) {
cache.add(cacheKey);
return true;
}
return false;
}
}
exports.IncomingEdgeFromVCExists = IncomingEdgeFromVCExists;
//# sourceMappingURL=IncomingEdgeFromVCExists.js.map