UNPKG

@openfga/frontend-utils

Version:

Exposes helpful utilities for building authoring experiences of OpenFGA Models.

125 lines (124 loc) 6.85 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AuthorizationModelGraphBuilder = void 0; const graph_typings_1 = require("./graph.typings"); class AuthorizationModelGraphBuilder { constructor(authorizationModel, store) { this.authorizationModel = authorizationModel; this.store = store; this._graph = { nodes: [], edges: [] }; this.buildGraph(); } static getStoreId(storeName) { return `store|${storeName}`; } static getTypeId(typeId) { return `type|${typeId}`; } static getRelationId(typeId, relationKey) { return `${typeId}.relation|${relationKey}`; } buildGraph() { var _a, _b, _c; const storeName = ((_a = this.store) === null || _a === void 0 ? void 0 : _a.name) || ((_b = this.store) === null || _b === void 0 ? void 0 : _b.id) || "Store"; const rootId = AuthorizationModelGraphBuilder.getStoreId(storeName); const authorizationModelGraph = { nodes: [{ id: rootId, label: storeName, group: graph_typings_1.GraphNodeGroup.StoreName }], edges: [], }; (_c = this.authorizationModel.type_definitions) === null || _c === void 0 ? void 0 : _c.forEach((typeDef) => { const graph = this.getTypeGraph(typeDef, authorizationModelGraph); authorizationModelGraph.nodes = authorizationModelGraph.nodes.concat(graph.nodes); authorizationModelGraph.edges = authorizationModelGraph.edges.concat(graph.edges); }); this._graph = authorizationModelGraph; } // A relation definition has self if `this` is in the relation definition checkIfRelationAssignable(relationDef) { var _a, _b, _c, _d; return !!(relationDef.this || ((_a = relationDef.difference) === null || _a === void 0 ? void 0 : _a.base.this) || ((_b = relationDef.difference) === null || _b === void 0 ? void 0 : _b.subtract.this) || (((_c = relationDef.intersection) === null || _c === void 0 ? void 0 : _c.child) || []).some((child) => this.checkIfRelationAssignable(child)) || (((_d = relationDef.union) === null || _d === void 0 ? void 0 : _d.child) || []).some((child) => this.checkIfRelationAssignable(child))); } // Get the sources that can be assignable to a relation getAssignableSourcesForRelation(relationDef, relationMetadata) { const assignableSources = { types: [], relations: [], conditions: [], publicTypes: [], isAssignable: false }; // If this is not used anywhere, then it's not assignable if (!this.checkIfRelationAssignable(relationDef)) { return assignableSources; } const assignable = relationMetadata.directly_related_user_types; assignable === null || assignable === void 0 ? void 0 : assignable.forEach((relationRef) => { // TODO: wildcard and conditions if (!(relationRef.relation || relationRef.wildcard || relationRef.condition)) { return; } // TODO: Mark relations as assignable once supported if (relationRef.relation) { assignableSources.relations.push(AuthorizationModelGraphBuilder.getRelationId(relationRef.type, relationRef.relation)); return; } assignableSources.isAssignable = true; assignableSources.types.push(AuthorizationModelGraphBuilder.getTypeId(relationRef.type)); }); return assignableSources; } addRelationToRelationEdge(typeGraph, typeId, fromRelationKey, toRelation) { typeGraph.edges.push({ from: AuthorizationModelGraphBuilder.getRelationId(typeId, fromRelationKey), to: AuthorizationModelGraphBuilder.getRelationId(typeId, toRelation.relation), group: graph_typings_1.GraphEdgeGroup.RelationToRelation, dashes: true, }); } getTypeGraph(typeDef, authorizationModelGraph, { showAssignable } = {}) { const typeId = AuthorizationModelGraphBuilder.getTypeId(typeDef.type); const typeGraph = { nodes: [{ id: typeId, label: typeDef.type, group: graph_typings_1.GraphNodeGroup.Type }], edges: [{ from: authorizationModelGraph.nodes[0].id, to: typeId, group: graph_typings_1.GraphEdgeGroup.StoreToType }], }; const relationDefs = (typeDef === null || typeDef === void 0 ? void 0 : typeDef.relations) || {}; Object.keys(relationDefs).forEach((relationKey) => { var _a, _b, _c; const relationId = AuthorizationModelGraphBuilder.getRelationId(typeId, relationKey); const relationDef = relationDefs[relationKey] || {}; const assignableSources = this.getAssignableSourcesForRelation(relationDef, ((_b = (_a = typeDef.metadata) === null || _a === void 0 ? void 0 : _a.relations) === null || _b === void 0 ? void 0 : _b[relationKey]) || {}); const isAssignable = assignableSources.isAssignable; // If a relation definition does not have this, then we call it a `permission`, e.g. not directly assignable typeGraph.nodes.push({ id: relationId, label: relationKey, group: isAssignable ? graph_typings_1.GraphNodeGroup.AssignableRelation : graph_typings_1.GraphNodeGroup.NonassignableRelation, }); if (showAssignable) { // TODO: Support assignable relations and wildcards, and conditionals assignableSources.types.forEach((assignableSource) => { typeGraph.edges.push({ from: AuthorizationModelGraphBuilder.getTypeId(assignableSource), to: relationId, group: graph_typings_1.GraphEdgeGroup.AssignableSourceToRelation, }); }); } // TODO: Support - 1. AND, 2. BUT NOT, 3. Nested relations, 4. Tuple to Userset typeGraph.edges.push({ from: typeId, to: relationId, group: graph_typings_1.GraphEdgeGroup.TypeToRelation }); if (relationDef.computedUserset) { this.addRelationToRelationEdge(typeGraph, typeId, relationKey, relationDef.computedUserset); } else { (((_c = relationDef.union) === null || _c === void 0 ? void 0 : _c.child) || []).forEach((child) => { if (child.computedUserset) { this.addRelationToRelationEdge(typeGraph, typeId, relationKey, child.computedUserset); } }); } }); return typeGraph; } get graph() { return this._graph; } } exports.AuthorizationModelGraphBuilder = AuthorizationModelGraphBuilder;