@contember/engine-content-api 2.0.0-beta.6 → 2.0.0-rc.2
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/src/acl/PermissionFactory.cjs +80 -39
- package/dist/development/src/acl/PermissionFactory.cjs.map +1 -1
- package/dist/development/src/acl/PermissionFactory.js +80 -39
- package/dist/development/src/acl/PermissionFactory.js.map +1 -1
- package/dist/production/src/acl/PermissionFactory.cjs +80 -39
- package/dist/production/src/acl/PermissionFactory.cjs.map +1 -1
- package/dist/production/src/acl/PermissionFactory.js +80 -39
- package/dist/production/src/acl/PermissionFactory.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/dist/types/acl/PermissionFactory.d.ts +3 -0
- package/dist/types/acl/PermissionFactory.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/acl/PermissionFactory.ts +111 -42
- package/tests/cases/unit/permissionMerger.test.ts +180 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contember/engine-content-api",
|
|
3
|
-
"version": "2.0.0-
|
|
3
|
+
"version": "2.0.0-rc.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "./dist/production/index.js",
|
|
6
6
|
"typings": "./dist/types/index.d.ts",
|
|
@@ -26,18 +26,18 @@
|
|
|
26
26
|
"generate": "gql-gen --config graphql.codegen.yml"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@contember/database": "2.0.0-
|
|
30
|
-
"@contember/dic": "2.0.0-
|
|
31
|
-
"@contember/graphql-utils": "2.0.0-
|
|
32
|
-
"@contember/logger": "2.0.0-
|
|
33
|
-
"@contember/schema": "2.0.0-
|
|
34
|
-
"@contember/schema-definition": "2.0.0-
|
|
35
|
-
"@contember/schema-utils": "2.0.0-
|
|
29
|
+
"@contember/database": "2.0.0-rc.2",
|
|
30
|
+
"@contember/dic": "2.0.0-rc.2",
|
|
31
|
+
"@contember/graphql-utils": "2.0.0-rc.2",
|
|
32
|
+
"@contember/logger": "2.0.0-rc.2",
|
|
33
|
+
"@contember/schema": "2.0.0-rc.2",
|
|
34
|
+
"@contember/schema-definition": "2.0.0-rc.2",
|
|
35
|
+
"@contember/schema-utils": "2.0.0-rc.2",
|
|
36
36
|
"fast-deep-equal": "^3.1.3"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@contember/database-tester": "2.0.0-
|
|
40
|
-
"@contember/schema-definition": "2.0.0-
|
|
39
|
+
"@contember/database-tester": "2.0.0-rc.2",
|
|
40
|
+
"@contember/schema-definition": "2.0.0-rc.2",
|
|
41
41
|
"@types/node": "^20.16.11",
|
|
42
42
|
"graphql": "^16.9.0",
|
|
43
43
|
"pg": "^8.12.0"
|
|
@@ -76,7 +76,7 @@ export class PermissionFactory {
|
|
|
76
76
|
let idPermissions: Acl.Predicate = fieldPermissions[entity.primary] || false
|
|
77
77
|
|
|
78
78
|
for (let predicateReference of predicateReferences) {
|
|
79
|
-
const
|
|
79
|
+
const { predicateDefinition, predicate } = this.mergePredicates(
|
|
80
80
|
entityPredicates,
|
|
81
81
|
idPermissions,
|
|
82
82
|
entityPredicates,
|
|
@@ -118,45 +118,36 @@ export class PermissionFactory {
|
|
|
118
118
|
}
|
|
119
119
|
const noRoot: `${Acl.Operation}`[] = []
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
for (let operation of [
|
|
122
122
|
'create',
|
|
123
123
|
'read',
|
|
124
124
|
'update',
|
|
125
|
-
]
|
|
125
|
+
] as const) {
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
if (leftNoRoot && rightNoRoot) {
|
|
127
|
+
const { predicates: opPredicates, permissions: fieldPermissions, noRoot: opNoRoot } = this.mergeOperationPermissions(left, right, operation)
|
|
128
|
+
|
|
129
|
+
if (opNoRoot) {
|
|
131
130
|
noRoot.push(operation)
|
|
132
131
|
}
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
const rightFieldPermissions: Acl.FieldPermissions = rightNoRoot && !leftNoRoot ? {} : (right.operations[operation] || {})
|
|
136
|
-
const [operationPredicates, fieldPermissions] = this.mergeFieldPermissions(
|
|
137
|
-
left.predicates,
|
|
138
|
-
leftFieldPermissions,
|
|
139
|
-
right.predicates,
|
|
140
|
-
rightFieldPermissions,
|
|
141
|
-
)
|
|
142
|
-
predicates = { ...predicates, ...operationPredicates }
|
|
133
|
+
predicates = { ...predicates, ...opPredicates }
|
|
143
134
|
if (Object.keys(fieldPermissions).length > 0) {
|
|
144
135
|
operations[operation] = fieldPermissions
|
|
145
136
|
}
|
|
146
137
|
}
|
|
147
138
|
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
right.operations.delete || false,
|
|
153
|
-
)
|
|
139
|
+
const { predicateDefinition, predicate, noRoot: opNoRoot } = this.mergeDeletePermissions(left, right)
|
|
140
|
+
if (opNoRoot) {
|
|
141
|
+
noRoot.push('delete')
|
|
142
|
+
}
|
|
154
143
|
if (predicate === true) {
|
|
155
144
|
operations.delete = true
|
|
156
145
|
} else if (predicateDefinition !== undefined && typeof predicate === 'string') {
|
|
157
146
|
predicates[predicate] = predicateDefinition
|
|
158
147
|
operations.delete = predicate
|
|
159
148
|
}
|
|
149
|
+
|
|
150
|
+
|
|
160
151
|
if (noRoot.length > 0) {
|
|
161
152
|
operations.noRoot = noRoot
|
|
162
153
|
}
|
|
@@ -167,62 +158,134 @@ export class PermissionFactory {
|
|
|
167
158
|
}
|
|
168
159
|
}
|
|
169
160
|
|
|
161
|
+
|
|
162
|
+
private mergeOperationPermissions(left: Acl.EntityPermissions, right: Acl.EntityPermissions, operation: 'create' | 'read' | 'update'): {
|
|
163
|
+
noRoot: boolean
|
|
164
|
+
predicates: Acl.PredicateMap
|
|
165
|
+
permissions: Acl.FieldPermissions
|
|
166
|
+
} {
|
|
167
|
+
const { noRoot, leftPermissions, rightPermissions } = this.resolveNoRoot(left, right, operation)
|
|
168
|
+
return {
|
|
169
|
+
noRoot,
|
|
170
|
+
...this.mergeFieldPermissions(
|
|
171
|
+
left.predicates,
|
|
172
|
+
leftPermissions,
|
|
173
|
+
right.predicates,
|
|
174
|
+
rightPermissions,
|
|
175
|
+
),
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private mergeDeletePermissions(left: Acl.EntityPermissions, right: Acl.EntityPermissions): {
|
|
180
|
+
noRoot: boolean
|
|
181
|
+
predicate: Acl.PredicateReference | boolean
|
|
182
|
+
predicateDefinition: Acl.PredicateDefinition | undefined
|
|
183
|
+
} {
|
|
184
|
+
const { noRoot, leftPermissions, rightPermissions } = this.resolveNoRoot(left, right, 'delete')
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
noRoot,
|
|
188
|
+
...this.mergePredicates(
|
|
189
|
+
left.predicates,
|
|
190
|
+
leftPermissions,
|
|
191
|
+
right.predicates,
|
|
192
|
+
rightPermissions,
|
|
193
|
+
),
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private resolveNoRoot<const Op extends`${Acl.Operation}`>(left: Acl.EntityPermissions, right: Acl.EntityPermissions, operation: Op): {
|
|
198
|
+
noRoot: boolean
|
|
199
|
+
leftPermissions: Acl.EntityPermissions['operations'][Op] | undefined
|
|
200
|
+
rightPermissions: Acl.EntityPermissions['operations'][Op] | undefined
|
|
201
|
+
} {
|
|
202
|
+
const leftRootForbidden = left.operations.noRoot?.includes(operation) || false
|
|
203
|
+
const rightRootForbidden = right.operations.noRoot?.includes(operation) || false
|
|
204
|
+
const leftPermissions = left.operations[operation]
|
|
205
|
+
const rightPermissions = right.operations[operation]
|
|
206
|
+
|
|
207
|
+
const rootForbidden = (leftRootForbidden && rightRootForbidden)
|
|
208
|
+
|| (leftRootForbidden && !rightPermissions)
|
|
209
|
+
|| (rightRootForbidden && !leftPermissions)
|
|
210
|
+
|
|
211
|
+
const resolvedLeftPermissions = !rootForbidden && leftRootForbidden ? undefined : leftPermissions
|
|
212
|
+
const resolvedRightPermissions = !rootForbidden && rightRootForbidden ? undefined : rightPermissions
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
noRoot: rootForbidden,
|
|
216
|
+
leftPermissions: resolvedLeftPermissions,
|
|
217
|
+
rightPermissions: resolvedRightPermissions,
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
170
221
|
private mergeFieldPermissions(
|
|
171
222
|
leftPredicates: Acl.PredicateMap,
|
|
172
|
-
leftFieldPermissions: Acl.FieldPermissions,
|
|
223
|
+
leftFieldPermissions: Acl.FieldPermissions | undefined = {},
|
|
173
224
|
rightPredicates: Acl.PredicateMap,
|
|
174
|
-
rightFieldPermissions: Acl.FieldPermissions,
|
|
175
|
-
):
|
|
176
|
-
const
|
|
225
|
+
rightFieldPermissions: Acl.FieldPermissions | undefined = {},
|
|
226
|
+
): {predicates: Acl.PredicateMap; permissions: Acl.FieldPermissions} {
|
|
227
|
+
const permissions: Writable<Acl.FieldPermissions> = {}
|
|
177
228
|
const predicates: Writable<Acl.PredicateMap> = {}
|
|
178
229
|
|
|
179
230
|
for (let field in { ...leftFieldPermissions, ...rightFieldPermissions }) {
|
|
180
|
-
const
|
|
231
|
+
const { predicateDefinition, predicate } = this.mergePredicates(
|
|
181
232
|
leftPredicates,
|
|
182
233
|
leftFieldPermissions[field] || false,
|
|
183
234
|
rightPredicates,
|
|
184
235
|
rightFieldPermissions[field] || false,
|
|
185
236
|
)
|
|
186
237
|
if (predicate === true) {
|
|
187
|
-
|
|
238
|
+
permissions[field] = true
|
|
188
239
|
} else if (predicateDefinition !== undefined && typeof predicate === 'string') {
|
|
189
|
-
|
|
240
|
+
permissions[field] = predicate
|
|
190
241
|
predicates[predicate] = predicateDefinition
|
|
191
242
|
}
|
|
192
243
|
}
|
|
193
244
|
|
|
194
|
-
return
|
|
245
|
+
return { predicates, permissions }
|
|
195
246
|
}
|
|
196
247
|
|
|
197
248
|
private mergePredicates(
|
|
198
249
|
leftPredicates: Acl.PredicateMap,
|
|
199
|
-
leftReference: Acl.Predicate,
|
|
250
|
+
leftReference: Acl.Predicate | undefined = false,
|
|
200
251
|
rightPredicates: Acl.PredicateMap,
|
|
201
|
-
rightReference: Acl.Predicate,
|
|
202
|
-
):
|
|
252
|
+
rightReference: Acl.Predicate | undefined = false,
|
|
253
|
+
): {
|
|
254
|
+
predicate: Acl.PredicateReference | boolean
|
|
255
|
+
predicateDefinition: Acl.PredicateDefinition | undefined
|
|
256
|
+
} {
|
|
203
257
|
if (leftReference === true || rightReference === true) {
|
|
204
|
-
return
|
|
258
|
+
return {
|
|
259
|
+
predicate: true,
|
|
260
|
+
predicateDefinition: undefined,
|
|
261
|
+
}
|
|
205
262
|
}
|
|
206
263
|
|
|
207
264
|
if (leftReference !== false && rightReference !== false) {
|
|
208
265
|
const leftPredicate: Acl.PredicateDefinition = leftPredicates[leftReference]
|
|
209
266
|
const rightPredicate: Acl.PredicateDefinition = rightPredicates[rightReference]
|
|
210
267
|
if (leftPredicate === rightPredicate) {
|
|
211
|
-
return
|
|
268
|
+
return {
|
|
269
|
+
predicate: leftReference,
|
|
270
|
+
predicateDefinition: leftPredicate,
|
|
271
|
+
}
|
|
212
272
|
}
|
|
213
273
|
|
|
214
274
|
let predicateName = '__merge__' + leftReference + '__' + rightReference
|
|
215
275
|
while (leftPredicates[predicateName]) {
|
|
216
276
|
predicateName += '_'
|
|
217
277
|
}
|
|
218
|
-
return
|
|
219
|
-
|
|
278
|
+
return {
|
|
279
|
+
predicate: predicateName,
|
|
280
|
+
predicateDefinition: {
|
|
220
281
|
or: [leftPredicate, rightPredicate],
|
|
221
282
|
} as Acl.PredicateDefinition,
|
|
222
|
-
|
|
223
|
-
]
|
|
283
|
+
}
|
|
224
284
|
} else if (leftReference !== false) {
|
|
225
|
-
return
|
|
285
|
+
return {
|
|
286
|
+
predicate: leftReference,
|
|
287
|
+
predicateDefinition: leftPredicates[leftReference],
|
|
288
|
+
}
|
|
226
289
|
} else if (rightReference !== false) {
|
|
227
290
|
let predicateName = rightReference
|
|
228
291
|
if (rightPredicates !== leftPredicates) {
|
|
@@ -230,9 +293,15 @@ export class PermissionFactory {
|
|
|
230
293
|
predicateName += '_'
|
|
231
294
|
}
|
|
232
295
|
}
|
|
233
|
-
return
|
|
296
|
+
return {
|
|
297
|
+
predicate: predicateName,
|
|
298
|
+
predicateDefinition: rightPredicates[rightReference],
|
|
299
|
+
}
|
|
234
300
|
} else {
|
|
235
|
-
return
|
|
301
|
+
return {
|
|
302
|
+
predicate: false,
|
|
303
|
+
predicateDefinition: undefined,
|
|
304
|
+
}
|
|
236
305
|
}
|
|
237
306
|
}
|
|
238
307
|
}
|
|
@@ -550,6 +550,186 @@ describe('Permission merger', () => {
|
|
|
550
550
|
})
|
|
551
551
|
|
|
552
552
|
|
|
553
|
+
it('merge noRootOperations', () => {
|
|
554
|
+
execute({
|
|
555
|
+
acl: {
|
|
556
|
+
roles: {
|
|
557
|
+
role1: {
|
|
558
|
+
variables: {},
|
|
559
|
+
entities: {
|
|
560
|
+
Entity2: {
|
|
561
|
+
predicates: {},
|
|
562
|
+
operations: {
|
|
563
|
+
read: {
|
|
564
|
+
title: true,
|
|
565
|
+
},
|
|
566
|
+
noRoot: ['read'],
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
},
|
|
570
|
+
},
|
|
571
|
+
role2: {
|
|
572
|
+
variables: {},
|
|
573
|
+
entities: {
|
|
574
|
+
Entity2: {
|
|
575
|
+
predicates: {},
|
|
576
|
+
operations: {
|
|
577
|
+
read: {
|
|
578
|
+
title: true,
|
|
579
|
+
},
|
|
580
|
+
update: {
|
|
581
|
+
title: true,
|
|
582
|
+
},
|
|
583
|
+
noRoot: ['read', 'update'],
|
|
584
|
+
},
|
|
585
|
+
},
|
|
586
|
+
},
|
|
587
|
+
},
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
roles: ['role1', 'role2'],
|
|
591
|
+
result: {
|
|
592
|
+
Entity2: {
|
|
593
|
+
operations: {
|
|
594
|
+
update: {
|
|
595
|
+
id: true,
|
|
596
|
+
title: true,
|
|
597
|
+
},
|
|
598
|
+
read: {
|
|
599
|
+
id: true,
|
|
600
|
+
title: true,
|
|
601
|
+
},
|
|
602
|
+
noRoot: ['read', 'update'],
|
|
603
|
+
},
|
|
604
|
+
predicates: {},
|
|
605
|
+
},
|
|
606
|
+
},
|
|
607
|
+
})
|
|
608
|
+
})
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
it('merge delete #1', () => {
|
|
612
|
+
execute({
|
|
613
|
+
acl: {
|
|
614
|
+
roles: {
|
|
615
|
+
role1: {
|
|
616
|
+
variables: {},
|
|
617
|
+
entities: {
|
|
618
|
+
Entity2: {
|
|
619
|
+
predicates: {},
|
|
620
|
+
operations: {
|
|
621
|
+
delete: true,
|
|
622
|
+
noRoot: ['delete'],
|
|
623
|
+
},
|
|
624
|
+
},
|
|
625
|
+
},
|
|
626
|
+
},
|
|
627
|
+
role2: {
|
|
628
|
+
variables: {},
|
|
629
|
+
entities: {
|
|
630
|
+
Entity2: {
|
|
631
|
+
predicates: {},
|
|
632
|
+
operations: {},
|
|
633
|
+
},
|
|
634
|
+
},
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
},
|
|
638
|
+
roles: ['role1', 'role2'],
|
|
639
|
+
result: {
|
|
640
|
+
Entity2: {
|
|
641
|
+
operations: {
|
|
642
|
+
delete: true,
|
|
643
|
+
noRoot: ['delete'],
|
|
644
|
+
},
|
|
645
|
+
predicates: {},
|
|
646
|
+
},
|
|
647
|
+
},
|
|
648
|
+
})
|
|
649
|
+
})
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
it('merge delete #2', () => {
|
|
653
|
+
execute({
|
|
654
|
+
acl: {
|
|
655
|
+
roles: {
|
|
656
|
+
role1: {
|
|
657
|
+
variables: {},
|
|
658
|
+
entities: {
|
|
659
|
+
Entity2: {
|
|
660
|
+
predicates: {},
|
|
661
|
+
operations: {
|
|
662
|
+
delete: true,
|
|
663
|
+
noRoot: ['delete'],
|
|
664
|
+
},
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
},
|
|
668
|
+
role2: {
|
|
669
|
+
variables: {},
|
|
670
|
+
entities: {
|
|
671
|
+
Entity2: {
|
|
672
|
+
predicates: {},
|
|
673
|
+
operations: {
|
|
674
|
+
delete: true,
|
|
675
|
+
},
|
|
676
|
+
},
|
|
677
|
+
},
|
|
678
|
+
},
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
roles: ['role1', 'role2'],
|
|
682
|
+
result: {
|
|
683
|
+
Entity2: {
|
|
684
|
+
operations: {
|
|
685
|
+
delete: true,
|
|
686
|
+
},
|
|
687
|
+
predicates: {},
|
|
688
|
+
},
|
|
689
|
+
},
|
|
690
|
+
})
|
|
691
|
+
})
|
|
692
|
+
|
|
693
|
+
it('merge delete #2', () => {
|
|
694
|
+
execute({
|
|
695
|
+
acl: {
|
|
696
|
+
roles: {
|
|
697
|
+
role1: {
|
|
698
|
+
variables: {},
|
|
699
|
+
entities: {
|
|
700
|
+
Entity2: {
|
|
701
|
+
predicates: {},
|
|
702
|
+
operations: {
|
|
703
|
+
delete: true,
|
|
704
|
+
noRoot: ['delete'],
|
|
705
|
+
},
|
|
706
|
+
},
|
|
707
|
+
},
|
|
708
|
+
},
|
|
709
|
+
role2: {
|
|
710
|
+
variables: {},
|
|
711
|
+
entities: {
|
|
712
|
+
Entity2: {
|
|
713
|
+
predicates: {},
|
|
714
|
+
operations: {},
|
|
715
|
+
},
|
|
716
|
+
},
|
|
717
|
+
},
|
|
718
|
+
},
|
|
719
|
+
},
|
|
720
|
+
roles: ['role1', 'role2'],
|
|
721
|
+
result: {
|
|
722
|
+
Entity2: {
|
|
723
|
+
operations: {
|
|
724
|
+
delete: true,
|
|
725
|
+
noRoot: ['delete'],
|
|
726
|
+
},
|
|
727
|
+
predicates: {},
|
|
728
|
+
},
|
|
729
|
+
},
|
|
730
|
+
})
|
|
731
|
+
})
|
|
732
|
+
|
|
553
733
|
|
|
554
734
|
it('prefer permissions with root allowed', () => {
|
|
555
735
|
execute({
|