@contember/schema-definition 2.1.0-alpha.25 → 2.1.0-alpha.26

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.
Files changed (34) hide show
  1. package/dist/development/src/acl/definition/internal/AclFactory.cjs +10 -4
  2. package/dist/development/src/acl/definition/internal/AclFactory.cjs.map +1 -1
  3. package/dist/development/src/acl/definition/internal/AclFactory.js +10 -4
  4. package/dist/development/src/acl/definition/internal/AclFactory.js.map +1 -1
  5. package/dist/development/src/acl/definition/internal/stores.cjs.map +1 -1
  6. package/dist/development/src/acl/definition/internal/stores.js.map +1 -1
  7. package/dist/development/src/acl/definition/permissions.cjs +4 -1
  8. package/dist/development/src/acl/definition/permissions.cjs.map +1 -1
  9. package/dist/development/src/acl/definition/permissions.js +4 -1
  10. package/dist/development/src/acl/definition/permissions.js.map +1 -1
  11. package/dist/production/src/acl/definition/internal/AclFactory.cjs +10 -4
  12. package/dist/production/src/acl/definition/internal/AclFactory.cjs.map +1 -1
  13. package/dist/production/src/acl/definition/internal/AclFactory.js +10 -4
  14. package/dist/production/src/acl/definition/internal/AclFactory.js.map +1 -1
  15. package/dist/production/src/acl/definition/internal/stores.cjs.map +1 -1
  16. package/dist/production/src/acl/definition/internal/stores.js.map +1 -1
  17. package/dist/production/src/acl/definition/permissions.cjs +4 -1
  18. package/dist/production/src/acl/definition/permissions.cjs.map +1 -1
  19. package/dist/production/src/acl/definition/permissions.js +4 -1
  20. package/dist/production/src/acl/definition/permissions.js.map +1 -1
  21. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  22. package/dist/types/acl/definition/internal/AclFactory.d.ts +1 -1
  23. package/dist/types/acl/definition/internal/AclFactory.d.ts.map +1 -1
  24. package/dist/types/acl/definition/internal/stores.d.ts +7 -3
  25. package/dist/types/acl/definition/internal/stores.d.ts.map +1 -1
  26. package/dist/types/acl/definition/permissions.d.ts +7 -2
  27. package/dist/types/acl/definition/permissions.d.ts.map +1 -1
  28. package/dist/types/index.d.ts +1 -1
  29. package/dist/types/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +3 -3
  31. package/src/acl/definition/internal/AclFactory.ts +9 -4
  32. package/src/acl/definition/internal/stores.ts +2 -2
  33. package/src/acl/definition/permissions.ts +12 -3
  34. package/tests/cases/unit/readAclDefinitions.test.ts +189 -163
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contember/schema-definition",
3
- "version": "2.1.0-alpha.25",
3
+ "version": "2.1.0-alpha.26",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./dist/production/index.js",
6
6
  "typings": "./dist/types/index.d.ts",
@@ -23,8 +23,8 @@
23
23
  }
24
24
  },
25
25
  "dependencies": {
26
- "@contember/schema": "2.1.0-alpha.25",
27
- "@contember/schema-utils": "2.1.0-alpha.25",
26
+ "@contember/schema": "2.1.0-alpha.26",
27
+ "@contember/schema-utils": "2.1.0-alpha.26",
28
28
  "reflect-metadata": "^0.2.2",
29
29
  "fast-deep-equal": "^3.1.3"
30
30
  },
@@ -25,7 +25,7 @@ export class AclFactory {
25
25
  const roles: Role[] = Object.values(exportedDefinitions).filter(it => it instanceof Role)
26
26
  const variables: VariableDefinition[] = Object.values(exportedDefinitions).filter(it => it instanceof VariableDefinition)
27
27
 
28
- const groupedPermissions = AclFactory.groupPermissions(entityLikeDefinition, roles)
28
+ const groupedPermissions = this.groupPermissions(entityLikeDefinition, roles)
29
29
 
30
30
  return {
31
31
  roles: Object.fromEntries(roles.map((role): [string, Acl.RolePermissions] => {
@@ -118,7 +118,7 @@ export class AclFactory {
118
118
  }))
119
119
  }
120
120
 
121
- private static groupPermissions(entityLikeDefinition: [string, { new(): any }][], roles: Role[]): PermissionsByRoleAndEntity {
121
+ private groupPermissions(entityLikeDefinition: [string, { new(): any }][], roles: Role[]): PermissionsByRoleAndEntity {
122
122
  const groupedPermissions: PermissionsByRoleAndEntity = new Map()
123
123
  for (const [name, entity] of entityLikeDefinition) {
124
124
  const initEntityPermissions = (role: Role): EntityPermissions => {
@@ -131,12 +131,17 @@ export class AclFactory {
131
131
  return entityPermissions
132
132
  }
133
133
  const metadata: EntityPermissionsDefinition[] = allowDefinitionsStore.get(entity)
134
- for (const { role, ...definition } of metadata) {
134
+ for (const { role, factory } of metadata) {
135
135
  if (!roles.includes(role)) {
136
136
  throw `Role ${role.name} used on entity ${name} is not registered. Have you exported it?`
137
137
  }
138
138
  const entityPermissions = initEntityPermissions(role)
139
- entityPermissions.definitions.push(definition)
139
+ const entity = getEntity(this.model, name)
140
+ entityPermissions.definitions.push(factory({
141
+ model: this.model,
142
+ entity: getEntity(this.model, name),
143
+ except: (...fields) => (Object.keys(entity.fields)).filter(it => !fields.includes(it)),
144
+ }))
140
145
  }
141
146
  }
142
147
 
@@ -1,10 +1,10 @@
1
1
  import { createMetadataStore } from '../../../utils/MetadataStore'
2
+ import { AllowDefinitionFactory } from '../permissions'
2
3
  import { Role } from '../roles'
3
- import { AllowDefinition } from '../permissions'
4
4
 
5
5
  export type EntityPermissionsDefinition =
6
- & AllowDefinition<any>
7
6
  & {
7
+ factory: AllowDefinitionFactory<any>
8
8
  role: Role<string>
9
9
  }
10
10
  export const allowDefinitionsStore = createMetadataStore<EntityPermissionsDefinition[]>([])
@@ -1,4 +1,4 @@
1
- import { Acl } from '@contember/schema'
1
+ import { Acl, Model } from '@contember/schema'
2
2
  import { Role } from './roles'
3
3
  import { VariableDefinition } from './variables'
4
4
  import { PredicateEvaluationReference } from './references'
@@ -18,10 +18,19 @@ export interface AllowDefinition<E> {
18
18
  delete?: true
19
19
  }
20
20
 
21
- export const allow = <E, R extends Role<string>>(role: R | R[], args: AllowDefinition<E>): DecoratorFunction<E> => {
21
+ export type AllowDefinitionFactory<E> = (args: {
22
+ model: Model.Schema
23
+ entity: Model.Entity<(keyof E) & string>
24
+ except: (...fields: (keyof E)[]) => (keyof E)[]
25
+ }) => AllowDefinition<E>
26
+
27
+ export const allow = <E, R extends Role<string>>(role: R | R[], args: AllowDefinition<E> | AllowDefinitionFactory<E>): DecoratorFunction<E> => {
22
28
  return (entity: EntityConstructor<E>) => {
23
29
  (Array.isArray(role) ? role : [role]).forEach(role => {
24
- allowDefinitionsStore.update(entity, val => [...val, { ...args, role }])
30
+ allowDefinitionsStore.update(entity, val => [...val, {
31
+ factory: typeof args === 'function' ? args : () => args,
32
+ role,
33
+ }])
25
34
  })
26
35
  }
27
36
  }
@@ -1,6 +1,6 @@
1
1
  import { expect, test } from 'bun:test'
2
2
  import { c, createSchema } from '../../../src'
3
- import { Acl } from '@contember/schema'
3
+ import { Acl, Model } from '@contember/schema'
4
4
 
5
5
  namespace SimpleModel {
6
6
  export const publicRole = c.createRole('public')
@@ -130,24 +130,24 @@ test('definition with update, create, delete predicates', () => {
130
130
  const schema = createSchema(ModelWithModificationAcl)
131
131
  expect(schema.acl.roles.public.entities.Book).toStrictEqual(
132
132
  {
133
- 'operations': {
134
- 'create': {
135
- 'isPublished': 'isPublished_eq_true',
136
- 'title': 'isPublished_eq_true',
137
- },
138
- 'delete': 'isPublished_eq_true',
139
- 'update': {
140
- 'isPublished': 'isPublished_eq_true',
141
- 'title': 'isPublished_eq_true',
142
- },
143
- },
144
- 'predicates': {
145
- 'isPublished_eq_true': {
146
- 'isPublished': {
147
- 'eq': true,
148
- },
149
- },
150
- },
133
+ 'operations': {
134
+ 'create': {
135
+ 'isPublished': 'isPublished_eq_true',
136
+ 'title': 'isPublished_eq_true',
137
+ },
138
+ 'delete': 'isPublished_eq_true',
139
+ 'update': {
140
+ 'isPublished': 'isPublished_eq_true',
141
+ 'title': 'isPublished_eq_true',
142
+ },
143
+ },
144
+ 'predicates': {
145
+ 'isPublished_eq_true': {
146
+ 'isPublished': {
147
+ 'eq': true,
148
+ },
149
+ },
150
+ },
151
151
  },
152
152
  )
153
153
  })
@@ -174,27 +174,27 @@ test('definition with multiple predicates on a single field', () => {
174
174
  const schema = createSchema(ModelWithMultiplePredicates)
175
175
  expect(schema.acl.roles.public.entities.Book).toStrictEqual(
176
176
  {
177
- 'operations': {
178
- 'read': {
179
- 'title': 'or_isPublished_eq_false_isPub',
180
- },
181
- },
182
- 'predicates': {
183
- 'or_isPublished_eq_false_isPub': {
184
- 'or': [
185
- {
186
- 'isPublished': {
187
- 'eq': false,
188
- },
189
- },
190
- {
191
- 'isPublished': {
192
- 'eq': true,
193
- },
194
- },
195
- ],
196
- },
197
- },
177
+ 'operations': {
178
+ 'read': {
179
+ 'title': 'or_isPublished_eq_false_isPub',
180
+ },
181
+ },
182
+ 'predicates': {
183
+ 'or_isPublished_eq_false_isPub': {
184
+ 'or': [
185
+ {
186
+ 'isPublished': {
187
+ 'eq': false,
188
+ },
189
+ },
190
+ {
191
+ 'isPublished': {
192
+ 'eq': true,
193
+ },
194
+ },
195
+ ],
196
+ },
197
+ },
198
198
  },
199
199
  )
200
200
  })
@@ -225,42 +225,42 @@ test('definition with collision', () => {
225
225
  const schema = createSchema(ModelWithJoinedPredicateCollision)
226
226
  expect(schema.acl.roles.public.entities.Book).toStrictEqual(
227
227
  {
228
- 'operations': {
229
- 'read': {
230
- 'isPublishedLoremIpsumDolorSitAmet': 'or_isPublishedLoremIpsumDolor_1',
231
- 'title': 'or_isPublishedLoremIpsumDolor',
232
- },
233
- },
234
- 'predicates': {
235
- 'or_isPublishedLoremIpsumDolor': {
236
- 'or': [
237
- {
238
- 'isPublishedLoremIpsumDolorSitAmet': {
239
- 'eq': false,
240
- },
241
- },
242
- {
243
- 'isPublishedLoremIpsumDolorSitAmet': {
244
- 'eq': true,
245
- },
246
- },
247
- ],
248
- },
249
- 'or_isPublishedLoremIpsumDolor_1': {
250
- 'or': [
251
- {
252
- 'isPublishedLoremIpsumDolorSitAmet': {
253
- 'eq': false,
254
- },
255
- },
256
- {
257
- 'isPublishedLoremIpsumDolorSitAmet': {
258
- 'eq': false,
259
- },
260
- },
261
- ],
262
- },
263
- },
228
+ 'operations': {
229
+ 'read': {
230
+ 'isPublishedLoremIpsumDolorSitAmet': 'or_isPublishedLoremIpsumDolor_1',
231
+ 'title': 'or_isPublishedLoremIpsumDolor',
232
+ },
233
+ },
234
+ 'predicates': {
235
+ 'or_isPublishedLoremIpsumDolor': {
236
+ 'or': [
237
+ {
238
+ 'isPublishedLoremIpsumDolorSitAmet': {
239
+ 'eq': false,
240
+ },
241
+ },
242
+ {
243
+ 'isPublishedLoremIpsumDolorSitAmet': {
244
+ 'eq': true,
245
+ },
246
+ },
247
+ ],
248
+ },
249
+ 'or_isPublishedLoremIpsumDolor_1': {
250
+ 'or': [
251
+ {
252
+ 'isPublishedLoremIpsumDolorSitAmet': {
253
+ 'eq': false,
254
+ },
255
+ },
256
+ {
257
+ 'isPublishedLoremIpsumDolorSitAmet': {
258
+ 'eq': false,
259
+ },
260
+ },
261
+ ],
262
+ },
263
+ },
264
264
  },
265
265
  )
266
266
  })
@@ -283,34 +283,34 @@ test('definition with multiple roles in single predicate', () => {
283
283
  const schema = createSchema(ModelWithMultipleRolesForSinglePredicate)
284
284
  expect(schema.acl.roles).toStrictEqual(
285
285
  {
286
- 'admin': {
287
- 'entities': {
288
- 'Book': {
289
- 'operations': {
290
- 'read': {
291
- 'title': true,
292
- },
293
- },
294
- 'predicates': {},
295
- },
296
- },
297
- 'stages': '*',
298
- 'variables': {},
299
- },
300
- 'public': {
301
- 'entities': {
302
- 'Book': {
303
- 'operations': {
304
- 'read': {
305
- 'title': true,
306
- },
307
- },
308
- 'predicates': {},
309
- },
310
- },
311
- 'stages': '*',
312
- 'variables': {},
313
- },
286
+ 'admin': {
287
+ 'entities': {
288
+ 'Book': {
289
+ 'operations': {
290
+ 'read': {
291
+ 'title': true,
292
+ },
293
+ },
294
+ 'predicates': {},
295
+ },
296
+ },
297
+ 'stages': '*',
298
+ 'variables': {},
299
+ },
300
+ 'public': {
301
+ 'entities': {
302
+ 'Book': {
303
+ 'operations': {
304
+ 'read': {
305
+ 'title': true,
306
+ },
307
+ },
308
+ 'predicates': {},
309
+ },
310
+ },
311
+ 'stages': '*',
312
+ 'variables': {},
313
+ },
314
314
  },
315
315
  )
316
316
  })
@@ -340,36 +340,36 @@ test('definition with predicate references', () => {
340
340
  const schema = createSchema(ModelWithAclPredicateReferences)
341
341
  expect(schema.acl.roles.public.entities).toStrictEqual(
342
342
  {
343
- 'Book': {
344
- 'operations': {
345
- 'read': {
346
- 'title': 'isPublished_eq_true',
347
- },
348
- },
349
- 'predicates': {
350
- 'isPublished_eq_true': {
351
- 'isPublished': {
352
- 'eq': true,
353
- },
354
- },
355
- },
356
- },
357
- 'BookReview': {
358
- 'operations': {
359
- 'read': {
360
- 'content': 'book_isPublished_eq_true',
361
- },
362
- },
363
- 'predicates': {
364
- 'book_isPublished_eq_true': {
365
- 'book': {
366
- 'isPublished': {
367
- 'eq': true,
368
- },
369
- },
370
- },
371
- },
372
- },
343
+ 'Book': {
344
+ 'operations': {
345
+ 'read': {
346
+ 'title': 'isPublished_eq_true',
347
+ },
348
+ },
349
+ 'predicates': {
350
+ 'isPublished_eq_true': {
351
+ 'isPublished': {
352
+ 'eq': true,
353
+ },
354
+ },
355
+ },
356
+ },
357
+ 'BookReview': {
358
+ 'operations': {
359
+ 'read': {
360
+ 'content': 'book_isPublished_eq_true',
361
+ },
362
+ },
363
+ 'predicates': {
364
+ 'book_isPublished_eq_true': {
365
+ 'book': {
366
+ 'isPublished': {
367
+ 'eq': true,
368
+ },
369
+ },
370
+ },
371
+ },
372
+ },
373
373
  },
374
374
  )
375
375
  })
@@ -392,27 +392,27 @@ test('definition with variables', () => {
392
392
  const schema = createSchema(ModelWithVariables)
393
393
  expect(schema.acl.roles.manager).toStrictEqual(
394
394
  {
395
- 'entities': {
396
- 'Book': {
397
- 'operations': {
398
- 'read': {
399
- 'title': 'id_bookId',
400
- },
401
- },
402
- 'predicates': {
403
- 'id_bookId': {
404
- 'id': 'bookId',
405
- },
406
- },
407
- },
408
- },
409
- 'stages': '*',
410
- 'variables': {
411
- 'bookId': {
412
- 'entityName': 'Book',
413
- 'type': Acl.VariableType.entity,
414
- },
415
- },
395
+ 'entities': {
396
+ 'Book': {
397
+ 'operations': {
398
+ 'read': {
399
+ 'title': 'id_bookId',
400
+ },
401
+ },
402
+ 'predicates': {
403
+ 'id_bookId': {
404
+ 'id': 'bookId',
405
+ },
406
+ },
407
+ },
408
+ },
409
+ 'stages': '*',
410
+ 'variables': {
411
+ 'bookId': {
412
+ 'entityName': 'Book',
413
+ 'type': Acl.VariableType.entity,
414
+ },
415
+ },
416
416
  },
417
417
  )
418
418
  })
@@ -430,10 +430,10 @@ test('allow custom primary', () => {
430
430
  const schema = createSchema(ModelWithAllowCustomPrimary)
431
431
  expect(schema.acl.roles.public.entities.Book).toStrictEqual(
432
432
  {
433
- 'operations': {
434
- 'customPrimary': true,
435
- },
436
- 'predicates': {},
433
+ 'operations': {
434
+ 'customPrimary': true,
435
+ },
436
+ 'predicates': {},
437
437
  },
438
438
  )
439
439
  })
@@ -451,10 +451,10 @@ test('allow custom primary', () => {
451
451
  const schema = createSchema(ModelWithAllowCustomPrimaryAllRoles)
452
452
  expect(schema.acl.roles.public.entities.Book).toStrictEqual(
453
453
  {
454
- 'operations': {
455
- 'customPrimary': true,
456
- },
457
- 'predicates': {},
454
+ 'operations': {
455
+ 'customPrimary': true,
456
+ },
457
+ 'predicates': {},
458
458
  },
459
459
  )
460
460
  })
@@ -642,3 +642,29 @@ namespace NoRootInvalid {
642
642
  test('no root - invalid combo', () => {
643
643
  expect(() => createSchema(NoRootInvalid)).toThrow('Operation update cannot be both allowed and disallowed on root on entity Book')
644
644
  })
645
+
646
+
647
+ namespace AllowFactoryModel {
648
+ export const publicRole = c.createRole('public')
649
+
650
+ @c.Allow(publicRole, ({ except }) => ({
651
+ read: except('confidential'),
652
+ }))
653
+ export class Book {
654
+ title = c.stringColumn()
655
+ confidential = c.stringColumn()
656
+ }
657
+ }
658
+
659
+ test('allow with factory', () => {
660
+ const schema = createSchema(AllowFactoryModel)
661
+ expect(schema.acl.roles.public.entities.Book).toStrictEqual({
662
+ 'operations': {
663
+ 'read': {
664
+ 'id': true,
665
+ 'title': true,
666
+ },
667
+ },
668
+ 'predicates': {},
669
+ })
670
+ })