@contember/engine-content-api 2.1.0-beta.1 → 2.1.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/development/index.cjs +1 -0
- package/dist/development/index.cjs.map +1 -1
- package/dist/development/index.js +2 -1
- package/dist/development/src/ExecutionContainer.cjs +40 -4
- package/dist/development/src/ExecutionContainer.cjs.map +1 -1
- package/dist/development/src/ExecutionContainer.js +41 -5
- package/dist/development/src/ExecutionContainer.js.map +1 -1
- package/dist/development/src/acl/PermissionFactory.cjs +29 -15
- package/dist/development/src/acl/PermissionFactory.cjs.map +1 -1
- package/dist/development/src/acl/PermissionFactory.js +29 -15
- package/dist/development/src/acl/PermissionFactory.js.map +1 -1
- package/dist/development/src/acl/PredicateFactory.cjs +33 -13
- package/dist/development/src/acl/PredicateFactory.cjs.map +1 -1
- package/dist/development/src/acl/PredicateFactory.js +33 -13
- package/dist/development/src/acl/PredicateFactory.js.map +1 -1
- package/dist/development/src/acl/PredicatesInjector.cjs +21 -18
- package/dist/development/src/acl/PredicatesInjector.cjs.map +1 -1
- package/dist/development/src/acl/PredicatesInjector.js +21 -18
- package/dist/development/src/acl/PredicatesInjector.js.map +1 -1
- package/dist/development/src/mapper/select/handlers/FieldsVisitor.cjs +2 -1
- package/dist/development/src/mapper/select/handlers/FieldsVisitor.cjs.map +1 -1
- package/dist/development/src/mapper/select/handlers/FieldsVisitor.js +2 -1
- package/dist/development/src/mapper/select/handlers/FieldsVisitor.js.map +1 -1
- package/dist/production/index.cjs +1 -0
- package/dist/production/index.cjs.map +1 -1
- package/dist/production/index.js +2 -1
- package/dist/production/src/ExecutionContainer.cjs +40 -4
- package/dist/production/src/ExecutionContainer.cjs.map +1 -1
- package/dist/production/src/ExecutionContainer.js +41 -5
- package/dist/production/src/ExecutionContainer.js.map +1 -1
- package/dist/production/src/acl/PermissionFactory.cjs +29 -15
- package/dist/production/src/acl/PermissionFactory.cjs.map +1 -1
- package/dist/production/src/acl/PermissionFactory.js +29 -15
- package/dist/production/src/acl/PermissionFactory.js.map +1 -1
- package/dist/production/src/acl/PredicateFactory.cjs +33 -13
- package/dist/production/src/acl/PredicateFactory.cjs.map +1 -1
- package/dist/production/src/acl/PredicateFactory.js +33 -13
- package/dist/production/src/acl/PredicateFactory.js.map +1 -1
- package/dist/production/src/acl/PredicatesInjector.cjs +21 -18
- package/dist/production/src/acl/PredicatesInjector.cjs.map +1 -1
- package/dist/production/src/acl/PredicatesInjector.js +21 -18
- package/dist/production/src/acl/PredicatesInjector.js.map +1 -1
- package/dist/production/src/mapper/select/handlers/FieldsVisitor.cjs +2 -1
- package/dist/production/src/mapper/select/handlers/FieldsVisitor.cjs.map +1 -1
- package/dist/production/src/mapper/select/handlers/FieldsVisitor.js +2 -1
- package/dist/production/src/mapper/select/handlers/FieldsVisitor.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/dist/types/ExecutionContainer.d.ts +20 -1
- package/dist/types/ExecutionContainer.d.ts.map +1 -1
- package/dist/types/acl/PermissionFactory.d.ts +6 -0
- package/dist/types/acl/PermissionFactory.d.ts.map +1 -1
- package/dist/types/acl/PredicateFactory.d.ts +17 -6
- package/dist/types/acl/PredicateFactory.d.ts.map +1 -1
- package/dist/types/acl/PredicatesInjector.d.ts.map +1 -1
- package/dist/types/mapper/select/handlers/FieldsVisitor.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -2
- package/src/ExecutionContainer.ts +57 -4
- package/src/acl/PermissionFactory.ts +48 -15
- package/src/acl/PredicateFactory.ts +38 -9
- package/src/acl/PredicatesInjector.ts +19 -13
- package/src/mapper/select/handlers/FieldsVisitor.ts +2 -1
- package/tests/cases/unit/permissionMerger.test.ts +162 -0
- package/tests/cases/unit/predicatesFactory.test.ts +75 -0
- package/vitest.config.ts +0 -3
|
@@ -3,15 +3,24 @@ import { mapObject } from "../utils/object.js";
|
|
|
3
3
|
import { prefixVariable } from "./VariableUtils.js";
|
|
4
4
|
class PermissionFactory {
|
|
5
5
|
create(schema, roles, prefix) {
|
|
6
|
+
return this.createInternal(schema, roles, false, prefix);
|
|
7
|
+
}
|
|
8
|
+
createContextual(schema, roles) {
|
|
9
|
+
return {
|
|
10
|
+
root: this.createInternal(schema, roles, false),
|
|
11
|
+
all: this.createInternal(schema, roles, true)
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
createInternal(schema, roles, includeThrough, prefix) {
|
|
6
15
|
let result = {};
|
|
7
16
|
for (let role of roles) {
|
|
8
17
|
const roleDefinition = schema.acl.roles[role] || { entities: {} };
|
|
9
18
|
let rolePermissions = this.prefixPredicatesWithRole(schema.model, roleDefinition.entities, prefix || role);
|
|
10
19
|
if (roleDefinition.inherits) {
|
|
11
|
-
const inheritedPermissions = this.
|
|
12
|
-
rolePermissions = this.mergePermissions(inheritedPermissions, rolePermissions);
|
|
20
|
+
const inheritedPermissions = this.createInternal(schema, roleDefinition.inherits, includeThrough, prefix || role);
|
|
21
|
+
rolePermissions = this.mergePermissions(inheritedPermissions, rolePermissions, includeThrough);
|
|
13
22
|
}
|
|
14
|
-
result = this.mergePermissions(result, rolePermissions);
|
|
23
|
+
result = this.mergePermissions(result, rolePermissions, includeThrough);
|
|
15
24
|
}
|
|
16
25
|
result = this.makePrimaryPredicatesUnionOfAllFields(schema.model, result);
|
|
17
26
|
return result;
|
|
@@ -80,18 +89,18 @@ class PermissionFactory {
|
|
|
80
89
|
};
|
|
81
90
|
});
|
|
82
91
|
}
|
|
83
|
-
mergePermissions(left, right) {
|
|
92
|
+
mergePermissions(left, right, includeThrough = false) {
|
|
84
93
|
const result = { ...left };
|
|
85
94
|
for (let entityName in right) {
|
|
86
95
|
if (result[entityName] !== void 0) {
|
|
87
|
-
result[entityName] = this.mergeEntityPermissions(result[entityName], right[entityName]);
|
|
96
|
+
result[entityName] = this.mergeEntityPermissions(result[entityName], right[entityName], includeThrough);
|
|
88
97
|
} else {
|
|
89
98
|
result[entityName] = right[entityName];
|
|
90
99
|
}
|
|
91
100
|
}
|
|
92
101
|
return result;
|
|
93
102
|
}
|
|
94
|
-
mergeEntityPermissions(left, right) {
|
|
103
|
+
mergeEntityPermissions(left, right, includeThrough = false) {
|
|
95
104
|
let predicates = {};
|
|
96
105
|
const operations = {};
|
|
97
106
|
if (left.operations.customPrimary || right.operations.customPrimary) {
|
|
@@ -103,7 +112,12 @@ class PermissionFactory {
|
|
|
103
112
|
"read",
|
|
104
113
|
"update"
|
|
105
114
|
]) {
|
|
106
|
-
const { predicates: opPredicates, permissions: fieldPermissions, noRoot: opNoRoot2 } = this.mergeOperationPermissions(
|
|
115
|
+
const { predicates: opPredicates, permissions: fieldPermissions, noRoot: opNoRoot2 } = this.mergeOperationPermissions(
|
|
116
|
+
left,
|
|
117
|
+
right,
|
|
118
|
+
operation,
|
|
119
|
+
includeThrough
|
|
120
|
+
);
|
|
107
121
|
if (opNoRoot2) {
|
|
108
122
|
noRoot.push(operation);
|
|
109
123
|
}
|
|
@@ -112,7 +126,7 @@ class PermissionFactory {
|
|
|
112
126
|
operations[operation] = fieldPermissions;
|
|
113
127
|
}
|
|
114
128
|
}
|
|
115
|
-
const { predicateDefinition, predicate, noRoot: opNoRoot } = this.mergeDeletePermissions(left, right);
|
|
129
|
+
const { predicateDefinition, predicate, noRoot: opNoRoot } = this.mergeDeletePermissions(left, right, includeThrough);
|
|
116
130
|
if (opNoRoot) {
|
|
117
131
|
noRoot.push("delete");
|
|
118
132
|
}
|
|
@@ -130,8 +144,8 @@ class PermissionFactory {
|
|
|
130
144
|
operations
|
|
131
145
|
};
|
|
132
146
|
}
|
|
133
|
-
mergeOperationPermissions(left, right, operation) {
|
|
134
|
-
const { noRoot, leftPermissions, rightPermissions } = this.resolveNoRoot(left, right, operation);
|
|
147
|
+
mergeOperationPermissions(left, right, operation, includeThrough = false) {
|
|
148
|
+
const { noRoot, leftPermissions, rightPermissions } = this.resolveNoRoot(left, right, operation, includeThrough);
|
|
135
149
|
return {
|
|
136
150
|
noRoot,
|
|
137
151
|
...this.mergeFieldPermissions(
|
|
@@ -142,8 +156,8 @@ class PermissionFactory {
|
|
|
142
156
|
)
|
|
143
157
|
};
|
|
144
158
|
}
|
|
145
|
-
mergeDeletePermissions(left, right) {
|
|
146
|
-
const { noRoot, leftPermissions, rightPermissions } = this.resolveNoRoot(left, right, "delete");
|
|
159
|
+
mergeDeletePermissions(left, right, includeThrough = false) {
|
|
160
|
+
const { noRoot, leftPermissions, rightPermissions } = this.resolveNoRoot(left, right, "delete", includeThrough);
|
|
147
161
|
return {
|
|
148
162
|
noRoot,
|
|
149
163
|
...this.mergePredicates(
|
|
@@ -154,14 +168,14 @@ class PermissionFactory {
|
|
|
154
168
|
)
|
|
155
169
|
};
|
|
156
170
|
}
|
|
157
|
-
resolveNoRoot(left, right, operation) {
|
|
171
|
+
resolveNoRoot(left, right, operation, includeThrough = false) {
|
|
158
172
|
const leftRootForbidden = left.operations.noRoot?.includes(operation) || false;
|
|
159
173
|
const rightRootForbidden = right.operations.noRoot?.includes(operation) || false;
|
|
160
174
|
const leftPermissions = left.operations[operation];
|
|
161
175
|
const rightPermissions = right.operations[operation];
|
|
162
176
|
const rootForbidden = leftRootForbidden && rightRootForbidden || leftRootForbidden && !rightPermissions || rightRootForbidden && !leftPermissions;
|
|
163
|
-
const resolvedLeftPermissions = !rootForbidden && leftRootForbidden ? void 0 : leftPermissions;
|
|
164
|
-
const resolvedRightPermissions = !rootForbidden && rightRootForbidden ? void 0 : rightPermissions;
|
|
177
|
+
const resolvedLeftPermissions = !includeThrough && !rootForbidden && leftRootForbidden ? void 0 : leftPermissions;
|
|
178
|
+
const resolvedRightPermissions = !includeThrough && !rootForbidden && rightRootForbidden ? void 0 : rightPermissions;
|
|
165
179
|
return {
|
|
166
180
|
noRoot: rootForbidden,
|
|
167
181
|
leftPermissions: resolvedLeftPermissions,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermissionFactory.js","sources":["../../../../../packages/engine-content-api/src/acl/PermissionFactory.ts"],"sourcesContent":["import { Acl, Model, Schema, Writable } from '@contember/schema'\nimport { getEntity, PredicateDefinitionProcessor } from '@contember/schema-utils'\nimport { mapObject } from '../utils'\nimport { prefixVariable } from './VariableUtils'\n\nexport interface Identity {\n\tprojectRoles: readonly string[]\n}\n\nexport class PermissionFactory {\n\tpublic create(schema: Schema, roles: readonly string[], prefix?: string): Acl.Permissions {\n\t\tlet result: Acl.Permissions = {}\n\t\tfor (let role of roles) {\n\t\t\tconst roleDefinition = schema.acl.roles[role] || { entities: {} }\n\t\t\tlet rolePermissions: Acl.Permissions = this.prefixPredicatesWithRole(schema.model, roleDefinition.entities, prefix || role)\n\t\t\tif (roleDefinition.inherits) {\n\t\t\t\tconst inheritedPermissions = this.create(schema, roleDefinition.inherits, prefix || role)\n\t\t\t\trolePermissions = this.mergePermissions(inheritedPermissions, rolePermissions)\n\t\t\t}\n\t\t\tresult = this.mergePermissions(result, rolePermissions)\n\t\t}\n\t\tresult = this.makePrimaryPredicatesUnionOfAllFields(schema.model, result)\n\n\t\treturn result\n\t}\n\n\tprivate prefixPredicatesWithRole(model: Model.Schema, permissions: Acl.Permissions, role: string): Acl.Permissions {\n\t\treturn mapObject(permissions, ({ operations, predicates }, entityName) => ({\n\t\t\toperations,\n\t\t\tpredicates: mapObject(predicates, predicate => {\n\t\t\t\tconst predicateDefinitionProcessor = new PredicateDefinitionProcessor(model)\n\t\t\t\treturn predicateDefinitionProcessor.process(getEntity(model, entityName), predicate, {\n\t\t\t\t\thandleColumn: ({ value }) => {\n\t\t\t\t\t\tif (typeof value === 'string') {\n\t\t\t\t\t\t\treturn prefixVariable(role, value)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn value\n\t\t\t\t\t},\n\t\t\t\t\thandleRelation: ({ value }) => {\n\t\t\t\t\t\tif (typeof value === 'string') {\n\t\t\t\t\t\t\treturn prefixVariable(role, value)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn value\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}),\n\t\t}))\n\t}\n\n\tprivate makePrimaryPredicatesUnionOfAllFields(model: Model.Schema, permissions: Acl.Permissions): Acl.Permissions {\n\t\treturn mapObject(permissions, (permission, entityName): Acl.EntityPermissions => {\n\t\t\tconst entity = getEntity(model, entityName)\n\t\t\tconst entityPredicates: Writable<Acl.PredicateMap> = { ...permission.predicates }\n\t\t\tconst entityOperations: Writable<Acl.EntityOperations> = { ...permission.operations }\n\n\t\t\tconst operationNames = ['read', 'create', 'update'] as const\n\t\t\tfor (let operation of operationNames) {\n\t\t\t\tif (!entityOperations[operation]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst fieldPermissions: Writable<Acl.FieldPermissions> = { ...entityOperations[operation] }\n\t\t\t\tentityOperations[operation] = fieldPermissions\n\n\t\t\t\tif (Object.values(fieldPermissions).some(it => it === true)) {\n\t\t\t\t\tfieldPermissions[entity.primary] = true\n\t\t\t\t}\n\t\t\t\tif (fieldPermissions[entity.primary] === true) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst predicateReferences: string[] = Object.entries(fieldPermissions)\n\t\t\t\t\t.filter(([key]) => key !== entity.primary)\n\t\t\t\t\t.map(([key, value]) => value)\n\t\t\t\t\t.filter(value => value !== false)\n\t\t\t\t\t.filter((value, index, array): value is string => array.indexOf(value) === index)\n\n\t\t\t\tlet idPermissions: Acl.Predicate = fieldPermissions[entity.primary] || false\n\n\t\t\t\tfor (let predicateReference of predicateReferences) {\n\t\t\t\t\tconst { predicateDefinition, predicate } = this.mergePredicates(\n\t\t\t\t\t\tentityPredicates,\n\t\t\t\t\t\tidPermissions,\n\t\t\t\t\t\tentityPredicates,\n\t\t\t\t\t\tpredicateReference,\n\t\t\t\t\t)\n\t\t\t\t\tif (typeof predicate !== 'string' || predicateDefinition === undefined) {\n\t\t\t\t\t\tthrow new Error('should not happen')\n\t\t\t\t\t}\n\t\t\t\t\tidPermissions = predicate\n\t\t\t\t\tentityPredicates[predicate] = predicateDefinition\n\t\t\t\t}\n\t\t\t\tfieldPermissions[entity.primary] = idPermissions\n\t\t\t\tentityPredicates[idPermissions as Acl.PredicateReference] = entityPredicates[idPermissions as Acl.PredicateReference]\n\t\t\t}\n\t\t\treturn {\n\t\t\t\toperations: entityOperations,\n\t\t\t\tpredicates: entityPredicates,\n\t\t\t}\n\t\t})\n\t}\n\n\tprivate mergePermissions(left: Acl.Permissions, right: Acl.Permissions): Acl.Permissions {\n\t\tconst result = { ...left }\n\t\tfor (let entityName in right) {\n\t\t\tif (result[entityName] !== undefined) {\n\t\t\t\tresult[entityName] = this.mergeEntityPermissions(result[entityName], right[entityName])\n\t\t\t} else {\n\t\t\t\tresult[entityName] = right[entityName]\n\t\t\t}\n\t\t}\n\t\treturn result\n\t}\n\n\tprivate mergeEntityPermissions(left: Acl.EntityPermissions, right: Acl.EntityPermissions): Acl.EntityPermissions {\n\t\tlet predicates: Writable<Acl.PredicateMap> = {}\n\t\tconst operations: Writable<Acl.EntityOperations> = {}\n\t\tif (left.operations.customPrimary || right.operations.customPrimary) {\n\t\t\toperations.customPrimary = true\n\t\t}\n\t\tconst noRoot: `${Acl.Operation}`[] = []\n\n\t\tfor (\n\t\t\tlet operation of [\n\t\t\t\t'create',\n\t\t\t\t'read',\n\t\t\t\t'update',\n\t\t\t] as const\n\t\t) {\n\t\t\tconst { predicates: opPredicates, permissions: fieldPermissions, noRoot: opNoRoot } = this.mergeOperationPermissions(left, right, operation)\n\n\t\t\tif (opNoRoot) {\n\t\t\t\tnoRoot.push(operation)\n\t\t\t}\n\n\t\t\tpredicates = { ...predicates, ...opPredicates }\n\t\t\tif (Object.keys(fieldPermissions).length > 0) {\n\t\t\t\toperations[operation] = fieldPermissions\n\t\t\t}\n\t\t}\n\n\t\tconst { predicateDefinition, predicate, noRoot: opNoRoot } = this.mergeDeletePermissions(left, right)\n\t\tif (opNoRoot) {\n\t\t\tnoRoot.push('delete')\n\t\t}\n\t\tif (predicate === true) {\n\t\t\toperations.delete = true\n\t\t} else if (predicateDefinition !== undefined && typeof predicate === 'string') {\n\t\t\tpredicates[predicate] = predicateDefinition\n\t\t\toperations.delete = predicate\n\t\t}\n\n\t\tif (noRoot.length > 0) {\n\t\t\toperations.noRoot = noRoot\n\t\t}\n\n\t\treturn {\n\t\t\tpredicates: predicates,\n\t\t\toperations: operations,\n\t\t}\n\t}\n\n\tprivate mergeOperationPermissions(left: Acl.EntityPermissions, right: Acl.EntityPermissions, operation: 'create' | 'read' | 'update'): {\n\t\tnoRoot: boolean\n\t\tpredicates: Acl.PredicateMap\n\t\tpermissions: Acl.FieldPermissions\n\t} {\n\t\tconst { noRoot, leftPermissions, rightPermissions } = this.resolveNoRoot(left, right, operation)\n\t\treturn {\n\t\t\tnoRoot,\n\t\t\t...this.mergeFieldPermissions(\n\t\t\t\tleft.predicates,\n\t\t\t\tleftPermissions,\n\t\t\t\tright.predicates,\n\t\t\t\trightPermissions,\n\t\t\t),\n\t\t}\n\t}\n\n\tprivate mergeDeletePermissions(left: Acl.EntityPermissions, right: Acl.EntityPermissions): {\n\t\tnoRoot: boolean\n\t\tpredicate: Acl.PredicateReference | boolean\n\t\tpredicateDefinition: Acl.PredicateDefinition | undefined\n\t} {\n\t\tconst { noRoot, leftPermissions, rightPermissions } = this.resolveNoRoot(left, right, 'delete')\n\n\t\treturn {\n\t\t\tnoRoot,\n\t\t\t...this.mergePredicates(\n\t\t\t\tleft.predicates,\n\t\t\t\tleftPermissions,\n\t\t\t\tright.predicates,\n\t\t\t\trightPermissions,\n\t\t\t),\n\t\t}\n\t}\n\n\tprivate resolveNoRoot<const Op extends `${Acl.Operation}`>(left: Acl.EntityPermissions, right: Acl.EntityPermissions, operation: Op): {\n\t\tnoRoot: boolean\n\t\tleftPermissions: Acl.EntityPermissions['operations'][Op] | undefined\n\t\trightPermissions: Acl.EntityPermissions['operations'][Op] | undefined\n\t} {\n\t\tconst leftRootForbidden = left.operations.noRoot?.includes(operation) || false\n\t\tconst rightRootForbidden = right.operations.noRoot?.includes(operation) || false\n\t\tconst leftPermissions = left.operations[operation]\n\t\tconst rightPermissions = right.operations[operation]\n\n\t\tconst rootForbidden = (leftRootForbidden && rightRootForbidden)\n\t\t\t|| (leftRootForbidden && !rightPermissions)\n\t\t\t|| (rightRootForbidden && !leftPermissions)\n\n\t\tconst resolvedLeftPermissions = !rootForbidden && leftRootForbidden ? undefined : leftPermissions\n\t\tconst resolvedRightPermissions = !rootForbidden && rightRootForbidden ? undefined : rightPermissions\n\n\t\treturn {\n\t\t\tnoRoot: rootForbidden,\n\t\t\tleftPermissions: resolvedLeftPermissions,\n\t\t\trightPermissions: resolvedRightPermissions,\n\t\t}\n\t}\n\n\tprivate mergeFieldPermissions(\n\t\tleftPredicates: Acl.PredicateMap,\n\t\tleftFieldPermissions: Acl.FieldPermissions | undefined = {},\n\t\trightPredicates: Acl.PredicateMap,\n\t\trightFieldPermissions: Acl.FieldPermissions | undefined = {},\n\t): { predicates: Acl.PredicateMap; permissions: Acl.FieldPermissions } {\n\t\tconst permissions: Writable<Acl.FieldPermissions> = {}\n\t\tconst predicates: Writable<Acl.PredicateMap> = {}\n\n\t\tfor (let field in { ...leftFieldPermissions, ...rightFieldPermissions }) {\n\t\t\tconst { predicateDefinition, predicate } = this.mergePredicates(\n\t\t\t\tleftPredicates,\n\t\t\t\tleftFieldPermissions[field] || false,\n\t\t\t\trightPredicates,\n\t\t\t\trightFieldPermissions[field] || false,\n\t\t\t)\n\t\t\tif (predicate === true) {\n\t\t\t\tpermissions[field] = true\n\t\t\t} else if (predicateDefinition !== undefined && typeof predicate === 'string') {\n\t\t\t\tpermissions[field] = predicate\n\t\t\t\tpredicates[predicate] = predicateDefinition\n\t\t\t}\n\t\t}\n\n\t\treturn { predicates, permissions }\n\t}\n\n\tprivate mergePredicates(\n\t\tleftPredicates: Acl.PredicateMap,\n\t\tleftReference: Acl.Predicate | undefined = false,\n\t\trightPredicates: Acl.PredicateMap,\n\t\trightReference: Acl.Predicate | undefined = false,\n\t): {\n\t\tpredicate: Acl.PredicateReference | boolean\n\t\tpredicateDefinition: Acl.PredicateDefinition | undefined\n\t} {\n\t\tif (leftReference === true || rightReference === true) {\n\t\t\treturn {\n\t\t\t\tpredicate: true,\n\t\t\t\tpredicateDefinition: undefined,\n\t\t\t}\n\t\t}\n\n\t\tif (leftReference !== false && rightReference !== false) {\n\t\t\tconst leftPredicate: Acl.PredicateDefinition = leftPredicates[leftReference]\n\t\t\tconst rightPredicate: Acl.PredicateDefinition = rightPredicates[rightReference]\n\t\t\tif (leftPredicate === rightPredicate) {\n\t\t\t\treturn {\n\t\t\t\t\tpredicate: leftReference,\n\t\t\t\t\tpredicateDefinition: leftPredicate,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet predicateName = '__merge__' + leftReference + '__' + rightReference\n\t\t\twhile (leftPredicates[predicateName]) {\n\t\t\t\tpredicateName += '_'\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tpredicate: predicateName,\n\t\t\t\tpredicateDefinition: {\n\t\t\t\t\tor: [leftPredicate, rightPredicate],\n\t\t\t\t} as Acl.PredicateDefinition,\n\t\t\t}\n\t\t} else if (leftReference !== false) {\n\t\t\treturn {\n\t\t\t\tpredicate: leftReference,\n\t\t\t\tpredicateDefinition: leftPredicates[leftReference],\n\t\t\t}\n\t\t} else if (rightReference !== false) {\n\t\t\tlet predicateName = rightReference\n\t\t\tif (rightPredicates !== leftPredicates) {\n\t\t\t\twhile (leftPredicates[predicateName]) {\n\t\t\t\t\tpredicateName += '_'\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tpredicate: predicateName,\n\t\t\t\tpredicateDefinition: rightPredicates[rightReference],\n\t\t\t}\n\t\t} else {\n\t\t\treturn {\n\t\t\t\tpredicate: false,\n\t\t\t\tpredicateDefinition: undefined,\n\t\t\t}\n\t\t}\n\t}\n}\n"],"names":["opNoRoot"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PermissionFactory.js","sources":["../../../../../packages/engine-content-api/src/acl/PermissionFactory.ts"],"sourcesContent":["import { Acl, Model, Schema, Writable } from '@contember/schema'\nimport { getEntity, PredicateDefinitionProcessor } from '@contember/schema-utils'\nimport { mapObject } from '../utils'\nimport { prefixVariable } from './VariableUtils'\n\nexport interface Identity {\n\tprojectRoles: readonly string[]\n}\n\nexport interface ContextualPermissions {\n\troot: Acl.Permissions\n\tall: Acl.Permissions\n}\n\nexport class PermissionFactory {\n\tpublic create(schema: Schema, roles: readonly string[], prefix?: string): Acl.Permissions {\n\t\treturn this.createInternal(schema, roles, false, prefix)\n\t}\n\n\tpublic createContextual(schema: Schema, roles: readonly string[]): ContextualPermissions {\n\t\treturn {\n\t\t\troot: this.createInternal(schema, roles, false),\n\t\t\tall: this.createInternal(schema, roles, true),\n\t\t}\n\t}\n\n\tprivate createInternal(schema: Schema, roles: readonly string[], includeThrough: boolean, prefix?: string): Acl.Permissions {\n\t\tlet result: Acl.Permissions = {}\n\t\tfor (let role of roles) {\n\t\t\tconst roleDefinition = schema.acl.roles[role] || { entities: {} }\n\t\t\tlet rolePermissions: Acl.Permissions = this.prefixPredicatesWithRole(schema.model, roleDefinition.entities, prefix || role)\n\t\t\tif (roleDefinition.inherits) {\n\t\t\t\tconst inheritedPermissions = this.createInternal(schema, roleDefinition.inherits, includeThrough, prefix || role)\n\t\t\t\trolePermissions = this.mergePermissions(inheritedPermissions, rolePermissions, includeThrough)\n\t\t\t}\n\t\t\tresult = this.mergePermissions(result, rolePermissions, includeThrough)\n\t\t}\n\t\tresult = this.makePrimaryPredicatesUnionOfAllFields(schema.model, result)\n\n\t\treturn result\n\t}\n\n\tprivate prefixPredicatesWithRole(model: Model.Schema, permissions: Acl.Permissions, role: string): Acl.Permissions {\n\t\treturn mapObject(permissions, ({ operations, predicates }, entityName) => ({\n\t\t\toperations,\n\t\t\tpredicates: mapObject(predicates, predicate => {\n\t\t\t\tconst predicateDefinitionProcessor = new PredicateDefinitionProcessor(model)\n\t\t\t\treturn predicateDefinitionProcessor.process(getEntity(model, entityName), predicate, {\n\t\t\t\t\thandleColumn: ({ value }) => {\n\t\t\t\t\t\tif (typeof value === 'string') {\n\t\t\t\t\t\t\treturn prefixVariable(role, value)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn value\n\t\t\t\t\t},\n\t\t\t\t\thandleRelation: ({ value }) => {\n\t\t\t\t\t\tif (typeof value === 'string') {\n\t\t\t\t\t\t\treturn prefixVariable(role, value)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn value\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}),\n\t\t}))\n\t}\n\n\tprivate makePrimaryPredicatesUnionOfAllFields(model: Model.Schema, permissions: Acl.Permissions): Acl.Permissions {\n\t\treturn mapObject(permissions, (permission, entityName): Acl.EntityPermissions => {\n\t\t\tconst entity = getEntity(model, entityName)\n\t\t\tconst entityPredicates: Writable<Acl.PredicateMap> = { ...permission.predicates }\n\t\t\tconst entityOperations: Writable<Acl.EntityOperations> = { ...permission.operations }\n\n\t\t\tconst operationNames = ['read', 'create', 'update'] as const\n\t\t\tfor (let operation of operationNames) {\n\t\t\t\tif (!entityOperations[operation]) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst fieldPermissions: Writable<Acl.FieldPermissions> = { ...entityOperations[operation] }\n\t\t\t\tentityOperations[operation] = fieldPermissions\n\n\t\t\t\tif (Object.values(fieldPermissions).some(it => it === true)) {\n\t\t\t\t\tfieldPermissions[entity.primary] = true\n\t\t\t\t}\n\t\t\t\tif (fieldPermissions[entity.primary] === true) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tconst predicateReferences: string[] = Object.entries(fieldPermissions)\n\t\t\t\t\t.filter(([key]) => key !== entity.primary)\n\t\t\t\t\t.map(([key, value]) => value)\n\t\t\t\t\t.filter(value => value !== false)\n\t\t\t\t\t.filter((value, index, array): value is string => array.indexOf(value) === index)\n\n\t\t\t\tlet idPermissions: Acl.Predicate = fieldPermissions[entity.primary] || false\n\n\t\t\t\tfor (let predicateReference of predicateReferences) {\n\t\t\t\t\tconst { predicateDefinition, predicate } = this.mergePredicates(\n\t\t\t\t\t\tentityPredicates,\n\t\t\t\t\t\tidPermissions,\n\t\t\t\t\t\tentityPredicates,\n\t\t\t\t\t\tpredicateReference,\n\t\t\t\t\t)\n\t\t\t\t\tif (typeof predicate !== 'string' || predicateDefinition === undefined) {\n\t\t\t\t\t\tthrow new Error('should not happen')\n\t\t\t\t\t}\n\t\t\t\t\tidPermissions = predicate\n\t\t\t\t\tentityPredicates[predicate] = predicateDefinition\n\t\t\t\t}\n\t\t\t\tfieldPermissions[entity.primary] = idPermissions\n\t\t\t\tentityPredicates[idPermissions as Acl.PredicateReference] = entityPredicates[idPermissions as Acl.PredicateReference]\n\t\t\t}\n\t\t\treturn {\n\t\t\t\toperations: entityOperations,\n\t\t\t\tpredicates: entityPredicates,\n\t\t\t}\n\t\t})\n\t}\n\n\tprivate mergePermissions(left: Acl.Permissions, right: Acl.Permissions, includeThrough = false): Acl.Permissions {\n\t\tconst result = { ...left }\n\t\tfor (let entityName in right) {\n\t\t\tif (result[entityName] !== undefined) {\n\t\t\t\tresult[entityName] = this.mergeEntityPermissions(result[entityName], right[entityName], includeThrough)\n\t\t\t} else {\n\t\t\t\tresult[entityName] = right[entityName]\n\t\t\t}\n\t\t}\n\t\treturn result\n\t}\n\n\tprivate mergeEntityPermissions(left: Acl.EntityPermissions, right: Acl.EntityPermissions, includeThrough = false): Acl.EntityPermissions {\n\t\tlet predicates: Writable<Acl.PredicateMap> = {}\n\t\tconst operations: Writable<Acl.EntityOperations> = {}\n\t\tif (left.operations.customPrimary || right.operations.customPrimary) {\n\t\t\toperations.customPrimary = true\n\t\t}\n\t\tconst noRoot: `${Acl.Operation}`[] = []\n\n\t\tfor (\n\t\t\tlet operation of [\n\t\t\t\t'create',\n\t\t\t\t'read',\n\t\t\t\t'update',\n\t\t\t] as const\n\t\t) {\n\t\t\tconst { predicates: opPredicates, permissions: fieldPermissions, noRoot: opNoRoot } = this.mergeOperationPermissions(\n\t\t\t\tleft,\n\t\t\t\tright,\n\t\t\t\toperation,\n\t\t\t\tincludeThrough,\n\t\t\t)\n\n\t\t\tif (opNoRoot) {\n\t\t\t\tnoRoot.push(operation)\n\t\t\t}\n\n\t\t\tpredicates = { ...predicates, ...opPredicates }\n\t\t\tif (Object.keys(fieldPermissions).length > 0) {\n\t\t\t\toperations[operation] = fieldPermissions\n\t\t\t}\n\t\t}\n\n\t\tconst { predicateDefinition, predicate, noRoot: opNoRoot } = this.mergeDeletePermissions(left, right, includeThrough)\n\t\tif (opNoRoot) {\n\t\t\tnoRoot.push('delete')\n\t\t}\n\t\tif (predicate === true) {\n\t\t\toperations.delete = true\n\t\t} else if (predicateDefinition !== undefined && typeof predicate === 'string') {\n\t\t\tpredicates[predicate] = predicateDefinition\n\t\t\toperations.delete = predicate\n\t\t}\n\n\t\tif (noRoot.length > 0) {\n\t\t\toperations.noRoot = noRoot\n\t\t}\n\n\t\treturn {\n\t\t\tpredicates: predicates,\n\t\t\toperations: operations,\n\t\t}\n\t}\n\n\tprivate mergeOperationPermissions(\n\t\tleft: Acl.EntityPermissions,\n\t\tright: Acl.EntityPermissions,\n\t\toperation: 'create' | 'read' | 'update',\n\t\tincludeThrough = false,\n\t): {\n\t\tnoRoot: boolean\n\t\tpredicates: Acl.PredicateMap\n\t\tpermissions: Acl.FieldPermissions\n\t} {\n\t\tconst { noRoot, leftPermissions, rightPermissions } = this.resolveNoRoot(left, right, operation, includeThrough)\n\t\treturn {\n\t\t\tnoRoot,\n\t\t\t...this.mergeFieldPermissions(\n\t\t\t\tleft.predicates,\n\t\t\t\tleftPermissions,\n\t\t\t\tright.predicates,\n\t\t\t\trightPermissions,\n\t\t\t),\n\t\t}\n\t}\n\n\tprivate mergeDeletePermissions(left: Acl.EntityPermissions, right: Acl.EntityPermissions, includeThrough = false): {\n\t\tnoRoot: boolean\n\t\tpredicate: Acl.PredicateReference | boolean\n\t\tpredicateDefinition: Acl.PredicateDefinition | undefined\n\t} {\n\t\tconst { noRoot, leftPermissions, rightPermissions } = this.resolveNoRoot(left, right, 'delete', includeThrough)\n\n\t\treturn {\n\t\t\tnoRoot,\n\t\t\t...this.mergePredicates(\n\t\t\t\tleft.predicates,\n\t\t\t\tleftPermissions,\n\t\t\t\tright.predicates,\n\t\t\t\trightPermissions,\n\t\t\t),\n\t\t}\n\t}\n\n\tprivate resolveNoRoot<const Op extends `${Acl.Operation}`>(\n\t\tleft: Acl.EntityPermissions,\n\t\tright: Acl.EntityPermissions,\n\t\toperation: Op,\n\t\tincludeThrough = false,\n\t): {\n\t\tnoRoot: boolean\n\t\tleftPermissions: Acl.EntityPermissions['operations'][Op] | undefined\n\t\trightPermissions: Acl.EntityPermissions['operations'][Op] | undefined\n\t} {\n\t\tconst leftRootForbidden = left.operations.noRoot?.includes(operation) || false\n\t\tconst rightRootForbidden = right.operations.noRoot?.includes(operation) || false\n\t\tconst leftPermissions = left.operations[operation]\n\t\tconst rightPermissions = right.operations[operation]\n\n\t\tconst rootForbidden = (leftRootForbidden && rightRootForbidden)\n\t\t\t|| (leftRootForbidden && !rightPermissions)\n\t\t\t|| (rightRootForbidden && !leftPermissions)\n\n\t\t// When includeThrough is true, keep all permissions regardless of noRoot\n\t\t// (used for \"all\" permissions that apply when accessing through relations)\n\t\tconst resolvedLeftPermissions = !includeThrough && !rootForbidden && leftRootForbidden ? undefined : leftPermissions\n\t\tconst resolvedRightPermissions = !includeThrough && !rootForbidden && rightRootForbidden ? undefined : rightPermissions\n\n\t\treturn {\n\t\t\tnoRoot: rootForbidden,\n\t\t\tleftPermissions: resolvedLeftPermissions,\n\t\t\trightPermissions: resolvedRightPermissions,\n\t\t}\n\t}\n\n\tprivate mergeFieldPermissions(\n\t\tleftPredicates: Acl.PredicateMap,\n\t\tleftFieldPermissions: Acl.FieldPermissions | undefined = {},\n\t\trightPredicates: Acl.PredicateMap,\n\t\trightFieldPermissions: Acl.FieldPermissions | undefined = {},\n\t): { predicates: Acl.PredicateMap; permissions: Acl.FieldPermissions } {\n\t\tconst permissions: Writable<Acl.FieldPermissions> = {}\n\t\tconst predicates: Writable<Acl.PredicateMap> = {}\n\n\t\tfor (let field in { ...leftFieldPermissions, ...rightFieldPermissions }) {\n\t\t\tconst { predicateDefinition, predicate } = this.mergePredicates(\n\t\t\t\tleftPredicates,\n\t\t\t\tleftFieldPermissions[field] || false,\n\t\t\t\trightPredicates,\n\t\t\t\trightFieldPermissions[field] || false,\n\t\t\t)\n\t\t\tif (predicate === true) {\n\t\t\t\tpermissions[field] = true\n\t\t\t} else if (predicateDefinition !== undefined && typeof predicate === 'string') {\n\t\t\t\tpermissions[field] = predicate\n\t\t\t\tpredicates[predicate] = predicateDefinition\n\t\t\t}\n\t\t}\n\n\t\treturn { predicates, permissions }\n\t}\n\n\tprivate mergePredicates(\n\t\tleftPredicates: Acl.PredicateMap,\n\t\tleftReference: Acl.Predicate | undefined = false,\n\t\trightPredicates: Acl.PredicateMap,\n\t\trightReference: Acl.Predicate | undefined = false,\n\t): {\n\t\tpredicate: Acl.PredicateReference | boolean\n\t\tpredicateDefinition: Acl.PredicateDefinition | undefined\n\t} {\n\t\tif (leftReference === true || rightReference === true) {\n\t\t\treturn {\n\t\t\t\tpredicate: true,\n\t\t\t\tpredicateDefinition: undefined,\n\t\t\t}\n\t\t}\n\n\t\tif (leftReference !== false && rightReference !== false) {\n\t\t\tconst leftPredicate: Acl.PredicateDefinition = leftPredicates[leftReference]\n\t\t\tconst rightPredicate: Acl.PredicateDefinition = rightPredicates[rightReference]\n\t\t\tif (leftPredicate === rightPredicate) {\n\t\t\t\treturn {\n\t\t\t\t\tpredicate: leftReference,\n\t\t\t\t\tpredicateDefinition: leftPredicate,\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet predicateName = '__merge__' + leftReference + '__' + rightReference\n\t\t\twhile (leftPredicates[predicateName]) {\n\t\t\t\tpredicateName += '_'\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tpredicate: predicateName,\n\t\t\t\tpredicateDefinition: {\n\t\t\t\t\tor: [leftPredicate, rightPredicate],\n\t\t\t\t} as Acl.PredicateDefinition,\n\t\t\t}\n\t\t} else if (leftReference !== false) {\n\t\t\treturn {\n\t\t\t\tpredicate: leftReference,\n\t\t\t\tpredicateDefinition: leftPredicates[leftReference],\n\t\t\t}\n\t\t} else if (rightReference !== false) {\n\t\t\tlet predicateName = rightReference\n\t\t\tif (rightPredicates !== leftPredicates) {\n\t\t\t\twhile (leftPredicates[predicateName]) {\n\t\t\t\t\tpredicateName += '_'\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tpredicate: predicateName,\n\t\t\t\tpredicateDefinition: rightPredicates[rightReference],\n\t\t\t}\n\t\t} else {\n\t\t\treturn {\n\t\t\t\tpredicate: false,\n\t\t\t\tpredicateDefinition: undefined,\n\t\t\t}\n\t\t}\n\t}\n}\n"],"names":["opNoRoot"],"mappings":";;;AAcO,MAAM,kBAAkB;AAAA,EACvB,OAAO,QAAgB,OAA0B,QAAkC;AACzF,WAAO,KAAK,eAAe,QAAQ,OAAO,OAAO,MAAM;AAAA,EACxD;AAAA,EAEO,iBAAiB,QAAgB,OAAiD;AACxF,WAAO;AAAA,MACN,MAAM,KAAK,eAAe,QAAQ,OAAO,KAAK;AAAA,MAC9C,KAAK,KAAK,eAAe,QAAQ,OAAO,IAAI;AAAA,IAAA;AAAA,EAE9C;AAAA,EAEQ,eAAe,QAAgB,OAA0B,gBAAyB,QAAkC;AAC3H,QAAI,SAA0B,CAAA;AAC9B,aAAS,QAAQ,OAAO;AACvB,YAAM,iBAAiB,OAAO,IAAI,MAAM,IAAI,KAAK,EAAE,UAAU,GAAC;AAC9D,UAAI,kBAAmC,KAAK,yBAAyB,OAAO,OAAO,eAAe,UAAU,UAAU,IAAI;AAC1H,UAAI,eAAe,UAAU;AAC5B,cAAM,uBAAuB,KAAK,eAAe,QAAQ,eAAe,UAAU,gBAAgB,UAAU,IAAI;AAChH,0BAAkB,KAAK,iBAAiB,sBAAsB,iBAAiB,cAAc;AAAA,MAC9F;AACA,eAAS,KAAK,iBAAiB,QAAQ,iBAAiB,cAAc;AAAA,IACvE;AACA,aAAS,KAAK,sCAAsC,OAAO,OAAO,MAAM;AAExE,WAAO;AAAA,EACR;AAAA,EAEQ,yBAAyB,OAAqB,aAA8B,MAA+B;AAClH,WAAO,UAAU,aAAa,CAAC,EAAE,YAAY,WAAA,GAAc,gBAAgB;AAAA,MAC1E;AAAA,MACA,YAAY,UAAU,YAAY,CAAA,cAAa;AAC9C,cAAM,+BAA+B,IAAI,6BAA6B,KAAK;AAC3E,eAAO,6BAA6B,QAAQ,UAAU,OAAO,UAAU,GAAG,WAAW;AAAA,UACpF,cAAc,CAAC,EAAE,YAAY;AAC5B,gBAAI,OAAO,UAAU,UAAU;AAC9B,qBAAO,eAAe,MAAM,KAAK;AAAA,YAClC;AACA,mBAAO;AAAA,UACR;AAAA,UACA,gBAAgB,CAAC,EAAE,YAAY;AAC9B,gBAAI,OAAO,UAAU,UAAU;AAC9B,qBAAO,eAAe,MAAM,KAAK;AAAA,YAClC;AACA,mBAAO;AAAA,UACR;AAAA,QAAA,CACA;AAAA,MACF,CAAC;AAAA,IAAA,EACA;AAAA,EACH;AAAA,EAEQ,sCAAsC,OAAqB,aAA+C;AACjH,WAAO,UAAU,aAAa,CAAC,YAAY,eAAsC;AAChF,YAAM,SAAS,UAAU,OAAO,UAAU;AAC1C,YAAM,mBAA+C,EAAE,GAAG,WAAW,WAAA;AACrE,YAAM,mBAAmD,EAAE,GAAG,WAAW,WAAA;AAEzE,YAAM,iBAAiB,CAAC,QAAQ,UAAU,QAAQ;AAClD,eAAS,aAAa,gBAAgB;AACrC,YAAI,CAAC,iBAAiB,SAAS,GAAG;AACjC;AAAA,QACD;AACA,cAAM,mBAAmD,EAAE,GAAG,iBAAiB,SAAS,EAAA;AACxF,yBAAiB,SAAS,IAAI;AAE9B,YAAI,OAAO,OAAO,gBAAgB,EAAE,KAAK,CAAA,OAAM,OAAO,IAAI,GAAG;AAC5D,2BAAiB,OAAO,OAAO,IAAI;AAAA,QACpC;AACA,YAAI,iBAAiB,OAAO,OAAO,MAAM,MAAM;AAC9C;AAAA,QACD;AACA,cAAM,sBAAgC,OAAO,QAAQ,gBAAgB,EACnE,OAAO,CAAC,CAAC,GAAG,MAAM,QAAQ,OAAO,OAAO,EACxC,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,KAAK,EAC3B,OAAO,CAAA,UAAS,UAAU,KAAK,EAC/B,OAAO,CAAC,OAAO,OAAO,UAA2B,MAAM,QAAQ,KAAK,MAAM,KAAK;AAEjF,YAAI,gBAA+B,iBAAiB,OAAO,OAAO,KAAK;AAEvE,iBAAS,sBAAsB,qBAAqB;AACnD,gBAAM,EAAE,qBAAqB,UAAA,IAAc,KAAK;AAAA,YAC/C;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UAAA;AAED,cAAI,OAAO,cAAc,YAAY,wBAAwB,QAAW;AACvE,kBAAM,IAAI,MAAM,mBAAmB;AAAA,UACpC;AACA,0BAAgB;AAChB,2BAAiB,SAAS,IAAI;AAAA,QAC/B;AACA,yBAAiB,OAAO,OAAO,IAAI;AACnC,yBAAiB,aAAuC,IAAI,iBAAiB,aAAuC;AAAA,MACrH;AACA,aAAO;AAAA,QACN,YAAY;AAAA,QACZ,YAAY;AAAA,MAAA;AAAA,IAEd,CAAC;AAAA,EACF;AAAA,EAEQ,iBAAiB,MAAuB,OAAwB,iBAAiB,OAAwB;AAChH,UAAM,SAAS,EAAE,GAAG,KAAA;AACpB,aAAS,cAAc,OAAO;AAC7B,UAAI,OAAO,UAAU,MAAM,QAAW;AACrC,eAAO,UAAU,IAAI,KAAK,uBAAuB,OAAO,UAAU,GAAG,MAAM,UAAU,GAAG,cAAc;AAAA,MACvG,OAAO;AACN,eAAO,UAAU,IAAI,MAAM,UAAU;AAAA,MACtC;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA,EAEQ,uBAAuB,MAA6B,OAA8B,iBAAiB,OAA8B;AACxI,QAAI,aAAyC,CAAA;AAC7C,UAAM,aAA6C,CAAA;AACnD,QAAI,KAAK,WAAW,iBAAiB,MAAM,WAAW,eAAe;AACpE,iBAAW,gBAAgB;AAAA,IAC5B;AACA,UAAM,SAA+B,CAAA;AAErC,aACK,aAAa;AAAA,MAChB;AAAA,MACA;AAAA,MACA;AAAA,IAAA,GAEA;AACD,YAAM,EAAE,YAAY,cAAc,aAAa,kBAAkB,QAAQA,cAAa,KAAK;AAAA,QAC1F;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAGD,UAAIA,WAAU;AACb,eAAO,KAAK,SAAS;AAAA,MACtB;AAEA,mBAAa,EAAE,GAAG,YAAY,GAAG,aAAA;AACjC,UAAI,OAAO,KAAK,gBAAgB,EAAE,SAAS,GAAG;AAC7C,mBAAW,SAAS,IAAI;AAAA,MACzB;AAAA,IACD;AAEA,UAAM,EAAE,qBAAqB,WAAW,QAAQ,SAAA,IAAa,KAAK,uBAAuB,MAAM,OAAO,cAAc;AACpH,QAAI,UAAU;AACb,aAAO,KAAK,QAAQ;AAAA,IACrB;AACA,QAAI,cAAc,MAAM;AACvB,iBAAW,SAAS;AAAA,IACrB,WAAW,wBAAwB,UAAa,OAAO,cAAc,UAAU;AAC9E,iBAAW,SAAS,IAAI;AACxB,iBAAW,SAAS;AAAA,IACrB;AAEA,QAAI,OAAO,SAAS,GAAG;AACtB,iBAAW,SAAS;AAAA,IACrB;AAEA,WAAO;AAAA,MACN;AAAA,MACA;AAAA,IAAA;AAAA,EAEF;AAAA,EAEQ,0BACP,MACA,OACA,WACA,iBAAiB,OAKhB;AACD,UAAM,EAAE,QAAQ,iBAAiB,qBAAqB,KAAK,cAAc,MAAM,OAAO,WAAW,cAAc;AAC/G,WAAO;AAAA,MACN;AAAA,MACA,GAAG,KAAK;AAAA,QACP,KAAK;AAAA,QACL;AAAA,QACA,MAAM;AAAA,QACN;AAAA,MAAA;AAAA,IACD;AAAA,EAEF;AAAA,EAEQ,uBAAuB,MAA6B,OAA8B,iBAAiB,OAIzG;AACD,UAAM,EAAE,QAAQ,iBAAiB,qBAAqB,KAAK,cAAc,MAAM,OAAO,UAAU,cAAc;AAE9G,WAAO;AAAA,MACN;AAAA,MACA,GAAG,KAAK;AAAA,QACP,KAAK;AAAA,QACL;AAAA,QACA,MAAM;AAAA,QACN;AAAA,MAAA;AAAA,IACD;AAAA,EAEF;AAAA,EAEQ,cACP,MACA,OACA,WACA,iBAAiB,OAKhB;AACD,UAAM,oBAAoB,KAAK,WAAW,QAAQ,SAAS,SAAS,KAAK;AACzE,UAAM,qBAAqB,MAAM,WAAW,QAAQ,SAAS,SAAS,KAAK;AAC3E,UAAM,kBAAkB,KAAK,WAAW,SAAS;AACjD,UAAM,mBAAmB,MAAM,WAAW,SAAS;AAEnD,UAAM,gBAAiB,qBAAqB,sBACvC,qBAAqB,CAAC,oBACtB,sBAAsB,CAAC;AAI5B,UAAM,0BAA0B,CAAC,kBAAkB,CAAC,iBAAiB,oBAAoB,SAAY;AACrG,UAAM,2BAA2B,CAAC,kBAAkB,CAAC,iBAAiB,qBAAqB,SAAY;AAEvG,WAAO;AAAA,MACN,QAAQ;AAAA,MACR,iBAAiB;AAAA,MACjB,kBAAkB;AAAA,IAAA;AAAA,EAEpB;AAAA,EAEQ,sBACP,gBACA,uBAAyD,CAAA,GACzD,iBACA,wBAA0D,IACY;AACtE,UAAM,cAA8C,CAAA;AACpD,UAAM,aAAyC,CAAA;AAE/C,aAAS,SAAS,EAAE,GAAG,sBAAsB,GAAG,yBAAyB;AACxE,YAAM,EAAE,qBAAqB,UAAA,IAAc,KAAK;AAAA,QAC/C;AAAA,QACA,qBAAqB,KAAK,KAAK;AAAA,QAC/B;AAAA,QACA,sBAAsB,KAAK,KAAK;AAAA,MAAA;AAEjC,UAAI,cAAc,MAAM;AACvB,oBAAY,KAAK,IAAI;AAAA,MACtB,WAAW,wBAAwB,UAAa,OAAO,cAAc,UAAU;AAC9E,oBAAY,KAAK,IAAI;AACrB,mBAAW,SAAS,IAAI;AAAA,MACzB;AAAA,IACD;AAEA,WAAO,EAAE,YAAY,YAAA;AAAA,EACtB;AAAA,EAEQ,gBACP,gBACA,gBAA2C,OAC3C,iBACA,iBAA4C,OAI3C;AACD,QAAI,kBAAkB,QAAQ,mBAAmB,MAAM;AACtD,aAAO;AAAA,QACN,WAAW;AAAA,QACX,qBAAqB;AAAA,MAAA;AAAA,IAEvB;AAEA,QAAI,kBAAkB,SAAS,mBAAmB,OAAO;AACxD,YAAM,gBAAyC,eAAe,aAAa;AAC3E,YAAM,iBAA0C,gBAAgB,cAAc;AAC9E,UAAI,kBAAkB,gBAAgB;AACrC,eAAO;AAAA,UACN,WAAW;AAAA,UACX,qBAAqB;AAAA,QAAA;AAAA,MAEvB;AAEA,UAAI,gBAAgB,cAAc,gBAAgB,OAAO;AACzD,aAAO,eAAe,aAAa,GAAG;AACrC,yBAAiB;AAAA,MAClB;AACA,aAAO;AAAA,QACN,WAAW;AAAA,QACX,qBAAqB;AAAA,UACpB,IAAI,CAAC,eAAe,cAAc;AAAA,QAAA;AAAA,MACnC;AAAA,IAEF,WAAW,kBAAkB,OAAO;AACnC,aAAO;AAAA,QACN,WAAW;AAAA,QACX,qBAAqB,eAAe,aAAa;AAAA,MAAA;AAAA,IAEnD,WAAW,mBAAmB,OAAO;AACpC,UAAI,gBAAgB;AACpB,UAAI,oBAAoB,gBAAgB;AACvC,eAAO,eAAe,aAAa,GAAG;AACrC,2BAAiB;AAAA,QAClB;AAAA,MACD;AACA,aAAO;AAAA,QACN,WAAW;AAAA,QACX,qBAAqB,gBAAgB,cAAc;AAAA,MAAA;AAAA,IAErD,OAAO;AACN,aAAO;AAAA,QACN,WAAW;AAAA,QACX,qBAAqB;AAAA,MAAA;AAAA,IAEvB;AAAA,EACD;AACD;"}
|
|
@@ -4,13 +4,29 @@ const schema = require("@contember/schema");
|
|
|
4
4
|
const EvaluatedPredicateReplacer = require("./EvaluatedPredicateReplacer.cjs");
|
|
5
5
|
const getRowLevelPredicatePseudoField = (entity) => entity.primary;
|
|
6
6
|
class PredicateFactory {
|
|
7
|
-
constructor(permissions, model, variableInjector) {
|
|
7
|
+
constructor(permissions, model, variableInjector, allPermissions) {
|
|
8
8
|
this.permissions = permissions;
|
|
9
9
|
this.model = model;
|
|
10
10
|
this.variableInjector = variableInjector;
|
|
11
|
+
this.allPermissions = allPermissions;
|
|
11
12
|
}
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Selects the appropriate permission set based on query context:
|
|
15
|
+
* - `isRoot === false` (nested/relation context): uses allPermissions (includes through-only permissions)
|
|
16
|
+
* - `isRoot === true` or `isRoot === undefined` (root or unknown context): uses root-only permissions
|
|
17
|
+
*
|
|
18
|
+
* The `undefined` vs `true` distinction matters: `undefined` is the default for callers
|
|
19
|
+
* that don't track root context, and must fall through to root permissions for safety.
|
|
20
|
+
*/
|
|
21
|
+
getPermissionsForContext(isRoot) {
|
|
22
|
+
if (isRoot === false && this.allPermissions) {
|
|
23
|
+
return this.allPermissions;
|
|
24
|
+
}
|
|
25
|
+
return this.permissions;
|
|
26
|
+
}
|
|
27
|
+
getFieldPredicate(entity, operation, fieldName, isRoot) {
|
|
28
|
+
const perms = this.getPermissionsForContext(isRoot);
|
|
29
|
+
const permissions = perms[entity.name]?.operations?.[operation];
|
|
14
30
|
const predicate = permissions?.[fieldName] ?? false;
|
|
15
31
|
const rowLevelField = getRowLevelPredicatePseudoField(entity);
|
|
16
32
|
const primaryPredicate = permissions?.[rowLevelField] ?? false;
|
|
@@ -20,11 +36,13 @@ class PredicateFactory {
|
|
|
20
36
|
predicate
|
|
21
37
|
};
|
|
22
38
|
}
|
|
23
|
-
shouldApplyCellLevelPredicate(entity, operation, fieldName) {
|
|
39
|
+
shouldApplyCellLevelPredicate(entity, operation, fieldName, isRoot) {
|
|
40
|
+
const perms = this.getPermissionsForContext(isRoot);
|
|
24
41
|
const rowLevelField = getRowLevelPredicatePseudoField(entity);
|
|
25
|
-
const permissions =
|
|
42
|
+
const permissions = perms[entity.name]?.operations?.[operation];
|
|
26
43
|
return permissions?.[fieldName] !== permissions?.[rowLevelField];
|
|
27
44
|
}
|
|
45
|
+
/** Delete predicates are not context-aware — through-permission support is scoped to read operations only. */
|
|
28
46
|
createDeletePredicate(entity) {
|
|
29
47
|
const neverCondition = { [entity.primary]: { never: true } };
|
|
30
48
|
const entityPermissions = this.permissions[entity.name];
|
|
@@ -40,8 +58,9 @@ class PredicateFactory {
|
|
|
40
58
|
}
|
|
41
59
|
return this.buildPredicates(entity, [deletePredicate]);
|
|
42
60
|
}
|
|
43
|
-
create(entity, operation, fieldNames = [getRowLevelPredicatePseudoField(entity)], relationContext) {
|
|
44
|
-
const
|
|
61
|
+
create(entity, operation, fieldNames = [getRowLevelPredicatePseudoField(entity)], relationContext, isRoot) {
|
|
62
|
+
const perms = this.getPermissionsForContext(isRoot);
|
|
63
|
+
const entityPermissions = perms[entity.name];
|
|
45
64
|
const neverCondition = { [entity.primary]: { never: true } };
|
|
46
65
|
if (!entityPermissions) {
|
|
47
66
|
return neverCondition;
|
|
@@ -57,10 +76,11 @@ class PredicateFactory {
|
|
|
57
76
|
if (operationPredicates === false) {
|
|
58
77
|
return neverCondition;
|
|
59
78
|
}
|
|
60
|
-
return this.buildPredicates(entity, operationPredicates, relationContext);
|
|
79
|
+
return this.buildPredicates(entity, operationPredicates, relationContext, isRoot);
|
|
61
80
|
}
|
|
62
|
-
buildPredicates(entity, predicates, relationContext) {
|
|
63
|
-
const
|
|
81
|
+
buildPredicates(entity, predicates, relationContext, isRoot) {
|
|
82
|
+
const perms = this.getPermissionsForContext(isRoot);
|
|
83
|
+
const entityPermissions = perms[entity.name] ?? {};
|
|
64
84
|
const predicatesWhere = predicates.reduce(
|
|
65
85
|
(result, name) => {
|
|
66
86
|
if (!entityPermissions.predicates[name]) {
|
|
@@ -75,7 +95,7 @@ class PredicateFactory {
|
|
|
75
95
|
return {};
|
|
76
96
|
}
|
|
77
97
|
const where = predicatesWhere.length === 1 ? predicatesWhere[0] : { and: predicatesWhere };
|
|
78
|
-
return this.optimizePredicates(where, relationContext);
|
|
98
|
+
return this.optimizePredicates(where, relationContext, isRoot);
|
|
79
99
|
}
|
|
80
100
|
getRequiredPredicates(fieldNames, fieldPermissions) {
|
|
81
101
|
const predicates = [];
|
|
@@ -93,11 +113,11 @@ class PredicateFactory {
|
|
|
93
113
|
}
|
|
94
114
|
return predicates;
|
|
95
115
|
}
|
|
96
|
-
optimizePredicates(where, relationContext) {
|
|
116
|
+
optimizePredicates(where, relationContext, isRoot) {
|
|
97
117
|
if (!relationContext || !relationContext.targetRelation) {
|
|
98
118
|
return where;
|
|
99
119
|
}
|
|
100
|
-
const sourcePredicate = this.create(relationContext.entity, schema.Acl.Operation.read, [relationContext.relation.name]);
|
|
120
|
+
const sourcePredicate = this.create(relationContext.entity, schema.Acl.Operation.read, [relationContext.relation.name], void 0, isRoot);
|
|
101
121
|
if (Object.keys(sourcePredicate).length === 0) {
|
|
102
122
|
return where;
|
|
103
123
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PredicateFactory.cjs","sources":["../../../../../packages/engine-content-api/src/acl/PredicateFactory.ts"],"sourcesContent":["import { Acl, Input, Model } from '@contember/schema'\nimport { VariableInjector } from './VariableInjector'\nimport { EvaluatedPredicateReplacer } from './EvaluatedPredicateReplacer'\n\nconst getRowLevelPredicatePseudoField = (entity: Model.Entity) => entity.primary\n\nexport interface FieldRequiredPredicate {\n\tpredicate: Acl.Predicate\n\tisSameAsPrimary: boolean\n}\n\nexport class PredicateFactory {\n\tconstructor(\n\t\tprivate readonly permissions: Acl.Permissions,\n\t\tprivate readonly model: Model.Schema,\n\t\tprivate readonly variableInjector: VariableInjector,\n\t) {}\n\n\tpublic getFieldPredicate(\n\t\tentity: Model.Entity,\n\t\toperation: Acl.Operation.update | Acl.Operation.read | Acl.Operation.create,\n\t\tfieldName: string,\n\t): FieldRequiredPredicate {\n\t\tconst
|
|
1
|
+
{"version":3,"file":"PredicateFactory.cjs","sources":["../../../../../packages/engine-content-api/src/acl/PredicateFactory.ts"],"sourcesContent":["import { Acl, Input, Model } from '@contember/schema'\nimport { VariableInjector } from './VariableInjector'\nimport { EvaluatedPredicateReplacer } from './EvaluatedPredicateReplacer'\n\nconst getRowLevelPredicatePseudoField = (entity: Model.Entity) => entity.primary\n\nexport interface FieldRequiredPredicate {\n\tpredicate: Acl.Predicate\n\tisSameAsPrimary: boolean\n}\n\nexport class PredicateFactory {\n\tconstructor(\n\t\tprivate readonly permissions: Acl.Permissions,\n\t\tprivate readonly model: Model.Schema,\n\t\tprivate readonly variableInjector: VariableInjector,\n\t\tprivate readonly allPermissions?: Acl.Permissions,\n\t) {}\n\n\t/**\n\t * Selects the appropriate permission set based on query context:\n\t * - `isRoot === false` (nested/relation context): uses allPermissions (includes through-only permissions)\n\t * - `isRoot === true` or `isRoot === undefined` (root or unknown context): uses root-only permissions\n\t *\n\t * The `undefined` vs `true` distinction matters: `undefined` is the default for callers\n\t * that don't track root context, and must fall through to root permissions for safety.\n\t */\n\tprivate getPermissionsForContext(isRoot?: boolean): Acl.Permissions {\n\t\tif (isRoot === false && this.allPermissions) {\n\t\t\treturn this.allPermissions\n\t\t}\n\t\treturn this.permissions\n\t}\n\n\tpublic getFieldPredicate(\n\t\tentity: Model.Entity,\n\t\toperation: Acl.Operation.update | Acl.Operation.read | Acl.Operation.create,\n\t\tfieldName: string,\n\t\tisRoot?: boolean,\n\t): FieldRequiredPredicate {\n\t\tconst perms = this.getPermissionsForContext(isRoot)\n\t\tconst permissions = perms[entity.name]?.operations?.[operation]\n\t\tconst predicate = permissions?.[fieldName] ?? false\n\t\tconst rowLevelField = getRowLevelPredicatePseudoField(entity)\n\n\t\tconst primaryPredicate = permissions?.[rowLevelField] ?? false\n\t\tconst isSameAsPrimary = predicate === primaryPredicate\n\n\t\treturn {\n\t\t\tisSameAsPrimary,\n\t\t\tpredicate,\n\t\t}\n\t}\n\n\tpublic shouldApplyCellLevelPredicate(\n\t\tentity: Model.Entity,\n\t\toperation: Acl.Operation.read,\n\t\tfieldName: string,\n\t\tisRoot?: boolean,\n\t): boolean {\n\t\tconst perms = this.getPermissionsForContext(isRoot)\n\t\tconst rowLevelField = getRowLevelPredicatePseudoField(entity)\n\t\tconst permissions = perms[entity.name]?.operations?.[operation]\n\t\treturn permissions?.[fieldName] !== permissions?.[rowLevelField]\n\t}\n\n\t/** Delete predicates are not context-aware — through-permission support is scoped to read operations only. */\n\tpublic createDeletePredicate(entity: Model.Entity) {\n\t\tconst neverCondition: Input.Where = { [entity.primary]: { never: true } }\n\t\tconst entityPermissions = this.permissions[entity.name]\n\t\tif (!entityPermissions) {\n\t\t\treturn neverCondition\n\t\t}\n\t\tconst deletePredicate = entityPermissions.operations.delete\n\t\tif (deletePredicate === undefined || deletePredicate === false) {\n\t\t\treturn neverCondition\n\t\t}\n\t\tif (deletePredicate === true) {\n\t\t\treturn {}\n\t\t}\n\t\treturn this.buildPredicates(entity, [deletePredicate])\n\t}\n\n\tpublic create(\n\t\tentity: Model.Entity,\n\t\toperation: Acl.Operation.update | Acl.Operation.read | Acl.Operation.create,\n\t\tfieldNames: string[] = [getRowLevelPredicatePseudoField(entity)],\n\t\trelationContext?: Model.AnyRelationContext,\n\t\tisRoot?: boolean,\n\t): Input.OptionalWhere {\n\t\tconst perms = this.getPermissionsForContext(isRoot)\n\t\tconst entityPermissions: Acl.EntityPermissions = perms[entity.name]\n\t\tconst neverCondition: Input.Where = { [entity.primary]: { never: true } }\n\n\t\tif (!entityPermissions) {\n\t\t\treturn neverCondition\n\t\t}\n\n\t\tif (fieldNames === undefined) {\n\t\t\tfieldNames = [getRowLevelPredicatePseudoField(entity)]\n\t\t}\n\t\tconst fieldPermissions = entityPermissions.operations[operation]\n\t\tif (fieldPermissions === undefined) {\n\t\t\treturn neverCondition\n\t\t}\n\t\tconst operationPredicates = this.getRequiredPredicates(fieldNames, fieldPermissions)\n\t\tif (operationPredicates === false) {\n\t\t\treturn neverCondition\n\t\t}\n\n\t\treturn this.buildPredicates(entity, operationPredicates, relationContext, isRoot)\n\t}\n\n\tpublic buildPredicates(\n\t\tentity: Model.Entity,\n\t\tpredicates: Acl.PredicateReference[],\n\t\trelationContext?: Model.AnyRelationContext,\n\t\tisRoot?: boolean,\n\t): Input.OptionalWhere {\n\t\tconst perms = this.getPermissionsForContext(isRoot)\n\t\tconst entityPermissions: Acl.EntityPermissions = perms[entity.name] ?? {}\n\n\t\tconst predicatesWhere: Input.Where[] = predicates.reduce(\n\t\t\t(result: Input.Where[], name: Acl.PredicateReference): Input.Where[] => {\n\t\t\t\tif (!entityPermissions.predicates[name]) {\n\t\t\t\t\tthrow new Error(`${entity.name}: Undefined predicate ${name}`)\n\t\t\t\t}\n\t\t\t\tconst predicateWhere: Input.Where = this.variableInjector.inject(entity, entityPermissions.predicates[name])\n\t\t\t\treturn [...result, predicateWhere]\n\t\t\t},\n\t\t\t[],\n\t\t)\n\t\tif (predicatesWhere.length === 0) {\n\t\t\treturn {}\n\t\t}\n\t\tconst where: Input.Where = predicatesWhere.length === 1 ? predicatesWhere[0] : { and: predicatesWhere }\n\t\treturn this.optimizePredicates(where, relationContext, isRoot)\n\t}\n\n\tprivate getRequiredPredicates(\n\t\tfieldNames: string[],\n\t\tfieldPermissions: Acl.FieldPermissions,\n\t): Acl.PredicateReference[] | false {\n\t\tconst predicates: Acl.PredicateReference[] = []\n\t\tfor (let name of fieldNames) {\n\t\t\tconst fieldPredicate = fieldPermissions[name]\n\t\t\tif (fieldPredicate === undefined || fieldPredicate === false) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (fieldPredicate === true) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (!predicates.includes(fieldPredicate)) {\n\t\t\t\tpredicates.push(fieldPredicate)\n\t\t\t}\n\t\t}\n\t\treturn predicates\n\t}\n\n\tpublic optimizePredicates(where: Input.OptionalWhere, relationContext?: Model.AnyRelationContext, isRoot?: boolean) {\n\t\tif (!relationContext || !relationContext.targetRelation) {\n\t\t\treturn where\n\t\t}\n\t\tconst sourcePredicate = this.create(relationContext.entity, Acl.Operation.read, [relationContext.relation.name], undefined, isRoot)\n\t\tif (Object.keys(sourcePredicate).length === 0) {\n\t\t\treturn where\n\t\t}\n\n\t\tconst replacer = new EvaluatedPredicateReplacer(sourcePredicate, relationContext.entity, relationContext.targetRelation)\n\t\treturn replacer.replace(where)\n\t}\n}\n"],"names":["Acl","EvaluatedPredicateReplacer"],"mappings":"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"}
|
|
@@ -2,13 +2,29 @@ import { Acl } from "@contember/schema";
|
|
|
2
2
|
import { EvaluatedPredicateReplacer } from "./EvaluatedPredicateReplacer.js";
|
|
3
3
|
const getRowLevelPredicatePseudoField = (entity) => entity.primary;
|
|
4
4
|
class PredicateFactory {
|
|
5
|
-
constructor(permissions, model, variableInjector) {
|
|
5
|
+
constructor(permissions, model, variableInjector, allPermissions) {
|
|
6
6
|
this.permissions = permissions;
|
|
7
7
|
this.model = model;
|
|
8
8
|
this.variableInjector = variableInjector;
|
|
9
|
+
this.allPermissions = allPermissions;
|
|
9
10
|
}
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Selects the appropriate permission set based on query context:
|
|
13
|
+
* - `isRoot === false` (nested/relation context): uses allPermissions (includes through-only permissions)
|
|
14
|
+
* - `isRoot === true` or `isRoot === undefined` (root or unknown context): uses root-only permissions
|
|
15
|
+
*
|
|
16
|
+
* The `undefined` vs `true` distinction matters: `undefined` is the default for callers
|
|
17
|
+
* that don't track root context, and must fall through to root permissions for safety.
|
|
18
|
+
*/
|
|
19
|
+
getPermissionsForContext(isRoot) {
|
|
20
|
+
if (isRoot === false && this.allPermissions) {
|
|
21
|
+
return this.allPermissions;
|
|
22
|
+
}
|
|
23
|
+
return this.permissions;
|
|
24
|
+
}
|
|
25
|
+
getFieldPredicate(entity, operation, fieldName, isRoot) {
|
|
26
|
+
const perms = this.getPermissionsForContext(isRoot);
|
|
27
|
+
const permissions = perms[entity.name]?.operations?.[operation];
|
|
12
28
|
const predicate = permissions?.[fieldName] ?? false;
|
|
13
29
|
const rowLevelField = getRowLevelPredicatePseudoField(entity);
|
|
14
30
|
const primaryPredicate = permissions?.[rowLevelField] ?? false;
|
|
@@ -18,11 +34,13 @@ class PredicateFactory {
|
|
|
18
34
|
predicate
|
|
19
35
|
};
|
|
20
36
|
}
|
|
21
|
-
shouldApplyCellLevelPredicate(entity, operation, fieldName) {
|
|
37
|
+
shouldApplyCellLevelPredicate(entity, operation, fieldName, isRoot) {
|
|
38
|
+
const perms = this.getPermissionsForContext(isRoot);
|
|
22
39
|
const rowLevelField = getRowLevelPredicatePseudoField(entity);
|
|
23
|
-
const permissions =
|
|
40
|
+
const permissions = perms[entity.name]?.operations?.[operation];
|
|
24
41
|
return permissions?.[fieldName] !== permissions?.[rowLevelField];
|
|
25
42
|
}
|
|
43
|
+
/** Delete predicates are not context-aware — through-permission support is scoped to read operations only. */
|
|
26
44
|
createDeletePredicate(entity) {
|
|
27
45
|
const neverCondition = { [entity.primary]: { never: true } };
|
|
28
46
|
const entityPermissions = this.permissions[entity.name];
|
|
@@ -38,8 +56,9 @@ class PredicateFactory {
|
|
|
38
56
|
}
|
|
39
57
|
return this.buildPredicates(entity, [deletePredicate]);
|
|
40
58
|
}
|
|
41
|
-
create(entity, operation, fieldNames = [getRowLevelPredicatePseudoField(entity)], relationContext) {
|
|
42
|
-
const
|
|
59
|
+
create(entity, operation, fieldNames = [getRowLevelPredicatePseudoField(entity)], relationContext, isRoot) {
|
|
60
|
+
const perms = this.getPermissionsForContext(isRoot);
|
|
61
|
+
const entityPermissions = perms[entity.name];
|
|
43
62
|
const neverCondition = { [entity.primary]: { never: true } };
|
|
44
63
|
if (!entityPermissions) {
|
|
45
64
|
return neverCondition;
|
|
@@ -55,10 +74,11 @@ class PredicateFactory {
|
|
|
55
74
|
if (operationPredicates === false) {
|
|
56
75
|
return neverCondition;
|
|
57
76
|
}
|
|
58
|
-
return this.buildPredicates(entity, operationPredicates, relationContext);
|
|
77
|
+
return this.buildPredicates(entity, operationPredicates, relationContext, isRoot);
|
|
59
78
|
}
|
|
60
|
-
buildPredicates(entity, predicates, relationContext) {
|
|
61
|
-
const
|
|
79
|
+
buildPredicates(entity, predicates, relationContext, isRoot) {
|
|
80
|
+
const perms = this.getPermissionsForContext(isRoot);
|
|
81
|
+
const entityPermissions = perms[entity.name] ?? {};
|
|
62
82
|
const predicatesWhere = predicates.reduce(
|
|
63
83
|
(result, name) => {
|
|
64
84
|
if (!entityPermissions.predicates[name]) {
|
|
@@ -73,7 +93,7 @@ class PredicateFactory {
|
|
|
73
93
|
return {};
|
|
74
94
|
}
|
|
75
95
|
const where = predicatesWhere.length === 1 ? predicatesWhere[0] : { and: predicatesWhere };
|
|
76
|
-
return this.optimizePredicates(where, relationContext);
|
|
96
|
+
return this.optimizePredicates(where, relationContext, isRoot);
|
|
77
97
|
}
|
|
78
98
|
getRequiredPredicates(fieldNames, fieldPermissions) {
|
|
79
99
|
const predicates = [];
|
|
@@ -91,11 +111,11 @@ class PredicateFactory {
|
|
|
91
111
|
}
|
|
92
112
|
return predicates;
|
|
93
113
|
}
|
|
94
|
-
optimizePredicates(where, relationContext) {
|
|
114
|
+
optimizePredicates(where, relationContext, isRoot) {
|
|
95
115
|
if (!relationContext || !relationContext.targetRelation) {
|
|
96
116
|
return where;
|
|
97
117
|
}
|
|
98
|
-
const sourcePredicate = this.create(relationContext.entity, Acl.Operation.read, [relationContext.relation.name]);
|
|
118
|
+
const sourcePredicate = this.create(relationContext.entity, Acl.Operation.read, [relationContext.relation.name], void 0, isRoot);
|
|
99
119
|
if (Object.keys(sourcePredicate).length === 0) {
|
|
100
120
|
return where;
|
|
101
121
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PredicateFactory.js","sources":["../../../../../packages/engine-content-api/src/acl/PredicateFactory.ts"],"sourcesContent":["import { Acl, Input, Model } from '@contember/schema'\nimport { VariableInjector } from './VariableInjector'\nimport { EvaluatedPredicateReplacer } from './EvaluatedPredicateReplacer'\n\nconst getRowLevelPredicatePseudoField = (entity: Model.Entity) => entity.primary\n\nexport interface FieldRequiredPredicate {\n\tpredicate: Acl.Predicate\n\tisSameAsPrimary: boolean\n}\n\nexport class PredicateFactory {\n\tconstructor(\n\t\tprivate readonly permissions: Acl.Permissions,\n\t\tprivate readonly model: Model.Schema,\n\t\tprivate readonly variableInjector: VariableInjector,\n\t) {}\n\n\tpublic getFieldPredicate(\n\t\tentity: Model.Entity,\n\t\toperation: Acl.Operation.update | Acl.Operation.read | Acl.Operation.create,\n\t\tfieldName: string,\n\t): FieldRequiredPredicate {\n\t\tconst
|
|
1
|
+
{"version":3,"file":"PredicateFactory.js","sources":["../../../../../packages/engine-content-api/src/acl/PredicateFactory.ts"],"sourcesContent":["import { Acl, Input, Model } from '@contember/schema'\nimport { VariableInjector } from './VariableInjector'\nimport { EvaluatedPredicateReplacer } from './EvaluatedPredicateReplacer'\n\nconst getRowLevelPredicatePseudoField = (entity: Model.Entity) => entity.primary\n\nexport interface FieldRequiredPredicate {\n\tpredicate: Acl.Predicate\n\tisSameAsPrimary: boolean\n}\n\nexport class PredicateFactory {\n\tconstructor(\n\t\tprivate readonly permissions: Acl.Permissions,\n\t\tprivate readonly model: Model.Schema,\n\t\tprivate readonly variableInjector: VariableInjector,\n\t\tprivate readonly allPermissions?: Acl.Permissions,\n\t) {}\n\n\t/**\n\t * Selects the appropriate permission set based on query context:\n\t * - `isRoot === false` (nested/relation context): uses allPermissions (includes through-only permissions)\n\t * - `isRoot === true` or `isRoot === undefined` (root or unknown context): uses root-only permissions\n\t *\n\t * The `undefined` vs `true` distinction matters: `undefined` is the default for callers\n\t * that don't track root context, and must fall through to root permissions for safety.\n\t */\n\tprivate getPermissionsForContext(isRoot?: boolean): Acl.Permissions {\n\t\tif (isRoot === false && this.allPermissions) {\n\t\t\treturn this.allPermissions\n\t\t}\n\t\treturn this.permissions\n\t}\n\n\tpublic getFieldPredicate(\n\t\tentity: Model.Entity,\n\t\toperation: Acl.Operation.update | Acl.Operation.read | Acl.Operation.create,\n\t\tfieldName: string,\n\t\tisRoot?: boolean,\n\t): FieldRequiredPredicate {\n\t\tconst perms = this.getPermissionsForContext(isRoot)\n\t\tconst permissions = perms[entity.name]?.operations?.[operation]\n\t\tconst predicate = permissions?.[fieldName] ?? false\n\t\tconst rowLevelField = getRowLevelPredicatePseudoField(entity)\n\n\t\tconst primaryPredicate = permissions?.[rowLevelField] ?? false\n\t\tconst isSameAsPrimary = predicate === primaryPredicate\n\n\t\treturn {\n\t\t\tisSameAsPrimary,\n\t\t\tpredicate,\n\t\t}\n\t}\n\n\tpublic shouldApplyCellLevelPredicate(\n\t\tentity: Model.Entity,\n\t\toperation: Acl.Operation.read,\n\t\tfieldName: string,\n\t\tisRoot?: boolean,\n\t): boolean {\n\t\tconst perms = this.getPermissionsForContext(isRoot)\n\t\tconst rowLevelField = getRowLevelPredicatePseudoField(entity)\n\t\tconst permissions = perms[entity.name]?.operations?.[operation]\n\t\treturn permissions?.[fieldName] !== permissions?.[rowLevelField]\n\t}\n\n\t/** Delete predicates are not context-aware — through-permission support is scoped to read operations only. */\n\tpublic createDeletePredicate(entity: Model.Entity) {\n\t\tconst neverCondition: Input.Where = { [entity.primary]: { never: true } }\n\t\tconst entityPermissions = this.permissions[entity.name]\n\t\tif (!entityPermissions) {\n\t\t\treturn neverCondition\n\t\t}\n\t\tconst deletePredicate = entityPermissions.operations.delete\n\t\tif (deletePredicate === undefined || deletePredicate === false) {\n\t\t\treturn neverCondition\n\t\t}\n\t\tif (deletePredicate === true) {\n\t\t\treturn {}\n\t\t}\n\t\treturn this.buildPredicates(entity, [deletePredicate])\n\t}\n\n\tpublic create(\n\t\tentity: Model.Entity,\n\t\toperation: Acl.Operation.update | Acl.Operation.read | Acl.Operation.create,\n\t\tfieldNames: string[] = [getRowLevelPredicatePseudoField(entity)],\n\t\trelationContext?: Model.AnyRelationContext,\n\t\tisRoot?: boolean,\n\t): Input.OptionalWhere {\n\t\tconst perms = this.getPermissionsForContext(isRoot)\n\t\tconst entityPermissions: Acl.EntityPermissions = perms[entity.name]\n\t\tconst neverCondition: Input.Where = { [entity.primary]: { never: true } }\n\n\t\tif (!entityPermissions) {\n\t\t\treturn neverCondition\n\t\t}\n\n\t\tif (fieldNames === undefined) {\n\t\t\tfieldNames = [getRowLevelPredicatePseudoField(entity)]\n\t\t}\n\t\tconst fieldPermissions = entityPermissions.operations[operation]\n\t\tif (fieldPermissions === undefined) {\n\t\t\treturn neverCondition\n\t\t}\n\t\tconst operationPredicates = this.getRequiredPredicates(fieldNames, fieldPermissions)\n\t\tif (operationPredicates === false) {\n\t\t\treturn neverCondition\n\t\t}\n\n\t\treturn this.buildPredicates(entity, operationPredicates, relationContext, isRoot)\n\t}\n\n\tpublic buildPredicates(\n\t\tentity: Model.Entity,\n\t\tpredicates: Acl.PredicateReference[],\n\t\trelationContext?: Model.AnyRelationContext,\n\t\tisRoot?: boolean,\n\t): Input.OptionalWhere {\n\t\tconst perms = this.getPermissionsForContext(isRoot)\n\t\tconst entityPermissions: Acl.EntityPermissions = perms[entity.name] ?? {}\n\n\t\tconst predicatesWhere: Input.Where[] = predicates.reduce(\n\t\t\t(result: Input.Where[], name: Acl.PredicateReference): Input.Where[] => {\n\t\t\t\tif (!entityPermissions.predicates[name]) {\n\t\t\t\t\tthrow new Error(`${entity.name}: Undefined predicate ${name}`)\n\t\t\t\t}\n\t\t\t\tconst predicateWhere: Input.Where = this.variableInjector.inject(entity, entityPermissions.predicates[name])\n\t\t\t\treturn [...result, predicateWhere]\n\t\t\t},\n\t\t\t[],\n\t\t)\n\t\tif (predicatesWhere.length === 0) {\n\t\t\treturn {}\n\t\t}\n\t\tconst where: Input.Where = predicatesWhere.length === 1 ? predicatesWhere[0] : { and: predicatesWhere }\n\t\treturn this.optimizePredicates(where, relationContext, isRoot)\n\t}\n\n\tprivate getRequiredPredicates(\n\t\tfieldNames: string[],\n\t\tfieldPermissions: Acl.FieldPermissions,\n\t): Acl.PredicateReference[] | false {\n\t\tconst predicates: Acl.PredicateReference[] = []\n\t\tfor (let name of fieldNames) {\n\t\t\tconst fieldPredicate = fieldPermissions[name]\n\t\t\tif (fieldPredicate === undefined || fieldPredicate === false) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (fieldPredicate === true) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (!predicates.includes(fieldPredicate)) {\n\t\t\t\tpredicates.push(fieldPredicate)\n\t\t\t}\n\t\t}\n\t\treturn predicates\n\t}\n\n\tpublic optimizePredicates(where: Input.OptionalWhere, relationContext?: Model.AnyRelationContext, isRoot?: boolean) {\n\t\tif (!relationContext || !relationContext.targetRelation) {\n\t\t\treturn where\n\t\t}\n\t\tconst sourcePredicate = this.create(relationContext.entity, Acl.Operation.read, [relationContext.relation.name], undefined, isRoot)\n\t\tif (Object.keys(sourcePredicate).length === 0) {\n\t\t\treturn where\n\t\t}\n\n\t\tconst replacer = new EvaluatedPredicateReplacer(sourcePredicate, relationContext.entity, relationContext.targetRelation)\n\t\treturn replacer.replace(where)\n\t}\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -8,8 +8,9 @@ class PredicatesInjector {
|
|
|
8
8
|
this.predicateFactory = predicateFactory;
|
|
9
9
|
}
|
|
10
10
|
inject(entity, where, relationContext, ancestorPath) {
|
|
11
|
-
const
|
|
12
|
-
|
|
11
|
+
const isQueryRoot = !relationContext && (!ancestorPath || ancestorPath.length === 0);
|
|
12
|
+
const restrictedWhere = this.injectToWhere(where, entity, true, relationContext, false, ancestorPath ?? [], isQueryRoot);
|
|
13
|
+
return this.createWhere(entity, void 0, restrictedWhere, relationContext, false, ancestorPath ?? [], isQueryRoot);
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
16
|
* Finds an ancestor in the path that matches the given relation as a back-reference.
|
|
@@ -23,18 +24,19 @@ class PredicatesInjector {
|
|
|
23
24
|
(ctx) => ctx.targetRelation?.name === relationName && ctx.targetEntity.name === relationSourceEntityName
|
|
24
25
|
);
|
|
25
26
|
}
|
|
26
|
-
createWhere(entity, fieldNames, where, relationContext, isBackReferenceContext, ancestorPath) {
|
|
27
|
+
createWhere(entity, fieldNames, where, relationContext, isBackReferenceContext, ancestorPath, isQueryRoot) {
|
|
27
28
|
const shouldSimplify = isBackReferenceContext === true && ancestorPath !== void 0 && relationContext !== void 0 && this.findBackReferencedAncestor(ancestorPath, relationContext.relation.name, relationContext.entity.name) !== void 0;
|
|
28
29
|
let predicatesWhere;
|
|
29
30
|
if (shouldSimplify) {
|
|
30
31
|
predicatesWhere = { [entity.primary]: { always: true } };
|
|
31
32
|
} else {
|
|
32
|
-
const rawPredicate = this.predicateFactory.create(entity, schema.Acl.Operation.read, fieldNames, relationContext);
|
|
33
|
+
const rawPredicate = this.predicateFactory.create(entity, schema.Acl.Operation.read, fieldNames, relationContext, isQueryRoot);
|
|
33
34
|
predicatesWhere = this.injectPredicatesToPredicate(
|
|
34
35
|
rawPredicate,
|
|
35
36
|
entity,
|
|
36
37
|
isBackReferenceContext ?? false,
|
|
37
|
-
ancestorPath ?? []
|
|
38
|
+
ancestorPath ?? [],
|
|
39
|
+
isQueryRoot
|
|
38
40
|
);
|
|
39
41
|
}
|
|
40
42
|
const and = [where, predicatesWhere].filter((it) => Object.keys(it).length > 0);
|
|
@@ -51,16 +53,16 @@ class PredicatesInjector {
|
|
|
51
53
|
* This ensures that when a predicate like { department: { company: { name: 'Acme' } } }
|
|
52
54
|
* is used, the predicates of Department and Company are also applied.
|
|
53
55
|
*/
|
|
54
|
-
injectPredicatesToPredicate(where, entity, isBackReferenceContext, ancestorPath) {
|
|
56
|
+
injectPredicatesToPredicate(where, entity, isBackReferenceContext, ancestorPath, isQueryRoot) {
|
|
55
57
|
const resultWhere = {};
|
|
56
58
|
if (where.and) {
|
|
57
|
-
resultWhere.and = where.and.filter((it) => !!it).map((it) => this.injectPredicatesToPredicate(it, entity, isBackReferenceContext, ancestorPath));
|
|
59
|
+
resultWhere.and = where.and.filter((it) => !!it).map((it) => this.injectPredicatesToPredicate(it, entity, isBackReferenceContext, ancestorPath, isQueryRoot));
|
|
58
60
|
}
|
|
59
61
|
if (where.or) {
|
|
60
|
-
resultWhere.or = where.or.filter((it) => !!it).map((it) => this.injectPredicatesToPredicate(it, entity, isBackReferenceContext, ancestorPath));
|
|
62
|
+
resultWhere.or = where.or.filter((it) => !!it).map((it) => this.injectPredicatesToPredicate(it, entity, isBackReferenceContext, ancestorPath, isQueryRoot));
|
|
61
63
|
}
|
|
62
64
|
if (where.not) {
|
|
63
|
-
resultWhere.not = this.injectPredicatesToPredicate(where.not, entity, isBackReferenceContext, ancestorPath);
|
|
65
|
+
resultWhere.not = this.injectPredicatesToPredicate(where.not, entity, isBackReferenceContext, ancestorPath, isQueryRoot);
|
|
64
66
|
}
|
|
65
67
|
const fields = Object.keys(where).filter((it) => !["and", "or", "not"].includes(it));
|
|
66
68
|
for (const field of fields) {
|
|
@@ -82,10 +84,11 @@ class PredicatesInjector {
|
|
|
82
84
|
relationWhere,
|
|
83
85
|
context.targetEntity,
|
|
84
86
|
nestedIsBackReferenceContext,
|
|
85
|
-
nestedAncestorPath
|
|
87
|
+
nestedAncestorPath,
|
|
88
|
+
isQueryRoot
|
|
86
89
|
);
|
|
87
90
|
const shouldSimplifyNested = nestedIsBackReferenceContext && this.findBackReferencedAncestor(nestedAncestorPath, context.relation.name, context.entity.name) !== void 0;
|
|
88
|
-
const targetPredicate = shouldSimplifyNested ? { [context.targetEntity.primary]: { always: true } } : this.predicateFactory.create(context.targetEntity, schema.Acl.Operation.read, void 0, context);
|
|
91
|
+
const targetPredicate = shouldSimplifyNested ? { [context.targetEntity.primary]: { always: true } } : this.predicateFactory.create(context.targetEntity, schema.Acl.Operation.read, void 0, context, isQueryRoot);
|
|
89
92
|
const primaryKey = context.targetEntity.primary;
|
|
90
93
|
const nestedIsAlwaysTrue = Object.keys(processedNestedWhere).length === 1 && processedNestedWhere[primaryKey] !== void 0 && processedNestedWhere[primaryKey]?.always === true;
|
|
91
94
|
const targetIsAlwaysTrue = Object.keys(targetPredicate).length === 1 && targetPredicate[primaryKey] !== void 0 && targetPredicate[primaryKey]?.always === true;
|
|
@@ -105,20 +108,20 @@ class PredicatesInjector {
|
|
|
105
108
|
}
|
|
106
109
|
return resultWhere;
|
|
107
110
|
}
|
|
108
|
-
injectToWhere(where, entity, isRoot, relationContext, isBackReferenceContext, ancestorPath) {
|
|
111
|
+
injectToWhere(where, entity, isRoot, relationContext, isBackReferenceContext, ancestorPath, isQueryRoot) {
|
|
109
112
|
const resultWhere = {};
|
|
110
113
|
if (where.and) {
|
|
111
114
|
resultWhere.and = where.and.filter((it) => !!it).map(
|
|
112
|
-
(it) => this.injectToWhere(it, entity, isRoot, relationContext, isBackReferenceContext, ancestorPath)
|
|
115
|
+
(it) => this.injectToWhere(it, entity, isRoot, relationContext, isBackReferenceContext, ancestorPath, isQueryRoot)
|
|
113
116
|
);
|
|
114
117
|
}
|
|
115
118
|
if (where.or) {
|
|
116
119
|
resultWhere.or = where.or.filter((it) => !!it).map(
|
|
117
|
-
(it) => this.injectToWhere(it, entity, isRoot, relationContext, isBackReferenceContext, ancestorPath)
|
|
120
|
+
(it) => this.injectToWhere(it, entity, isRoot, relationContext, isBackReferenceContext, ancestorPath, isQueryRoot)
|
|
118
121
|
);
|
|
119
122
|
}
|
|
120
123
|
if (where.not) {
|
|
121
|
-
resultWhere.not = this.injectToWhere(where.not, entity, isRoot, relationContext, isBackReferenceContext, ancestorPath);
|
|
124
|
+
resultWhere.not = this.injectToWhere(where.not, entity, isRoot, relationContext, isBackReferenceContext, ancestorPath, isQueryRoot);
|
|
122
125
|
}
|
|
123
126
|
const fields = Object.keys(where).filter((it) => !["and", "or", "not"].includes(it));
|
|
124
127
|
if (fields.length === 0) {
|
|
@@ -135,12 +138,12 @@ class PredicatesInjector {
|
|
|
135
138
|
const isBackReference = this.findBackReferencedAncestor(ancestorPath, context.relation.name, context.entity.name) !== void 0;
|
|
136
139
|
const nestedIsBackReferenceContext = isBackReference || isBackReferenceContext;
|
|
137
140
|
const nestedAncestorPath = [...ancestorPath, context];
|
|
138
|
-
return this.injectToWhere(relationWhere, context.targetEntity, false, context, nestedIsBackReferenceContext, nestedAncestorPath);
|
|
141
|
+
return this.injectToWhere(relationWhere, context.targetEntity, false, context, nestedIsBackReferenceContext, nestedAncestorPath, isQueryRoot);
|
|
139
142
|
}
|
|
140
143
|
});
|
|
141
144
|
}
|
|
142
|
-
const fieldsForPredicate = !isRoot ? fields : fields.filter((it) => this.predicateFactory.shouldApplyCellLevelPredicate(entity, schema.Acl.Operation.read, it));
|
|
143
|
-
return this.createWhere(entity, fieldsForPredicate, resultWhere, relationContext, isBackReferenceContext, ancestorPath);
|
|
145
|
+
const fieldsForPredicate = !isRoot ? fields : fields.filter((it) => this.predicateFactory.shouldApplyCellLevelPredicate(entity, schema.Acl.Operation.read, it, isQueryRoot));
|
|
146
|
+
return this.createWhere(entity, fieldsForPredicate, resultWhere, relationContext, isBackReferenceContext, ancestorPath, isQueryRoot);
|
|
144
147
|
}
|
|
145
148
|
}
|
|
146
149
|
exports.PredicatesInjector = PredicatesInjector;
|