@contember/schema-utils 1.1.0-alpha.4 → 1.1.0-alpha.7

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 (75) hide show
  1. package/dist/src/acl/AllowAllPermissionFactory.d.ts.map +1 -1
  2. package/dist/src/acl/AllowAllPermissionFactory.js.map +1 -1
  3. package/dist/src/acl/index.js +5 -1
  4. package/dist/src/acl/index.js.map +1 -1
  5. package/dist/src/dataUtils.d.ts +2 -2
  6. package/dist/src/dataUtils.d.ts.map +1 -1
  7. package/dist/src/dataUtils.js +1 -1
  8. package/dist/src/dataUtils.js.map +1 -1
  9. package/dist/src/index.d.ts +2 -0
  10. package/dist/src/index.d.ts.map +1 -1
  11. package/dist/src/index.js +25 -2
  12. package/dist/src/index.js.map +1 -1
  13. package/dist/src/model/NamingHelper.d.ts +1 -1
  14. package/dist/src/model/NamingHelper.d.ts.map +1 -1
  15. package/dist/src/model/NamingHelper.js.map +1 -1
  16. package/dist/src/model/index.js +5 -1
  17. package/dist/src/model/index.js.map +1 -1
  18. package/dist/src/tsconfig.tsbuildinfo +1 -1
  19. package/dist/src/type-schema/acl.d.ts +8 -0
  20. package/dist/src/type-schema/acl.d.ts.map +1 -0
  21. package/dist/src/type-schema/acl.js +88 -0
  22. package/dist/src/type-schema/acl.js.map +1 -0
  23. package/dist/src/type-schema/index.d.ts +4 -0
  24. package/dist/src/type-schema/index.d.ts.map +1 -0
  25. package/dist/src/type-schema/index.js +20 -0
  26. package/dist/src/type-schema/index.js.map +1 -0
  27. package/dist/src/type-schema/model.d.ts +30 -0
  28. package/dist/src/type-schema/model.d.ts.map +1 -0
  29. package/dist/src/type-schema/model.js +148 -0
  30. package/dist/src/type-schema/model.js.map +1 -0
  31. package/dist/src/type-schema/validation.d.ts +15 -0
  32. package/dist/src/type-schema/validation.d.ts.map +1 -0
  33. package/dist/src/type-schema/validation.js +95 -0
  34. package/dist/src/type-schema/validation.js.map +1 -0
  35. package/dist/src/utils/index.js +5 -1
  36. package/dist/src/utils/index.js.map +1 -1
  37. package/dist/src/validation/AclValidator.d.ts +1 -2
  38. package/dist/src/validation/AclValidator.d.ts.map +1 -1
  39. package/dist/src/validation/AclValidator.js +22 -197
  40. package/dist/src/validation/AclValidator.js.map +1 -1
  41. package/dist/src/validation/ModelValidator.d.ts +1 -1
  42. package/dist/src/validation/ModelValidator.d.ts.map +1 -1
  43. package/dist/src/validation/ModelValidator.js +48 -219
  44. package/dist/src/validation/ModelValidator.js.map +1 -1
  45. package/dist/src/validation/SchemaValidator.d.ts.map +1 -1
  46. package/dist/src/validation/SchemaValidator.js +5 -19
  47. package/dist/src/validation/SchemaValidator.js.map +1 -1
  48. package/dist/src/validation/ValidationValidator.d.ts +1 -3
  49. package/dist/src/validation/ValidationValidator.d.ts.map +1 -1
  50. package/dist/src/validation/ValidationValidator.js +28 -201
  51. package/dist/src/validation/ValidationValidator.js.map +1 -1
  52. package/dist/src/validation/index.d.ts +2 -0
  53. package/dist/src/validation/index.d.ts.map +1 -1
  54. package/dist/src/validation/index.js +7 -1
  55. package/dist/src/validation/index.js.map +1 -1
  56. package/package.json +3 -2
  57. package/src/acl/AllowAllPermissionFactory.ts +2 -2
  58. package/src/dataUtils.ts +3 -3
  59. package/src/index.ts +8 -0
  60. package/src/model/NamingHelper.ts +1 -1
  61. package/src/tsconfig.json +3 -0
  62. package/src/type-schema/acl.ts +100 -0
  63. package/src/type-schema/index.ts +3 -0
  64. package/src/type-schema/model.ts +173 -0
  65. package/src/type-schema/validation.ts +100 -0
  66. package/src/validation/AclValidator.ts +42 -239
  67. package/src/validation/ModelValidator.ts +63 -263
  68. package/src/validation/SchemaValidator.ts +6 -21
  69. package/src/validation/ValidationValidator.ts +41 -222
  70. package/src/validation/index.ts +2 -0
  71. package/dist/src/validation/utils.d.ts +0 -18
  72. package/dist/src/validation/utils.d.ts.map +0 -1
  73. package/dist/src/validation/utils.js +0 -36
  74. package/dist/src/validation/utils.js.map +0 -1
  75. package/src/validation/utils.ts +0 -45
@@ -0,0 +1,173 @@
1
+ import * as Typesafe from '@contember/typesafe'
2
+ import { Model } from '@contember/schema'
3
+
4
+ const orderBySchema = Typesafe.array(Typesafe.object({
5
+ path: Typesafe.array(Typesafe.string),
6
+ direction: Typesafe.enumeration<Model.OrderDirection>(Model.OrderDirection.asc, Model.OrderDirection.desc),
7
+ }))
8
+ const joiningColumnSchema = Typesafe.object({
9
+ columnName: Typesafe.string,
10
+ onDelete: Typesafe.enumeration<Model.OnDelete>(Model.OnDelete.cascade, Model.OnDelete.restrict, Model.OnDelete.setNull),
11
+ })
12
+ const oneHasManyRelationSchema = Typesafe.intersection(
13
+ Typesafe.object({
14
+ type: Typesafe.literal(Model.RelationType.OneHasMany),
15
+ name: Typesafe.string,
16
+ target: Typesafe.string,
17
+ ownedBy: Typesafe.string,
18
+ }),
19
+ Typesafe.partial({
20
+ orderBy: orderBySchema,
21
+ }),
22
+ )
23
+ const oneHasManyRelationSchemaCheck: Typesafe.Equals<Model.OneHasManyRelation, ReturnType<typeof oneHasManyRelationSchema>> = true
24
+
25
+ const manyHasOneRelationSchema = Typesafe.intersection(
26
+ Typesafe.object({
27
+ type: Typesafe.literal(Model.RelationType.ManyHasOne),
28
+ name: Typesafe.string,
29
+ target: Typesafe.string,
30
+ joiningColumn: joiningColumnSchema,
31
+ nullable: Typesafe.boolean,
32
+ }),
33
+ Typesafe.partial({
34
+ inversedBy: Typesafe.string,
35
+ }),
36
+ )
37
+
38
+ const manyHasOneRelationSchemaCheck: Typesafe.Equals<Model.ManyHasOneRelation, ReturnType<typeof manyHasOneRelationSchema>> = true
39
+
40
+ const oneHasOneInverseRelationSchema = Typesafe.object({
41
+ type: Typesafe.literal(Model.RelationType.OneHasOne),
42
+ name: Typesafe.string,
43
+ target: Typesafe.string,
44
+ ownedBy: Typesafe.string,
45
+ nullable: Typesafe.boolean,
46
+ })
47
+ const oneHasOneInverseRelationSchemaCheck: Typesafe.Equals<Model.OneHasOneInverseRelation, ReturnType<typeof oneHasOneInverseRelationSchema>> = true
48
+
49
+
50
+ const oneHasOneOwningRelationSchema = Typesafe.intersection(
51
+ Typesafe.object({
52
+ type: Typesafe.literal(Model.RelationType.OneHasOne),
53
+ name: Typesafe.string,
54
+ target: Typesafe.string,
55
+ joiningColumn: joiningColumnSchema,
56
+ nullable: Typesafe.boolean,
57
+ }),
58
+ Typesafe.partial({
59
+ inversedBy: Typesafe.string,
60
+ orphanRemoval: Typesafe.literal(true),
61
+ }),
62
+ )
63
+
64
+ const oneHasOneOwningRelationSchemaCheck: Typesafe.Equals<Model.OneHasOneOwningRelation, ReturnType<typeof oneHasOneOwningRelationSchema>> = true
65
+
66
+ const manyHasManyOwningRelationSchema = Typesafe.intersection(
67
+ Typesafe.object({
68
+ type: Typesafe.literal(Model.RelationType.ManyHasMany),
69
+ name: Typesafe.string,
70
+ target: Typesafe.string,
71
+ joiningTable: Typesafe.object({
72
+ tableName: Typesafe.string,
73
+ joiningColumn: joiningColumnSchema,
74
+ inverseJoiningColumn: joiningColumnSchema,
75
+ eventLog: Typesafe.object({
76
+ enabled: Typesafe.boolean,
77
+ }),
78
+ }),
79
+ }),
80
+ Typesafe.partial({
81
+ inversedBy: Typesafe.string,
82
+ orderBy: orderBySchema,
83
+ }),
84
+ )
85
+ const manyHasManyOwningRelationSchemaCheck: Typesafe.Equals<Model.ManyHasManyOwningRelation, ReturnType<typeof manyHasManyOwningRelationSchema>> = true
86
+
87
+ const manyHasManyInverseRelationSchema = Typesafe.intersection(
88
+ Typesafe.object({
89
+ type: Typesafe.literal(Model.RelationType.ManyHasMany),
90
+ name: Typesafe.string,
91
+ target: Typesafe.string,
92
+ ownedBy: Typesafe.string,
93
+ }),
94
+ Typesafe.partial({
95
+ orderBy: orderBySchema,
96
+ }),
97
+ )
98
+ const manyHasManyInverseRelationSchemaCheck: Typesafe.Equals<Model.ManyHasManyInverseRelation, ReturnType<typeof manyHasManyInverseRelationSchema>> = true
99
+
100
+ const intersectionSchema = Typesafe.intersection(
101
+ Typesafe.object({
102
+ precedence: Typesafe.enumeration('ALWAYS', 'BY DEFAULT'),
103
+ }),
104
+ Typesafe.partial({
105
+ start: Typesafe.number,
106
+ }),
107
+ )
108
+ const intersectionSchemaCheck: Typesafe.Equals<Model.AnyColumn['sequence'], ReturnType<typeof intersectionSchema>> = true
109
+
110
+ const columnSchema = Typesafe.intersection(
111
+ Typesafe.object({
112
+ name: Typesafe.string,
113
+ columnName: Typesafe.string,
114
+ columnType: Typesafe.string,
115
+ nullable: Typesafe.boolean,
116
+ type: Typesafe.enumeration<Model.ColumnType>(...Object.values(Model.ColumnType)),
117
+ }),
118
+ Typesafe.partial({
119
+ typeAlias: Typesafe.string,
120
+ default: Typesafe.union(Typesafe.string, Typesafe.number, Typesafe.boolean, Typesafe.null_),
121
+ sequence: intersectionSchema as Typesafe.Type<Model.AnyColumn['sequence']>,
122
+ }),
123
+ )
124
+ const columnSchemaCheck: Typesafe.Equals<Model.AnyColumn, ReturnType<typeof columnSchema>> = true
125
+
126
+ const fieldSchema: Typesafe.Type<Model.AnyField> = Typesafe.union(
127
+ oneHasOneInverseRelationSchema,
128
+ oneHasOneOwningRelationSchema,
129
+ oneHasManyRelationSchema,
130
+ manyHasOneRelationSchema,
131
+ manyHasManyInverseRelationSchema,
132
+ manyHasManyOwningRelationSchema,
133
+ columnSchema,
134
+ )
135
+
136
+ const viewSchemaInner = Typesafe.intersection(
137
+ Typesafe.object({
138
+ sql: Typesafe.string,
139
+ }),
140
+ Typesafe.partial({
141
+ dependencies: Typesafe.array(Typesafe.string),
142
+ }),
143
+ )
144
+ const viewSchemaCheck: Typesafe.Equals<Model.View, ReturnType<typeof viewSchemaInner>> = true
145
+ const viewSchema: Typesafe.Type<Model.View> = viewSchemaInner
146
+
147
+ const entitySchema = Typesafe.intersection(
148
+ Typesafe.object({
149
+ name: Typesafe.string,
150
+ primary: Typesafe.string,
151
+ primaryColumn: Typesafe.string,
152
+ tableName: Typesafe.string,
153
+ fields: Typesafe.record(Typesafe.string, fieldSchema),
154
+ unique: Typesafe.record(Typesafe.string, Typesafe.object({
155
+ fields: Typesafe.array(Typesafe.string),
156
+ name: Typesafe.string,
157
+ })),
158
+ eventLog: Typesafe.object({
159
+ enabled: Typesafe.boolean,
160
+ }),
161
+ }),
162
+ Typesafe.partial({
163
+ view: viewSchema,
164
+ }),
165
+ )
166
+
167
+ const entitySchemaCheck: Typesafe.Equals<Model.Entity, ReturnType<typeof entitySchema>> = true
168
+
169
+ export const modelSchema = Typesafe.object({
170
+ entities: Typesafe.record(Typesafe.string, entitySchema),
171
+ enums: Typesafe.record(Typesafe.string, Typesafe.array(Typesafe.string)),
172
+ })
173
+
@@ -0,0 +1,100 @@
1
+ import * as Typesafe from '@contember/typesafe'
2
+ import { Validation } from '@contember/schema'
3
+
4
+ const messageSchema = Typesafe.intersection(
5
+ Typesafe.object({
6
+ text: Typesafe.string,
7
+ }),
8
+ Typesafe.partial({
9
+ parameters: Typesafe.array(Typesafe.union(Typesafe.string, Typesafe.number)),
10
+ }),
11
+ )
12
+ const literalArgumentFactory = <T extends Typesafe.Json>(inner: Typesafe.Type<T>) => Typesafe.object({
13
+ type: Typesafe.literal(Validation.ArgumentType.literal),
14
+ value: inner,
15
+ })
16
+
17
+ const validatorArgumentSchema = Typesafe.object({
18
+ type: Typesafe.literal(Validation.ArgumentType.validator),
19
+ validator: (v, p): Validation.Validator => validatorSchema(v, p),
20
+ })
21
+ const pathArgumentSchema = Typesafe.object({
22
+ type: Typesafe.literal(Validation.ArgumentType.path),
23
+ path: Typesafe.array(Typesafe.string),
24
+ })
25
+
26
+ const validatorSchema = Typesafe.union(
27
+ Typesafe.object({
28
+ operation: Typesafe.literal('and'),
29
+ args: Typesafe.array(validatorArgumentSchema),
30
+ }),
31
+ Typesafe.object({
32
+ operation: Typesafe.literal('or'),
33
+ args: Typesafe.array(validatorArgumentSchema),
34
+ }),
35
+ Typesafe.object({
36
+ operation: Typesafe.literal('conditional'),
37
+ args: Typesafe.tuple(validatorArgumentSchema, validatorArgumentSchema),
38
+ }),
39
+ Typesafe.object({
40
+ operation: Typesafe.literal('pattern'),
41
+ args: Typesafe.tuple(literalArgumentFactory(Typesafe.tuple(Typesafe.string, Typesafe.string))),
42
+ }),
43
+ Typesafe.object({
44
+ operation: Typesafe.literal('lengthRange'),
45
+ args: Typesafe.tuple(
46
+ literalArgumentFactory(Typesafe.union(Typesafe.null_, Typesafe.number)),
47
+ literalArgumentFactory(Typesafe.union(Typesafe.null_, Typesafe.number)),
48
+ ),
49
+ }),
50
+ Typesafe.object({
51
+ operation: Typesafe.literal('range'),
52
+ args: Typesafe.tuple(
53
+ literalArgumentFactory(Typesafe.union(Typesafe.null_, Typesafe.number)),
54
+ literalArgumentFactory(Typesafe.union(Typesafe.null_, Typesafe.number)),
55
+ ),
56
+ }),
57
+ Typesafe.object({
58
+ operation: Typesafe.literal('equals'),
59
+ args: Typesafe.tuple(literalArgumentFactory(Typesafe.anyJson)),
60
+ }),
61
+ Typesafe.object({
62
+ operation: Typesafe.literal('not'),
63
+ args: Typesafe.tuple(validatorArgumentSchema),
64
+ }),
65
+ Typesafe.object({
66
+ operation: Typesafe.literal('empty'),
67
+ args: Typesafe.tuple(),
68
+ }),
69
+ Typesafe.object({
70
+ operation: Typesafe.literal('defined'),
71
+ args: Typesafe.tuple(),
72
+ }),
73
+ Typesafe.object({
74
+ operation: Typesafe.literal('inContext'),
75
+ args: Typesafe.tuple(pathArgumentSchema, validatorArgumentSchema),
76
+ }),
77
+ Typesafe.object({
78
+ operation: Typesafe.literal('every'),
79
+ args: Typesafe.tuple(validatorArgumentSchema),
80
+ }),
81
+ Typesafe.object({
82
+ operation: Typesafe.literal('any'),
83
+ args: Typesafe.tuple(validatorArgumentSchema),
84
+ }),
85
+ Typesafe.object({
86
+ operation: Typesafe.literal('filter'),
87
+ args: Typesafe.tuple(validatorArgumentSchema, validatorArgumentSchema),
88
+ }),
89
+ )
90
+ const validatorSchemaCheck: Typesafe.Equals<Validation.Validator, ReturnType<typeof validatorSchema>> = true
91
+
92
+ const validationRuleSchema = Typesafe.object({
93
+ validator: validatorSchema as Typesafe.Type<Validation.Validator>,
94
+ message: messageSchema,
95
+ })
96
+
97
+ export const validationSchema = Typesafe.record(
98
+ Typesafe.string,
99
+ Typesafe.record(Typesafe.string, Typesafe.array(validationRuleSchema)),
100
+ )
@@ -1,199 +1,71 @@
1
1
  import { Acl, Model } from '@contember/schema'
2
- import { checkExtraProperties, everyIs, hasStringProperty, isObject } from './utils'
3
2
  import { PredicateDefinitionProcessor } from '../acl'
4
3
  import { getEntity } from '../model'
5
4
  import { ErrorBuilder, ValidationError } from './errors'
6
5
 
6
+
7
7
  export class AclValidator {
8
8
  constructor(private readonly model: Model.Schema) {}
9
9
 
10
- public validate(schema: unknown): [Acl.Schema, ValidationError[]] {
10
+ public validate(schema: Acl.Schema): ValidationError[] {
11
11
  const errorBuilder = new ErrorBuilder([], ['acl'])
12
- let validSchema: Acl.Schema
13
- if (!isObject(schema)) {
14
- errorBuilder.add('Must be an object')
15
- validSchema = { roles: {} }
16
- } else if (!schema.roles) {
17
- errorBuilder.for('roles').add('Property is missing')
18
- validSchema = { roles: {} }
19
- } else {
20
- validSchema = { roles: this.validateRoles(schema.roles, errorBuilder) }
21
- }
22
- return [validSchema, errorBuilder.errors]
12
+ this.validateRoles(schema.roles, errorBuilder)
13
+ return errorBuilder.errors
23
14
  }
24
15
 
25
- private validateRoles(roles: unknown, errorBuilder: ErrorBuilder): Acl.Schema['roles'] {
26
- if (!isObject(roles)) {
27
- errorBuilder.add('Must be an object')
28
- return {}
29
- }
30
- const validRoles: Acl.Schema['roles'] = {}
16
+ private validateRoles(roles: Acl.Roles, errorBuilder: ErrorBuilder): void {
31
17
  for (const role in roles) {
32
18
  if (!roles.hasOwnProperty(role)) {
33
19
  continue
34
20
  }
35
- // if (role === 'admin') {
36
- // errorBuilder.add('Role "admin" is reserved.')
37
- // continue
38
- // }
39
- const rolePermissions = this.validateRolePermissions(roles[role], Object.keys(roles), errorBuilder.for(role))
40
- if (rolePermissions !== undefined) {
41
- validRoles[role] = rolePermissions
42
- }
21
+ this.validateRolePermissions(roles[role], Object.keys(roles), errorBuilder.for(role))
43
22
  }
44
- return validRoles
45
23
  }
46
24
 
47
25
  private validateRolePermissions(
48
- permissions: unknown,
26
+ permissions: Acl.RolePermissions,
49
27
  roles: string[],
50
28
  errorBuilder: ErrorBuilder,
51
- ): Acl.RolePermissions | undefined {
52
- if (!isObject(permissions)) {
53
- errorBuilder.add('Must be an object')
54
- return
55
- }
29
+ ): void {
56
30
 
57
- const {
58
- inherits: inheritsIn,
59
- variables: variablesIn,
60
- stages: stagesIn,
61
- entities: entitiesIn,
62
- implicit: implicitIn,
63
- ...plugins
64
- } = permissions
65
-
66
- const inherits = this.validateInherits(inheritsIn, roles, errorBuilder.for('inherits'))
67
- const variables = this.validateVariables(variablesIn, errorBuilder.for('variables'))
68
- const stages = this.validateStagesDefinition(stagesIn, errorBuilder.for('stages'))
69
- const entities = this.validatePermissions(
70
- entitiesIn,
71
- (permissions.variables as Acl.Variables) || {},
72
- errorBuilder.for('entities'),
73
- )
31
+ this.validateInherits(permissions.inherits, roles, errorBuilder.for('inherits'))
32
+ this.validateVariables(permissions.variables, errorBuilder.for('variables'))
33
+ this.validatePermissions(permissions.entities, (permissions.variables as Acl.Variables) || {}, errorBuilder.for('entities'))
74
34
  // todo: plugins validation
75
-
76
- const result: Acl.RolePermissions = { variables, stages, entities }
77
- if (inherits !== undefined) {
78
- result.inherits = inherits
79
- }
80
- if (implicitIn !== undefined) {
81
- if (typeof implicitIn !== 'boolean') {
82
- errorBuilder.for('implicit').add('Must be boolean')
83
- return
84
- }
85
- result.implicit = implicitIn
86
- }
87
-
88
- return { ...plugins, ...result }
89
35
  }
90
36
 
91
- private validateInherits(inherits: unknown, roles: string[], errorBuilder: ErrorBuilder): string[] | undefined {
37
+ private validateInherits(inherits: Acl.RolePermissions['inherits'], roles: string[], errorBuilder: ErrorBuilder): void {
92
38
  if (inherits === undefined) {
93
39
  return undefined
94
40
  }
95
- if (!Array.isArray(inherits) || !everyIs(inherits, (it): it is string => typeof it === 'string')) {
96
- errorBuilder.add('Must be an array of strings')
97
- return []
98
- } else {
99
- const validRoles: string[] = []
100
- for (const inheritsFrom of inherits) {
101
- if (!roles.includes(inheritsFrom)) {
102
- // todo: check recursion
103
- errorBuilder.for(inheritsFrom).add('Referenced role not exists.')
104
- } else {
105
- validRoles.push(inheritsFrom)
106
- }
41
+ for (const inheritsFrom of inherits) {
42
+ if (!roles.includes(inheritsFrom)) {
43
+ errorBuilder.for(inheritsFrom).add('Referenced role not exists.')
107
44
  }
108
- return validRoles
109
45
  }
110
46
  }
111
47
 
112
- private validateVariables(variables: unknown, errorBuilder: ErrorBuilder): Acl.Variables {
113
- let validVariables: Acl.Variables = {}
114
- if (!isObject(variables)) {
115
- errorBuilder.add('Must be an object')
116
- return {}
117
- }
48
+ private validateVariables(variables: Acl.Variables, errorBuilder: ErrorBuilder): void {
118
49
  for (const varName in variables) {
119
50
  if (!variables.hasOwnProperty(varName)) {
120
51
  continue
121
52
  }
122
- const validVariable = this.validateVariable(variables[varName], errorBuilder.for(varName))
123
- if (validVariable) {
124
- validVariables[varName] = validVariable
125
- }
53
+ this.validateVariable(variables[varName], errorBuilder.for(varName))
126
54
  }
127
- return validVariables
128
55
  }
129
56
 
130
- private validateVariable(variable: unknown, errorBuilder: ErrorBuilder): Acl.Variable | undefined {
131
- if (!isObject(variable)) {
132
- errorBuilder.add('Must be an object')
133
- return
134
- }
135
- if (!hasStringProperty(variable, 'type')) {
136
- errorBuilder.add('Variable type is not defined')
137
- return
138
- }
57
+ private validateVariable(variable: Acl.Variable, errorBuilder: ErrorBuilder): void {
139
58
  switch (variable.type) {
140
- case Acl.VariableType.entity:
141
- if (!hasStringProperty(variable, 'entityName')) {
142
- errorBuilder.add('Entity name must be specified for this type of variable')
143
- return
144
- }
59
+ case 'entity':
145
60
  if (!this.model.entities[variable.entityName]) {
146
61
  errorBuilder.add(`Entity "${variable.entityName}" not found`)
147
62
  return
148
63
  }
149
- const extra = checkExtraProperties(variable, ['entityName', 'type'])
150
- if (extra.length) {
151
- errorBuilder.add('Unsupported properties found: ' + extra.join(', '))
152
- }
153
- return { type: Acl.VariableType.entity, entityName: variable.entityName }
154
-
155
- case Acl.VariableType.predefined:
156
- if (!hasStringProperty(variable, 'value')) {
157
- errorBuilder.add('Variable value type must be specified for this type of variable')
158
- return
159
- }
160
- if (variable.value !== 'identityID' && variable.value !== 'personID') {
161
- errorBuilder.add('Unknown predefined variable')
162
- return
163
- }
164
- const extra2 = checkExtraProperties(variable, ['type', 'value'])
165
- if (extra2.length) {
166
- errorBuilder.add('Unsupported properties found: ' + extra2.join(', '))
167
- }
168
- return { type: variable.type, value: variable.value }
169
- default:
170
- errorBuilder.add(`Variable type "${variable.type}" is not supported.`)
171
64
  }
172
65
  return
173
66
  }
174
67
 
175
- private validateStagesDefinition(stages: unknown, errorBuilder: ErrorBuilder): Acl.StagesDefinition {
176
- if (stages === '*') {
177
- return '*'
178
- }
179
- if (!Array.isArray(stages) || !everyIs(stages, (it): it is string => typeof it === 'string')) {
180
- errorBuilder.add('Stages must be either "*" or array of stage names')
181
- return []
182
- }
183
- // todo validate stages ??
184
- return stages
185
- }
186
-
187
- private validatePermissions(
188
- permissions: unknown,
189
- variables: Acl.Variables,
190
- errorBuilder: ErrorBuilder,
191
- ): Acl.Permissions {
192
- if (!isObject(permissions)) {
193
- errorBuilder.add('Must be an object')
194
- return {}
195
- }
196
- const validPermissions: Acl.Permissions = {}
68
+ private validatePermissions(permissions: Acl.Permissions, variables: Acl.Variables, errorBuilder: ErrorBuilder): void {
197
69
  for (const entityName in permissions) {
198
70
  if (!permissions.hasOwnProperty(entityName)) {
199
71
  continue
@@ -204,161 +76,99 @@ export class AclValidator {
204
76
  }
205
77
  const entity = getEntity(this.model, entityName)
206
78
 
207
- const entityPermissions = this.validateEntityPermissions(
79
+ this.validateEntityPermissions(
208
80
  permissions[entityName],
209
81
  entity,
210
82
  variables,
211
83
  errorBuilder.for(entityName),
212
84
  )
213
- if (entityPermissions) {
214
- validPermissions[entityName] = entityPermissions
215
- }
216
85
  }
217
- return validPermissions
218
86
  }
219
87
 
220
88
  private validateEntityPermissions(
221
- entityPermissions: unknown,
89
+ entityPermissions: Acl.EntityPermissions,
222
90
  entity: Model.Entity,
223
91
  variables: Acl.Variables,
224
92
  errorBuilder: ErrorBuilder,
225
- ): Acl.EntityPermissions | undefined {
226
- if (!isObject(entityPermissions)) {
227
- errorBuilder.add('Must be an object')
228
- return
229
- }
230
- const extra = checkExtraProperties(entityPermissions, ['predicates', 'operations'])
231
- if (extra.length) {
232
- errorBuilder.add('Unsupported properties found: ' + extra.join(', '))
233
- }
234
- const predicates = this.validatePredicates(
93
+ ): void {
94
+ this.validatePredicates(
235
95
  entityPermissions.predicates,
236
96
  entity,
237
97
  variables,
238
98
  errorBuilder.for('predicates'),
239
99
  )
240
- const operations = this.validateOperations(
100
+ this.validateOperations(
241
101
  entityPermissions.operations,
242
102
  entity,
243
103
  entityPermissions.predicates as Acl.PredicateMap,
244
104
  errorBuilder.for('operations'),
245
105
  )
246
-
247
- return { operations, predicates }
248
106
  }
249
107
 
250
- private validatePredicates(
251
- predicates: unknown,
252
- entity: Model.Entity,
253
- variables: Acl.Variables,
254
- errorBuilder: ErrorBuilder,
255
- ): Acl.PredicateMap {
256
- if (!isObject(predicates)) {
257
- errorBuilder.add('Must be an object')
258
- return {}
259
- }
260
- const validPredicates: Acl.PredicateMap = {}
108
+ private validatePredicates(predicates: Acl.PredicateMap, entity: Model.Entity, variables: Acl.Variables, errorBuilder: ErrorBuilder): void {
261
109
  for (const predicateName in predicates) {
262
110
  if (!predicates.hasOwnProperty(predicateName)) {
263
111
  continue
264
112
  }
265
- const predicate = this.validatePredicateDefinition(
113
+ this.validatePredicateDefinition(
266
114
  predicates[predicateName],
267
115
  entity,
268
116
  variables,
269
117
  errorBuilder.for(predicateName),
270
118
  )
271
- if (predicate) {
272
- validPredicates[predicateName] = predicate
273
- }
274
119
  }
275
- return validPredicates
276
120
  }
277
121
 
278
122
  private validatePredicateDefinition(
279
- predicate: unknown,
123
+ predicate: Acl.PredicateDefinition,
280
124
  entity: Model.Entity,
281
125
  variables: Acl.Variables,
282
126
  errorBuilder: ErrorBuilder,
283
- ): Acl.PredicateDefinition | undefined {
284
- if (!isObject(predicate)) {
285
- errorBuilder.add('Must be an object')
286
- return
287
- }
127
+ ): void {
288
128
  const processor = new PredicateDefinitionProcessor(this.model)
289
- let valid = true
290
- const result = processor.process(entity, predicate, {
129
+ processor.process(entity, predicate, {
291
130
  handleColumn: ctx => {
292
131
  if (typeof ctx.value === 'string' && !variables[ctx.value]) {
293
- valid = false
294
132
  errorBuilder.for(...ctx.path).add(`Undefined variable ${ctx.value}`)
295
133
  }
296
134
  return ctx.value
297
135
  },
298
136
  handleRelation: ctx => {
299
137
  if (typeof ctx.value === 'string' && !variables[ctx.value]) {
300
- valid = false
301
138
  errorBuilder.for(...ctx.path).add(`Undefined variable ${ctx.value}`)
302
139
  }
303
140
  return ctx.value
304
141
  },
305
142
  handleUndefinedField: ctx => {
306
143
  errorBuilder.for(...ctx.path).add(`Undefined field ${ctx.name} on entity ${ctx.entity.name}`)
307
- valid = false
308
144
  return ctx.value
309
145
  },
310
146
  })
311
- if (!valid) {
312
- return
313
- }
314
-
315
- return result
316
147
  }
317
148
 
318
149
  private validateOperations(
319
- operations: unknown,
150
+ operations: Acl.EntityOperations,
320
151
  entity: Model.Entity,
321
152
  predicates: Acl.PredicateDefinition,
322
153
  errorBuilder: ErrorBuilder,
323
- ): Acl.EntityOperations {
324
- if (!isObject(operations)) {
325
- errorBuilder.add('Must be an object')
326
- return {}
327
- }
328
- const extra = checkExtraProperties(operations, ['read', 'create', 'update', 'delete', 'customPrimary'])
329
- if (extra.length) {
330
- errorBuilder.add('Unsupported properties found: ' + extra.join(', '))
331
- }
332
- const validOperations: Acl.EntityOperations = {}
154
+ ): void {
333
155
  if (operations.delete !== undefined) {
334
- validOperations.delete = this.validatePredicate(operations.delete, predicates, errorBuilder.for('delete'))
156
+ this.validatePredicate(operations.delete, predicates, errorBuilder.for('delete'))
335
157
  }
336
158
  for (const op of ['read', 'create', 'update'] as const) {
337
- if (operations[op]) {
338
- validOperations[op] = this.validateFieldPermissions(operations[op], entity, predicates, errorBuilder.for(op))
339
- }
340
- }
341
- if (operations.customPrimary !== undefined) {
342
- if (typeof operations.customPrimary !== 'boolean') {
343
- errorBuilder.for('customPrimary').add('Must be boolean')
159
+ const operation = operations[op]
160
+ if (operation !== undefined) {
161
+ this.validateFieldPermissions(operation, entity, predicates, errorBuilder.for(op))
344
162
  }
345
- validOperations.customPrimary = operations.customPrimary as boolean
346
163
  }
347
-
348
- return validOperations
349
164
  }
350
165
 
351
166
  private validateFieldPermissions(
352
- permissions: unknown,
167
+ permissions: Acl.FieldPermissions,
353
168
  entity: Model.Entity,
354
169
  predicates: Acl.PredicateDefinition,
355
170
  errorBuilder: ErrorBuilder,
356
- ): Acl.FieldPermissions {
357
- if (!isObject(permissions)) {
358
- errorBuilder.add('Must be an object')
359
- return {}
360
- }
361
- const valid: Acl.FieldPermissions = {}
171
+ ): void {
362
172
  for (const field in permissions) {
363
173
  if (!permissions.hasOwnProperty(field)) {
364
174
  continue
@@ -366,27 +176,20 @@ export class AclValidator {
366
176
  if (!entity.fields[field]) {
367
177
  errorBuilder.add(`Field ${field} not found on entity ${entity.name}`)
368
178
  }
369
- valid[field] = this.validatePredicate(permissions[field], predicates, errorBuilder.for(field))
179
+ this.validatePredicate(permissions[field], predicates, errorBuilder.for(field))
370
180
  }
371
- return valid
372
181
  }
373
182
 
374
183
  private validatePredicate(
375
- predicate: unknown,
184
+ predicate: Acl.Predicate,
376
185
  predicates: Acl.PredicateDefinition,
377
186
  errorBuilder: ErrorBuilder,
378
- ): Acl.Predicate {
187
+ ): void {
379
188
  if (predicate === false || predicate === true) {
380
- return predicate
381
- }
382
- if (typeof predicate !== 'string') {
383
- errorBuilder.add('Predicate must be either boolean or predicate reference')
384
- return false
189
+ return
385
190
  }
386
191
  if (!predicates[predicate]) {
387
192
  errorBuilder.add(`Predicate ${predicate} not found`)
388
- return false
389
193
  }
390
- return predicate
391
194
  }
392
195
  }