@contember/schema-utils 2.1.0-rc.1 → 2.1.0-rc.3
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/definition-generator/AclDefinitionCodeGenerator.cjs +19 -5
- package/dist/development/src/definition-generator/AclDefinitionCodeGenerator.cjs.map +1 -1
- package/dist/development/src/definition-generator/AclDefinitionCodeGenerator.js +19 -5
- package/dist/development/src/definition-generator/AclDefinitionCodeGenerator.js.map +1 -1
- package/dist/production/src/definition-generator/AclDefinitionCodeGenerator.cjs +19 -5
- package/dist/production/src/definition-generator/AclDefinitionCodeGenerator.cjs.map +1 -1
- package/dist/production/src/definition-generator/AclDefinitionCodeGenerator.js +19 -5
- package/dist/production/src/definition-generator/AclDefinitionCodeGenerator.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/dist/types/definition-generator/AclDefinitionCodeGenerator.d.ts +6 -0
- package/dist/types/definition-generator/AclDefinitionCodeGenerator.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/definition-generator/AclDefinitionCodeGenerator.ts +22 -6
- package/tests/cases/unit/tsDefinitionGenerator.test.ts +37 -1
|
@@ -50,7 +50,7 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
50
50
|
}
|
|
51
51
|
generateEntityAcl({ entity, schema: schema2 }) {
|
|
52
52
|
const aclOutput = [];
|
|
53
|
-
const
|
|
53
|
+
const nonPrimaryFields = Object.keys(entity.fields).filter((it) => it !== entity.primary);
|
|
54
54
|
for (const [roleName, roleDefinition] of Object.entries(schema2.acl.roles)) {
|
|
55
55
|
const entityPermission = roleDefinition.entities[entity.name];
|
|
56
56
|
if (!entityPermission) {
|
|
@@ -61,7 +61,8 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
61
61
|
const operations = this.getMatchingOperations({
|
|
62
62
|
predicate: predicateName,
|
|
63
63
|
operations: entityPermission.operations,
|
|
64
|
-
|
|
64
|
+
entity,
|
|
65
|
+
nonPrimaryFields
|
|
65
66
|
});
|
|
66
67
|
if (Object.keys(operations).length > 0) {
|
|
67
68
|
const processor = new PredicateDefinitionProcessor.PredicateDefinitionProcessor(schema2.model);
|
|
@@ -84,7 +85,8 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
84
85
|
const trueOperations = this.getMatchingOperations({
|
|
85
86
|
predicate: true,
|
|
86
87
|
operations: entityPermission.operations,
|
|
87
|
-
|
|
88
|
+
entity,
|
|
89
|
+
nonPrimaryFields
|
|
88
90
|
});
|
|
89
91
|
if (Object.keys(trueOperations).length > 0) {
|
|
90
92
|
const aclDefinition = printJsValue.printJsValue({ ...trueOperations }, indentFirstLevel);
|
|
@@ -97,12 +99,12 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
97
99
|
}
|
|
98
100
|
return `${aclOutput.join("")}`;
|
|
99
101
|
}
|
|
100
|
-
getMatchingOperations({ operations, predicate,
|
|
102
|
+
getMatchingOperations({ operations, predicate, entity, nonPrimaryFields }) {
|
|
101
103
|
const result = {};
|
|
102
104
|
for (const op of ["read", "create", "update"]) {
|
|
103
105
|
const fields = Object.entries(operations[op] ?? {}).filter(([, it]) => it === predicate).map(([it]) => it);
|
|
104
106
|
if (fields.length === 0) ;
|
|
105
|
-
else if (fields
|
|
107
|
+
else if (this.coversEveryNonPrimaryField(fields, entity, nonPrimaryFields)) {
|
|
106
108
|
result[op] = true;
|
|
107
109
|
} else {
|
|
108
110
|
result[op] = fields;
|
|
@@ -113,6 +115,18 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
113
115
|
}
|
|
114
116
|
return result;
|
|
115
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* `read: true` re-parses as every field except the primary, so it may only stand in for a grant that
|
|
120
|
+
* covers exactly that set. Comparing counts instead treated a grant naming the primary plus all but
|
|
121
|
+
* one field as complete, and regenerating it silently granted the missing field.
|
|
122
|
+
*/
|
|
123
|
+
coversEveryNonPrimaryField(fields, entity, nonPrimaryFields) {
|
|
124
|
+
if (fields.includes(entity.primary)) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
const granted = new Set(fields);
|
|
128
|
+
return nonPrimaryFields.every((it) => granted.has(it));
|
|
129
|
+
}
|
|
116
130
|
}
|
|
117
131
|
exports.AclDefinitionCodeGenerator = AclDefinitionCodeGenerator;
|
|
118
132
|
//# sourceMappingURL=AclDefinitionCodeGenerator.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AclDefinitionCodeGenerator.cjs","sources":["../../../../../packages/schema-utils/src/definition-generator/AclDefinitionCodeGenerator.ts"],"sourcesContent":["import { PredicateDefinitionProcessor } from '../acl'\nimport { IndentDecider, Literal, printJsValue } from '../utils/printJsValue'\nimport { Acl, Model, Schema } from '@contember/schema'\nimport { DefinitionNamingConventions } from './DefinitionNamingConventions'\n\nconst indentFirstLevel: IndentDecider = (val, path) => path.length === 0\n\nexport class AclDefinitionCodeGenerator {\n\tconstructor(\n\t\tprivate readonly definitionNamingConventions = new DefinitionNamingConventions(),\n\t) {\n\t}\n\n\tpublic generateRoles({ acl }: { acl: Acl.Schema }): string {\n\t\treturn Object.entries(acl.roles).map(([name, values]) =>\n\t\t\tthis.generateRole({\n\t\t\t\tname,\n\t\t\t\tvalues,\n\t\t\t})\n\t\t).join('')\n\t}\n\n\tprivate generateRole({ name, values }: { name: string; values: Acl.RolePermissions }): string {\n\t\tconst { variables, entities, inherits, ...other } = values\n\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(name)\n\t\treturn `\\nexport const ${roleVarName} = c.createRole(${printJsValue(name)}, ${printJsValue(other)})\\n`\n\t}\n\n\tpublic generateAclVariables({ acl }: { acl: Acl.Schema }): string {\n\t\tconst variablesOutput: string[] = []\n\n\t\tObject.entries(acl.roles).map(([roleName, roleValues]) => {\n\t\t\tObject.entries(roleValues.variables).map(([variableName, variableValues]) => {\n\t\t\t\tlet variableDefinition = `\\nexport const ${this.definitionNamingConventions.variableVarName(roleName, variableName)} = c.`\n\t\t\t\tconst varFormatted = printJsValue(variableName)\n\t\t\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(roleName)\n\n\t\t\t\tif (variableValues.type === Acl.VariableType.predefined) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.value)\n\t\t\t\t\tvariableDefinition += `createPredefinedVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else if (variableValues.type === Acl.VariableType.entity) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.entityName)\n\t\t\t\t\tvariableDefinition += `createEntityVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`Variable type ${variableValues.type} not yet supported`)\n\t\t\t\t}\n\n\t\t\t\tvariablesOutput.push(variableDefinition)\n\t\t\t})\n\t\t})\n\n\t\treturn variablesOutput.join('')\n\t}\n\n\tpublic generateEntityAcl({ entity, schema }: { entity: Model.Entity; schema: Schema }): string {\n\t\tconst aclOutput: string[] = []\n\t\tconst
|
|
1
|
+
{"version":3,"file":"AclDefinitionCodeGenerator.cjs","sources":["../../../../../packages/schema-utils/src/definition-generator/AclDefinitionCodeGenerator.ts"],"sourcesContent":["import { PredicateDefinitionProcessor } from '../acl'\nimport { IndentDecider, Literal, printJsValue } from '../utils/printJsValue'\nimport { Acl, Model, Schema } from '@contember/schema'\nimport { DefinitionNamingConventions } from './DefinitionNamingConventions'\n\nconst indentFirstLevel: IndentDecider = (val, path) => path.length === 0\n\nexport class AclDefinitionCodeGenerator {\n\tconstructor(\n\t\tprivate readonly definitionNamingConventions = new DefinitionNamingConventions(),\n\t) {\n\t}\n\n\tpublic generateRoles({ acl }: { acl: Acl.Schema }): string {\n\t\treturn Object.entries(acl.roles).map(([name, values]) =>\n\t\t\tthis.generateRole({\n\t\t\t\tname,\n\t\t\t\tvalues,\n\t\t\t})\n\t\t).join('')\n\t}\n\n\tprivate generateRole({ name, values }: { name: string; values: Acl.RolePermissions }): string {\n\t\tconst { variables, entities, inherits, ...other } = values\n\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(name)\n\t\treturn `\\nexport const ${roleVarName} = c.createRole(${printJsValue(name)}, ${printJsValue(other)})\\n`\n\t}\n\n\tpublic generateAclVariables({ acl }: { acl: Acl.Schema }): string {\n\t\tconst variablesOutput: string[] = []\n\n\t\tObject.entries(acl.roles).map(([roleName, roleValues]) => {\n\t\t\tObject.entries(roleValues.variables).map(([variableName, variableValues]) => {\n\t\t\t\tlet variableDefinition = `\\nexport const ${this.definitionNamingConventions.variableVarName(roleName, variableName)} = c.`\n\t\t\t\tconst varFormatted = printJsValue(variableName)\n\t\t\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(roleName)\n\n\t\t\t\tif (variableValues.type === Acl.VariableType.predefined) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.value)\n\t\t\t\t\tvariableDefinition += `createPredefinedVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else if (variableValues.type === Acl.VariableType.entity) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.entityName)\n\t\t\t\t\tvariableDefinition += `createEntityVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`Variable type ${variableValues.type} not yet supported`)\n\t\t\t\t}\n\n\t\t\t\tvariablesOutput.push(variableDefinition)\n\t\t\t})\n\t\t})\n\n\t\treturn variablesOutput.join('')\n\t}\n\n\tpublic generateEntityAcl({ entity, schema }: { entity: Model.Entity; schema: Schema }): string {\n\t\tconst aclOutput: string[] = []\n\t\tconst nonPrimaryFields = Object.keys(entity.fields).filter(it => it !== entity.primary)\n\t\tfor (const [roleName, roleDefinition] of Object.entries(schema.acl.roles)) {\n\t\t\tconst entityPermission = roleDefinition.entities[entity.name]\n\t\t\tif (!entityPermission) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(roleName)\n\t\t\tfor (const [predicateName, predicateDefinition] of Object.entries(entityPermission.predicates)) {\n\t\t\t\tconst operations = this.getMatchingOperations({\n\t\t\t\t\tpredicate: predicateName,\n\t\t\t\t\toperations: entityPermission.operations,\n\t\t\t\t\tentity,\n\t\t\t\t\tnonPrimaryFields,\n\t\t\t\t})\n\t\t\t\tif (Object.keys(operations).length > 0) {\n\t\t\t\t\tconst processor = new PredicateDefinitionProcessor(schema.model)\n\t\t\t\t\tconst when = processor.process(entity, predicateDefinition, {\n\t\t\t\t\t\thandleColumn: ctx => {\n\t\t\t\t\t\t\tif (typeof ctx.value === 'string' && ctx.value in roleDefinition.variables) {\n\t\t\t\t\t\t\t\treturn new Literal(this.definitionNamingConventions.variableVarName(roleName, ctx.value))\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn ctx.value\n\t\t\t\t\t\t},\n\t\t\t\t\t\thandleRelation: ctx => {\n\t\t\t\t\t\t\treturn ctx.value\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\tconst aclDefinition = printJsValue({ when, ...operations }, indentFirstLevel)\n\t\t\t\t\taclOutput.push(`@c.Allow(${roleVarName}, ${aclDefinition})\\n`)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst trueOperations = this.getMatchingOperations({\n\t\t\t\tpredicate: true,\n\t\t\t\toperations: entityPermission.operations,\n\t\t\t\tentity,\n\t\t\t\tnonPrimaryFields,\n\t\t\t})\n\t\t\tif (Object.keys(trueOperations).length > 0) {\n\t\t\t\tconst aclDefinition = printJsValue({ ...trueOperations }, indentFirstLevel)\n\t\t\t\taclOutput.push(`@c.Allow(${roleVarName}, ${aclDefinition})\\n`)\n\t\t\t}\n\t\t}\n\n\t\tif (!aclOutput.length) {\n\t\t\treturn ''\n\t\t}\n\t\treturn `${aclOutput.join('')}`\n\t}\n\n\tprivate getMatchingOperations({ operations, predicate, entity, nonPrimaryFields }: {\n\t\toperations: Acl.EntityOperations\n\t\tpredicate: Acl.Predicate\n\t\tentity: Model.Entity\n\t\tnonPrimaryFields: string[]\n\t}): { read?: string[] | true; create?: string[] | boolean; update?: string[] | boolean; delete?: true } {\n\t\tconst result: ReturnType<AclDefinitionCodeGenerator['getMatchingOperations']> = {}\n\t\tfor (const op of ['read', 'create', 'update'] as const) {\n\t\t\tconst fields = Object.entries(operations[op] ?? {}).filter(([, it]) => it === predicate).map(([it]) => it)\n\t\t\tif (fields.length === 0) {\n\t\t\t} else if (this.coversEveryNonPrimaryField(fields, entity, nonPrimaryFields)) {\n\t\t\t\tresult[op] = true\n\t\t\t} else {\n\t\t\t\tresult[op] = fields\n\t\t\t}\n\t\t}\n\t\tif (operations.delete === predicate) {\n\t\t\tresult.delete = true\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * `read: true` re-parses as every field except the primary, so it may only stand in for a grant that\n\t * covers exactly that set. Comparing counts instead treated a grant naming the primary plus all but\n\t * one field as complete, and regenerating it silently granted the missing field.\n\t */\n\tprivate coversEveryNonPrimaryField(fields: string[], entity: Model.Entity, nonPrimaryFields: string[]): boolean {\n\t\tif (fields.includes(entity.primary)) {\n\t\t\treturn false\n\t\t}\n\t\tconst granted = new Set(fields)\n\t\treturn nonPrimaryFields.every(it => granted.has(it))\n\t}\n}\n"],"names":["DefinitionNamingConventions","printJsValue","Acl","schema","PredicateDefinitionProcessor","Literal"],"mappings":"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"}
|
|
@@ -48,7 +48,7 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
48
48
|
}
|
|
49
49
|
generateEntityAcl({ entity, schema }) {
|
|
50
50
|
const aclOutput = [];
|
|
51
|
-
const
|
|
51
|
+
const nonPrimaryFields = Object.keys(entity.fields).filter((it) => it !== entity.primary);
|
|
52
52
|
for (const [roleName, roleDefinition] of Object.entries(schema.acl.roles)) {
|
|
53
53
|
const entityPermission = roleDefinition.entities[entity.name];
|
|
54
54
|
if (!entityPermission) {
|
|
@@ -59,7 +59,8 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
59
59
|
const operations = this.getMatchingOperations({
|
|
60
60
|
predicate: predicateName,
|
|
61
61
|
operations: entityPermission.operations,
|
|
62
|
-
|
|
62
|
+
entity,
|
|
63
|
+
nonPrimaryFields
|
|
63
64
|
});
|
|
64
65
|
if (Object.keys(operations).length > 0) {
|
|
65
66
|
const processor = new PredicateDefinitionProcessor(schema.model);
|
|
@@ -82,7 +83,8 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
82
83
|
const trueOperations = this.getMatchingOperations({
|
|
83
84
|
predicate: true,
|
|
84
85
|
operations: entityPermission.operations,
|
|
85
|
-
|
|
86
|
+
entity,
|
|
87
|
+
nonPrimaryFields
|
|
86
88
|
});
|
|
87
89
|
if (Object.keys(trueOperations).length > 0) {
|
|
88
90
|
const aclDefinition = printJsValue({ ...trueOperations }, indentFirstLevel);
|
|
@@ -95,12 +97,12 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
95
97
|
}
|
|
96
98
|
return `${aclOutput.join("")}`;
|
|
97
99
|
}
|
|
98
|
-
getMatchingOperations({ operations, predicate,
|
|
100
|
+
getMatchingOperations({ operations, predicate, entity, nonPrimaryFields }) {
|
|
99
101
|
const result = {};
|
|
100
102
|
for (const op of ["read", "create", "update"]) {
|
|
101
103
|
const fields = Object.entries(operations[op] ?? {}).filter(([, it]) => it === predicate).map(([it]) => it);
|
|
102
104
|
if (fields.length === 0) ;
|
|
103
|
-
else if (fields
|
|
105
|
+
else if (this.coversEveryNonPrimaryField(fields, entity, nonPrimaryFields)) {
|
|
104
106
|
result[op] = true;
|
|
105
107
|
} else {
|
|
106
108
|
result[op] = fields;
|
|
@@ -111,6 +113,18 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
111
113
|
}
|
|
112
114
|
return result;
|
|
113
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* `read: true` re-parses as every field except the primary, so it may only stand in for a grant that
|
|
118
|
+
* covers exactly that set. Comparing counts instead treated a grant naming the primary plus all but
|
|
119
|
+
* one field as complete, and regenerating it silently granted the missing field.
|
|
120
|
+
*/
|
|
121
|
+
coversEveryNonPrimaryField(fields, entity, nonPrimaryFields) {
|
|
122
|
+
if (fields.includes(entity.primary)) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
const granted = new Set(fields);
|
|
126
|
+
return nonPrimaryFields.every((it) => granted.has(it));
|
|
127
|
+
}
|
|
114
128
|
}
|
|
115
129
|
export {
|
|
116
130
|
AclDefinitionCodeGenerator
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AclDefinitionCodeGenerator.js","sources":["../../../../../packages/schema-utils/src/definition-generator/AclDefinitionCodeGenerator.ts"],"sourcesContent":["import { PredicateDefinitionProcessor } from '../acl'\nimport { IndentDecider, Literal, printJsValue } from '../utils/printJsValue'\nimport { Acl, Model, Schema } from '@contember/schema'\nimport { DefinitionNamingConventions } from './DefinitionNamingConventions'\n\nconst indentFirstLevel: IndentDecider = (val, path) => path.length === 0\n\nexport class AclDefinitionCodeGenerator {\n\tconstructor(\n\t\tprivate readonly definitionNamingConventions = new DefinitionNamingConventions(),\n\t) {\n\t}\n\n\tpublic generateRoles({ acl }: { acl: Acl.Schema }): string {\n\t\treturn Object.entries(acl.roles).map(([name, values]) =>\n\t\t\tthis.generateRole({\n\t\t\t\tname,\n\t\t\t\tvalues,\n\t\t\t})\n\t\t).join('')\n\t}\n\n\tprivate generateRole({ name, values }: { name: string; values: Acl.RolePermissions }): string {\n\t\tconst { variables, entities, inherits, ...other } = values\n\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(name)\n\t\treturn `\\nexport const ${roleVarName} = c.createRole(${printJsValue(name)}, ${printJsValue(other)})\\n`\n\t}\n\n\tpublic generateAclVariables({ acl }: { acl: Acl.Schema }): string {\n\t\tconst variablesOutput: string[] = []\n\n\t\tObject.entries(acl.roles).map(([roleName, roleValues]) => {\n\t\t\tObject.entries(roleValues.variables).map(([variableName, variableValues]) => {\n\t\t\t\tlet variableDefinition = `\\nexport const ${this.definitionNamingConventions.variableVarName(roleName, variableName)} = c.`\n\t\t\t\tconst varFormatted = printJsValue(variableName)\n\t\t\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(roleName)\n\n\t\t\t\tif (variableValues.type === Acl.VariableType.predefined) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.value)\n\t\t\t\t\tvariableDefinition += `createPredefinedVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else if (variableValues.type === Acl.VariableType.entity) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.entityName)\n\t\t\t\t\tvariableDefinition += `createEntityVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`Variable type ${variableValues.type} not yet supported`)\n\t\t\t\t}\n\n\t\t\t\tvariablesOutput.push(variableDefinition)\n\t\t\t})\n\t\t})\n\n\t\treturn variablesOutput.join('')\n\t}\n\n\tpublic generateEntityAcl({ entity, schema }: { entity: Model.Entity; schema: Schema }): string {\n\t\tconst aclOutput: string[] = []\n\t\tconst
|
|
1
|
+
{"version":3,"file":"AclDefinitionCodeGenerator.js","sources":["../../../../../packages/schema-utils/src/definition-generator/AclDefinitionCodeGenerator.ts"],"sourcesContent":["import { PredicateDefinitionProcessor } from '../acl'\nimport { IndentDecider, Literal, printJsValue } from '../utils/printJsValue'\nimport { Acl, Model, Schema } from '@contember/schema'\nimport { DefinitionNamingConventions } from './DefinitionNamingConventions'\n\nconst indentFirstLevel: IndentDecider = (val, path) => path.length === 0\n\nexport class AclDefinitionCodeGenerator {\n\tconstructor(\n\t\tprivate readonly definitionNamingConventions = new DefinitionNamingConventions(),\n\t) {\n\t}\n\n\tpublic generateRoles({ acl }: { acl: Acl.Schema }): string {\n\t\treturn Object.entries(acl.roles).map(([name, values]) =>\n\t\t\tthis.generateRole({\n\t\t\t\tname,\n\t\t\t\tvalues,\n\t\t\t})\n\t\t).join('')\n\t}\n\n\tprivate generateRole({ name, values }: { name: string; values: Acl.RolePermissions }): string {\n\t\tconst { variables, entities, inherits, ...other } = values\n\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(name)\n\t\treturn `\\nexport const ${roleVarName} = c.createRole(${printJsValue(name)}, ${printJsValue(other)})\\n`\n\t}\n\n\tpublic generateAclVariables({ acl }: { acl: Acl.Schema }): string {\n\t\tconst variablesOutput: string[] = []\n\n\t\tObject.entries(acl.roles).map(([roleName, roleValues]) => {\n\t\t\tObject.entries(roleValues.variables).map(([variableName, variableValues]) => {\n\t\t\t\tlet variableDefinition = `\\nexport const ${this.definitionNamingConventions.variableVarName(roleName, variableName)} = c.`\n\t\t\t\tconst varFormatted = printJsValue(variableName)\n\t\t\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(roleName)\n\n\t\t\t\tif (variableValues.type === Acl.VariableType.predefined) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.value)\n\t\t\t\t\tvariableDefinition += `createPredefinedVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else if (variableValues.type === Acl.VariableType.entity) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.entityName)\n\t\t\t\t\tvariableDefinition += `createEntityVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`Variable type ${variableValues.type} not yet supported`)\n\t\t\t\t}\n\n\t\t\t\tvariablesOutput.push(variableDefinition)\n\t\t\t})\n\t\t})\n\n\t\treturn variablesOutput.join('')\n\t}\n\n\tpublic generateEntityAcl({ entity, schema }: { entity: Model.Entity; schema: Schema }): string {\n\t\tconst aclOutput: string[] = []\n\t\tconst nonPrimaryFields = Object.keys(entity.fields).filter(it => it !== entity.primary)\n\t\tfor (const [roleName, roleDefinition] of Object.entries(schema.acl.roles)) {\n\t\t\tconst entityPermission = roleDefinition.entities[entity.name]\n\t\t\tif (!entityPermission) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(roleName)\n\t\t\tfor (const [predicateName, predicateDefinition] of Object.entries(entityPermission.predicates)) {\n\t\t\t\tconst operations = this.getMatchingOperations({\n\t\t\t\t\tpredicate: predicateName,\n\t\t\t\t\toperations: entityPermission.operations,\n\t\t\t\t\tentity,\n\t\t\t\t\tnonPrimaryFields,\n\t\t\t\t})\n\t\t\t\tif (Object.keys(operations).length > 0) {\n\t\t\t\t\tconst processor = new PredicateDefinitionProcessor(schema.model)\n\t\t\t\t\tconst when = processor.process(entity, predicateDefinition, {\n\t\t\t\t\t\thandleColumn: ctx => {\n\t\t\t\t\t\t\tif (typeof ctx.value === 'string' && ctx.value in roleDefinition.variables) {\n\t\t\t\t\t\t\t\treturn new Literal(this.definitionNamingConventions.variableVarName(roleName, ctx.value))\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn ctx.value\n\t\t\t\t\t\t},\n\t\t\t\t\t\thandleRelation: ctx => {\n\t\t\t\t\t\t\treturn ctx.value\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\tconst aclDefinition = printJsValue({ when, ...operations }, indentFirstLevel)\n\t\t\t\t\taclOutput.push(`@c.Allow(${roleVarName}, ${aclDefinition})\\n`)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst trueOperations = this.getMatchingOperations({\n\t\t\t\tpredicate: true,\n\t\t\t\toperations: entityPermission.operations,\n\t\t\t\tentity,\n\t\t\t\tnonPrimaryFields,\n\t\t\t})\n\t\t\tif (Object.keys(trueOperations).length > 0) {\n\t\t\t\tconst aclDefinition = printJsValue({ ...trueOperations }, indentFirstLevel)\n\t\t\t\taclOutput.push(`@c.Allow(${roleVarName}, ${aclDefinition})\\n`)\n\t\t\t}\n\t\t}\n\n\t\tif (!aclOutput.length) {\n\t\t\treturn ''\n\t\t}\n\t\treturn `${aclOutput.join('')}`\n\t}\n\n\tprivate getMatchingOperations({ operations, predicate, entity, nonPrimaryFields }: {\n\t\toperations: Acl.EntityOperations\n\t\tpredicate: Acl.Predicate\n\t\tentity: Model.Entity\n\t\tnonPrimaryFields: string[]\n\t}): { read?: string[] | true; create?: string[] | boolean; update?: string[] | boolean; delete?: true } {\n\t\tconst result: ReturnType<AclDefinitionCodeGenerator['getMatchingOperations']> = {}\n\t\tfor (const op of ['read', 'create', 'update'] as const) {\n\t\t\tconst fields = Object.entries(operations[op] ?? {}).filter(([, it]) => it === predicate).map(([it]) => it)\n\t\t\tif (fields.length === 0) {\n\t\t\t} else if (this.coversEveryNonPrimaryField(fields, entity, nonPrimaryFields)) {\n\t\t\t\tresult[op] = true\n\t\t\t} else {\n\t\t\t\tresult[op] = fields\n\t\t\t}\n\t\t}\n\t\tif (operations.delete === predicate) {\n\t\t\tresult.delete = true\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * `read: true` re-parses as every field except the primary, so it may only stand in for a grant that\n\t * covers exactly that set. Comparing counts instead treated a grant naming the primary plus all but\n\t * one field as complete, and regenerating it silently granted the missing field.\n\t */\n\tprivate coversEveryNonPrimaryField(fields: string[], entity: Model.Entity, nonPrimaryFields: string[]): boolean {\n\t\tif (fields.includes(entity.primary)) {\n\t\t\treturn false\n\t\t}\n\t\tconst granted = new Set(fields)\n\t\treturn nonPrimaryFields.every(it => granted.has(it))\n\t}\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -50,7 +50,7 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
50
50
|
}
|
|
51
51
|
generateEntityAcl({ entity, schema: schema2 }) {
|
|
52
52
|
const aclOutput = [];
|
|
53
|
-
const
|
|
53
|
+
const nonPrimaryFields = Object.keys(entity.fields).filter((it) => it !== entity.primary);
|
|
54
54
|
for (const [roleName, roleDefinition] of Object.entries(schema2.acl.roles)) {
|
|
55
55
|
const entityPermission = roleDefinition.entities[entity.name];
|
|
56
56
|
if (!entityPermission) {
|
|
@@ -61,7 +61,8 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
61
61
|
const operations = this.getMatchingOperations({
|
|
62
62
|
predicate: predicateName,
|
|
63
63
|
operations: entityPermission.operations,
|
|
64
|
-
|
|
64
|
+
entity,
|
|
65
|
+
nonPrimaryFields
|
|
65
66
|
});
|
|
66
67
|
if (Object.keys(operations).length > 0) {
|
|
67
68
|
const processor = new PredicateDefinitionProcessor.PredicateDefinitionProcessor(schema2.model);
|
|
@@ -84,7 +85,8 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
84
85
|
const trueOperations = this.getMatchingOperations({
|
|
85
86
|
predicate: true,
|
|
86
87
|
operations: entityPermission.operations,
|
|
87
|
-
|
|
88
|
+
entity,
|
|
89
|
+
nonPrimaryFields
|
|
88
90
|
});
|
|
89
91
|
if (Object.keys(trueOperations).length > 0) {
|
|
90
92
|
const aclDefinition = printJsValue.printJsValue({ ...trueOperations }, indentFirstLevel);
|
|
@@ -97,12 +99,12 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
97
99
|
}
|
|
98
100
|
return `${aclOutput.join("")}`;
|
|
99
101
|
}
|
|
100
|
-
getMatchingOperations({ operations, predicate,
|
|
102
|
+
getMatchingOperations({ operations, predicate, entity, nonPrimaryFields }) {
|
|
101
103
|
const result = {};
|
|
102
104
|
for (const op of ["read", "create", "update"]) {
|
|
103
105
|
const fields = Object.entries(operations[op] ?? {}).filter(([, it]) => it === predicate).map(([it]) => it);
|
|
104
106
|
if (fields.length === 0) ;
|
|
105
|
-
else if (fields
|
|
107
|
+
else if (this.coversEveryNonPrimaryField(fields, entity, nonPrimaryFields)) {
|
|
106
108
|
result[op] = true;
|
|
107
109
|
} else {
|
|
108
110
|
result[op] = fields;
|
|
@@ -113,6 +115,18 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
113
115
|
}
|
|
114
116
|
return result;
|
|
115
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* `read: true` re-parses as every field except the primary, so it may only stand in for a grant that
|
|
120
|
+
* covers exactly that set. Comparing counts instead treated a grant naming the primary plus all but
|
|
121
|
+
* one field as complete, and regenerating it silently granted the missing field.
|
|
122
|
+
*/
|
|
123
|
+
coversEveryNonPrimaryField(fields, entity, nonPrimaryFields) {
|
|
124
|
+
if (fields.includes(entity.primary)) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
const granted = new Set(fields);
|
|
128
|
+
return nonPrimaryFields.every((it) => granted.has(it));
|
|
129
|
+
}
|
|
116
130
|
}
|
|
117
131
|
exports.AclDefinitionCodeGenerator = AclDefinitionCodeGenerator;
|
|
118
132
|
//# sourceMappingURL=AclDefinitionCodeGenerator.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AclDefinitionCodeGenerator.cjs","sources":["../../../../../packages/schema-utils/src/definition-generator/AclDefinitionCodeGenerator.ts"],"sourcesContent":["import { PredicateDefinitionProcessor } from '../acl'\nimport { IndentDecider, Literal, printJsValue } from '../utils/printJsValue'\nimport { Acl, Model, Schema } from '@contember/schema'\nimport { DefinitionNamingConventions } from './DefinitionNamingConventions'\n\nconst indentFirstLevel: IndentDecider = (val, path) => path.length === 0\n\nexport class AclDefinitionCodeGenerator {\n\tconstructor(\n\t\tprivate readonly definitionNamingConventions = new DefinitionNamingConventions(),\n\t) {\n\t}\n\n\tpublic generateRoles({ acl }: { acl: Acl.Schema }): string {\n\t\treturn Object.entries(acl.roles).map(([name, values]) =>\n\t\t\tthis.generateRole({\n\t\t\t\tname,\n\t\t\t\tvalues,\n\t\t\t})\n\t\t).join('')\n\t}\n\n\tprivate generateRole({ name, values }: { name: string; values: Acl.RolePermissions }): string {\n\t\tconst { variables, entities, inherits, ...other } = values\n\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(name)\n\t\treturn `\\nexport const ${roleVarName} = c.createRole(${printJsValue(name)}, ${printJsValue(other)})\\n`\n\t}\n\n\tpublic generateAclVariables({ acl }: { acl: Acl.Schema }): string {\n\t\tconst variablesOutput: string[] = []\n\n\t\tObject.entries(acl.roles).map(([roleName, roleValues]) => {\n\t\t\tObject.entries(roleValues.variables).map(([variableName, variableValues]) => {\n\t\t\t\tlet variableDefinition = `\\nexport const ${this.definitionNamingConventions.variableVarName(roleName, variableName)} = c.`\n\t\t\t\tconst varFormatted = printJsValue(variableName)\n\t\t\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(roleName)\n\n\t\t\t\tif (variableValues.type === Acl.VariableType.predefined) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.value)\n\t\t\t\t\tvariableDefinition += `createPredefinedVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else if (variableValues.type === Acl.VariableType.entity) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.entityName)\n\t\t\t\t\tvariableDefinition += `createEntityVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`Variable type ${variableValues.type} not yet supported`)\n\t\t\t\t}\n\n\t\t\t\tvariablesOutput.push(variableDefinition)\n\t\t\t})\n\t\t})\n\n\t\treturn variablesOutput.join('')\n\t}\n\n\tpublic generateEntityAcl({ entity, schema }: { entity: Model.Entity; schema: Schema }): string {\n\t\tconst aclOutput: string[] = []\n\t\tconst
|
|
1
|
+
{"version":3,"file":"AclDefinitionCodeGenerator.cjs","sources":["../../../../../packages/schema-utils/src/definition-generator/AclDefinitionCodeGenerator.ts"],"sourcesContent":["import { PredicateDefinitionProcessor } from '../acl'\nimport { IndentDecider, Literal, printJsValue } from '../utils/printJsValue'\nimport { Acl, Model, Schema } from '@contember/schema'\nimport { DefinitionNamingConventions } from './DefinitionNamingConventions'\n\nconst indentFirstLevel: IndentDecider = (val, path) => path.length === 0\n\nexport class AclDefinitionCodeGenerator {\n\tconstructor(\n\t\tprivate readonly definitionNamingConventions = new DefinitionNamingConventions(),\n\t) {\n\t}\n\n\tpublic generateRoles({ acl }: { acl: Acl.Schema }): string {\n\t\treturn Object.entries(acl.roles).map(([name, values]) =>\n\t\t\tthis.generateRole({\n\t\t\t\tname,\n\t\t\t\tvalues,\n\t\t\t})\n\t\t).join('')\n\t}\n\n\tprivate generateRole({ name, values }: { name: string; values: Acl.RolePermissions }): string {\n\t\tconst { variables, entities, inherits, ...other } = values\n\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(name)\n\t\treturn `\\nexport const ${roleVarName} = c.createRole(${printJsValue(name)}, ${printJsValue(other)})\\n`\n\t}\n\n\tpublic generateAclVariables({ acl }: { acl: Acl.Schema }): string {\n\t\tconst variablesOutput: string[] = []\n\n\t\tObject.entries(acl.roles).map(([roleName, roleValues]) => {\n\t\t\tObject.entries(roleValues.variables).map(([variableName, variableValues]) => {\n\t\t\t\tlet variableDefinition = `\\nexport const ${this.definitionNamingConventions.variableVarName(roleName, variableName)} = c.`\n\t\t\t\tconst varFormatted = printJsValue(variableName)\n\t\t\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(roleName)\n\n\t\t\t\tif (variableValues.type === Acl.VariableType.predefined) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.value)\n\t\t\t\t\tvariableDefinition += `createPredefinedVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else if (variableValues.type === Acl.VariableType.entity) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.entityName)\n\t\t\t\t\tvariableDefinition += `createEntityVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`Variable type ${variableValues.type} not yet supported`)\n\t\t\t\t}\n\n\t\t\t\tvariablesOutput.push(variableDefinition)\n\t\t\t})\n\t\t})\n\n\t\treturn variablesOutput.join('')\n\t}\n\n\tpublic generateEntityAcl({ entity, schema }: { entity: Model.Entity; schema: Schema }): string {\n\t\tconst aclOutput: string[] = []\n\t\tconst nonPrimaryFields = Object.keys(entity.fields).filter(it => it !== entity.primary)\n\t\tfor (const [roleName, roleDefinition] of Object.entries(schema.acl.roles)) {\n\t\t\tconst entityPermission = roleDefinition.entities[entity.name]\n\t\t\tif (!entityPermission) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(roleName)\n\t\t\tfor (const [predicateName, predicateDefinition] of Object.entries(entityPermission.predicates)) {\n\t\t\t\tconst operations = this.getMatchingOperations({\n\t\t\t\t\tpredicate: predicateName,\n\t\t\t\t\toperations: entityPermission.operations,\n\t\t\t\t\tentity,\n\t\t\t\t\tnonPrimaryFields,\n\t\t\t\t})\n\t\t\t\tif (Object.keys(operations).length > 0) {\n\t\t\t\t\tconst processor = new PredicateDefinitionProcessor(schema.model)\n\t\t\t\t\tconst when = processor.process(entity, predicateDefinition, {\n\t\t\t\t\t\thandleColumn: ctx => {\n\t\t\t\t\t\t\tif (typeof ctx.value === 'string' && ctx.value in roleDefinition.variables) {\n\t\t\t\t\t\t\t\treturn new Literal(this.definitionNamingConventions.variableVarName(roleName, ctx.value))\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn ctx.value\n\t\t\t\t\t\t},\n\t\t\t\t\t\thandleRelation: ctx => {\n\t\t\t\t\t\t\treturn ctx.value\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\tconst aclDefinition = printJsValue({ when, ...operations }, indentFirstLevel)\n\t\t\t\t\taclOutput.push(`@c.Allow(${roleVarName}, ${aclDefinition})\\n`)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst trueOperations = this.getMatchingOperations({\n\t\t\t\tpredicate: true,\n\t\t\t\toperations: entityPermission.operations,\n\t\t\t\tentity,\n\t\t\t\tnonPrimaryFields,\n\t\t\t})\n\t\t\tif (Object.keys(trueOperations).length > 0) {\n\t\t\t\tconst aclDefinition = printJsValue({ ...trueOperations }, indentFirstLevel)\n\t\t\t\taclOutput.push(`@c.Allow(${roleVarName}, ${aclDefinition})\\n`)\n\t\t\t}\n\t\t}\n\n\t\tif (!aclOutput.length) {\n\t\t\treturn ''\n\t\t}\n\t\treturn `${aclOutput.join('')}`\n\t}\n\n\tprivate getMatchingOperations({ operations, predicate, entity, nonPrimaryFields }: {\n\t\toperations: Acl.EntityOperations\n\t\tpredicate: Acl.Predicate\n\t\tentity: Model.Entity\n\t\tnonPrimaryFields: string[]\n\t}): { read?: string[] | true; create?: string[] | boolean; update?: string[] | boolean; delete?: true } {\n\t\tconst result: ReturnType<AclDefinitionCodeGenerator['getMatchingOperations']> = {}\n\t\tfor (const op of ['read', 'create', 'update'] as const) {\n\t\t\tconst fields = Object.entries(operations[op] ?? {}).filter(([, it]) => it === predicate).map(([it]) => it)\n\t\t\tif (fields.length === 0) {\n\t\t\t} else if (this.coversEveryNonPrimaryField(fields, entity, nonPrimaryFields)) {\n\t\t\t\tresult[op] = true\n\t\t\t} else {\n\t\t\t\tresult[op] = fields\n\t\t\t}\n\t\t}\n\t\tif (operations.delete === predicate) {\n\t\t\tresult.delete = true\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * `read: true` re-parses as every field except the primary, so it may only stand in for a grant that\n\t * covers exactly that set. Comparing counts instead treated a grant naming the primary plus all but\n\t * one field as complete, and regenerating it silently granted the missing field.\n\t */\n\tprivate coversEveryNonPrimaryField(fields: string[], entity: Model.Entity, nonPrimaryFields: string[]): boolean {\n\t\tif (fields.includes(entity.primary)) {\n\t\t\treturn false\n\t\t}\n\t\tconst granted = new Set(fields)\n\t\treturn nonPrimaryFields.every(it => granted.has(it))\n\t}\n}\n"],"names":["DefinitionNamingConventions","printJsValue","Acl","schema","PredicateDefinitionProcessor","Literal"],"mappings":"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"}
|
|
@@ -48,7 +48,7 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
48
48
|
}
|
|
49
49
|
generateEntityAcl({ entity, schema }) {
|
|
50
50
|
const aclOutput = [];
|
|
51
|
-
const
|
|
51
|
+
const nonPrimaryFields = Object.keys(entity.fields).filter((it) => it !== entity.primary);
|
|
52
52
|
for (const [roleName, roleDefinition] of Object.entries(schema.acl.roles)) {
|
|
53
53
|
const entityPermission = roleDefinition.entities[entity.name];
|
|
54
54
|
if (!entityPermission) {
|
|
@@ -59,7 +59,8 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
59
59
|
const operations = this.getMatchingOperations({
|
|
60
60
|
predicate: predicateName,
|
|
61
61
|
operations: entityPermission.operations,
|
|
62
|
-
|
|
62
|
+
entity,
|
|
63
|
+
nonPrimaryFields
|
|
63
64
|
});
|
|
64
65
|
if (Object.keys(operations).length > 0) {
|
|
65
66
|
const processor = new PredicateDefinitionProcessor(schema.model);
|
|
@@ -82,7 +83,8 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
82
83
|
const trueOperations = this.getMatchingOperations({
|
|
83
84
|
predicate: true,
|
|
84
85
|
operations: entityPermission.operations,
|
|
85
|
-
|
|
86
|
+
entity,
|
|
87
|
+
nonPrimaryFields
|
|
86
88
|
});
|
|
87
89
|
if (Object.keys(trueOperations).length > 0) {
|
|
88
90
|
const aclDefinition = printJsValue({ ...trueOperations }, indentFirstLevel);
|
|
@@ -95,12 +97,12 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
95
97
|
}
|
|
96
98
|
return `${aclOutput.join("")}`;
|
|
97
99
|
}
|
|
98
|
-
getMatchingOperations({ operations, predicate,
|
|
100
|
+
getMatchingOperations({ operations, predicate, entity, nonPrimaryFields }) {
|
|
99
101
|
const result = {};
|
|
100
102
|
for (const op of ["read", "create", "update"]) {
|
|
101
103
|
const fields = Object.entries(operations[op] ?? {}).filter(([, it]) => it === predicate).map(([it]) => it);
|
|
102
104
|
if (fields.length === 0) ;
|
|
103
|
-
else if (fields
|
|
105
|
+
else if (this.coversEveryNonPrimaryField(fields, entity, nonPrimaryFields)) {
|
|
104
106
|
result[op] = true;
|
|
105
107
|
} else {
|
|
106
108
|
result[op] = fields;
|
|
@@ -111,6 +113,18 @@ export const ${this.definitionNamingConventions.variableVarName(roleName, variab
|
|
|
111
113
|
}
|
|
112
114
|
return result;
|
|
113
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* `read: true` re-parses as every field except the primary, so it may only stand in for a grant that
|
|
118
|
+
* covers exactly that set. Comparing counts instead treated a grant naming the primary plus all but
|
|
119
|
+
* one field as complete, and regenerating it silently granted the missing field.
|
|
120
|
+
*/
|
|
121
|
+
coversEveryNonPrimaryField(fields, entity, nonPrimaryFields) {
|
|
122
|
+
if (fields.includes(entity.primary)) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
const granted = new Set(fields);
|
|
126
|
+
return nonPrimaryFields.every((it) => granted.has(it));
|
|
127
|
+
}
|
|
114
128
|
}
|
|
115
129
|
export {
|
|
116
130
|
AclDefinitionCodeGenerator
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AclDefinitionCodeGenerator.js","sources":["../../../../../packages/schema-utils/src/definition-generator/AclDefinitionCodeGenerator.ts"],"sourcesContent":["import { PredicateDefinitionProcessor } from '../acl'\nimport { IndentDecider, Literal, printJsValue } from '../utils/printJsValue'\nimport { Acl, Model, Schema } from '@contember/schema'\nimport { DefinitionNamingConventions } from './DefinitionNamingConventions'\n\nconst indentFirstLevel: IndentDecider = (val, path) => path.length === 0\n\nexport class AclDefinitionCodeGenerator {\n\tconstructor(\n\t\tprivate readonly definitionNamingConventions = new DefinitionNamingConventions(),\n\t) {\n\t}\n\n\tpublic generateRoles({ acl }: { acl: Acl.Schema }): string {\n\t\treturn Object.entries(acl.roles).map(([name, values]) =>\n\t\t\tthis.generateRole({\n\t\t\t\tname,\n\t\t\t\tvalues,\n\t\t\t})\n\t\t).join('')\n\t}\n\n\tprivate generateRole({ name, values }: { name: string; values: Acl.RolePermissions }): string {\n\t\tconst { variables, entities, inherits, ...other } = values\n\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(name)\n\t\treturn `\\nexport const ${roleVarName} = c.createRole(${printJsValue(name)}, ${printJsValue(other)})\\n`\n\t}\n\n\tpublic generateAclVariables({ acl }: { acl: Acl.Schema }): string {\n\t\tconst variablesOutput: string[] = []\n\n\t\tObject.entries(acl.roles).map(([roleName, roleValues]) => {\n\t\t\tObject.entries(roleValues.variables).map(([variableName, variableValues]) => {\n\t\t\t\tlet variableDefinition = `\\nexport const ${this.definitionNamingConventions.variableVarName(roleName, variableName)} = c.`\n\t\t\t\tconst varFormatted = printJsValue(variableName)\n\t\t\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(roleName)\n\n\t\t\t\tif (variableValues.type === Acl.VariableType.predefined) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.value)\n\t\t\t\t\tvariableDefinition += `createPredefinedVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else if (variableValues.type === Acl.VariableType.entity) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.entityName)\n\t\t\t\t\tvariableDefinition += `createEntityVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`Variable type ${variableValues.type} not yet supported`)\n\t\t\t\t}\n\n\t\t\t\tvariablesOutput.push(variableDefinition)\n\t\t\t})\n\t\t})\n\n\t\treturn variablesOutput.join('')\n\t}\n\n\tpublic generateEntityAcl({ entity, schema }: { entity: Model.Entity; schema: Schema }): string {\n\t\tconst aclOutput: string[] = []\n\t\tconst
|
|
1
|
+
{"version":3,"file":"AclDefinitionCodeGenerator.js","sources":["../../../../../packages/schema-utils/src/definition-generator/AclDefinitionCodeGenerator.ts"],"sourcesContent":["import { PredicateDefinitionProcessor } from '../acl'\nimport { IndentDecider, Literal, printJsValue } from '../utils/printJsValue'\nimport { Acl, Model, Schema } from '@contember/schema'\nimport { DefinitionNamingConventions } from './DefinitionNamingConventions'\n\nconst indentFirstLevel: IndentDecider = (val, path) => path.length === 0\n\nexport class AclDefinitionCodeGenerator {\n\tconstructor(\n\t\tprivate readonly definitionNamingConventions = new DefinitionNamingConventions(),\n\t) {\n\t}\n\n\tpublic generateRoles({ acl }: { acl: Acl.Schema }): string {\n\t\treturn Object.entries(acl.roles).map(([name, values]) =>\n\t\t\tthis.generateRole({\n\t\t\t\tname,\n\t\t\t\tvalues,\n\t\t\t})\n\t\t).join('')\n\t}\n\n\tprivate generateRole({ name, values }: { name: string; values: Acl.RolePermissions }): string {\n\t\tconst { variables, entities, inherits, ...other } = values\n\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(name)\n\t\treturn `\\nexport const ${roleVarName} = c.createRole(${printJsValue(name)}, ${printJsValue(other)})\\n`\n\t}\n\n\tpublic generateAclVariables({ acl }: { acl: Acl.Schema }): string {\n\t\tconst variablesOutput: string[] = []\n\n\t\tObject.entries(acl.roles).map(([roleName, roleValues]) => {\n\t\t\tObject.entries(roleValues.variables).map(([variableName, variableValues]) => {\n\t\t\t\tlet variableDefinition = `\\nexport const ${this.definitionNamingConventions.variableVarName(roleName, variableName)} = c.`\n\t\t\t\tconst varFormatted = printJsValue(variableName)\n\t\t\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(roleName)\n\n\t\t\t\tif (variableValues.type === Acl.VariableType.predefined) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.value)\n\t\t\t\t\tvariableDefinition += `createPredefinedVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else if (variableValues.type === Acl.VariableType.entity) {\n\t\t\t\t\tconst varDefinition = printJsValue(variableValues.entityName)\n\t\t\t\t\tvariableDefinition += `createEntityVariable(${varFormatted}, ${varDefinition}, ${roleVarName})\\n`\n\t\t\t\t} else {\n\t\t\t\t\tthrow new Error(`Variable type ${variableValues.type} not yet supported`)\n\t\t\t\t}\n\n\t\t\t\tvariablesOutput.push(variableDefinition)\n\t\t\t})\n\t\t})\n\n\t\treturn variablesOutput.join('')\n\t}\n\n\tpublic generateEntityAcl({ entity, schema }: { entity: Model.Entity; schema: Schema }): string {\n\t\tconst aclOutput: string[] = []\n\t\tconst nonPrimaryFields = Object.keys(entity.fields).filter(it => it !== entity.primary)\n\t\tfor (const [roleName, roleDefinition] of Object.entries(schema.acl.roles)) {\n\t\t\tconst entityPermission = roleDefinition.entities[entity.name]\n\t\t\tif (!entityPermission) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst roleVarName = this.definitionNamingConventions.roleVarName(roleName)\n\t\t\tfor (const [predicateName, predicateDefinition] of Object.entries(entityPermission.predicates)) {\n\t\t\t\tconst operations = this.getMatchingOperations({\n\t\t\t\t\tpredicate: predicateName,\n\t\t\t\t\toperations: entityPermission.operations,\n\t\t\t\t\tentity,\n\t\t\t\t\tnonPrimaryFields,\n\t\t\t\t})\n\t\t\t\tif (Object.keys(operations).length > 0) {\n\t\t\t\t\tconst processor = new PredicateDefinitionProcessor(schema.model)\n\t\t\t\t\tconst when = processor.process(entity, predicateDefinition, {\n\t\t\t\t\t\thandleColumn: ctx => {\n\t\t\t\t\t\t\tif (typeof ctx.value === 'string' && ctx.value in roleDefinition.variables) {\n\t\t\t\t\t\t\t\treturn new Literal(this.definitionNamingConventions.variableVarName(roleName, ctx.value))\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn ctx.value\n\t\t\t\t\t\t},\n\t\t\t\t\t\thandleRelation: ctx => {\n\t\t\t\t\t\t\treturn ctx.value\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\tconst aclDefinition = printJsValue({ when, ...operations }, indentFirstLevel)\n\t\t\t\t\taclOutput.push(`@c.Allow(${roleVarName}, ${aclDefinition})\\n`)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst trueOperations = this.getMatchingOperations({\n\t\t\t\tpredicate: true,\n\t\t\t\toperations: entityPermission.operations,\n\t\t\t\tentity,\n\t\t\t\tnonPrimaryFields,\n\t\t\t})\n\t\t\tif (Object.keys(trueOperations).length > 0) {\n\t\t\t\tconst aclDefinition = printJsValue({ ...trueOperations }, indentFirstLevel)\n\t\t\t\taclOutput.push(`@c.Allow(${roleVarName}, ${aclDefinition})\\n`)\n\t\t\t}\n\t\t}\n\n\t\tif (!aclOutput.length) {\n\t\t\treturn ''\n\t\t}\n\t\treturn `${aclOutput.join('')}`\n\t}\n\n\tprivate getMatchingOperations({ operations, predicate, entity, nonPrimaryFields }: {\n\t\toperations: Acl.EntityOperations\n\t\tpredicate: Acl.Predicate\n\t\tentity: Model.Entity\n\t\tnonPrimaryFields: string[]\n\t}): { read?: string[] | true; create?: string[] | boolean; update?: string[] | boolean; delete?: true } {\n\t\tconst result: ReturnType<AclDefinitionCodeGenerator['getMatchingOperations']> = {}\n\t\tfor (const op of ['read', 'create', 'update'] as const) {\n\t\t\tconst fields = Object.entries(operations[op] ?? {}).filter(([, it]) => it === predicate).map(([it]) => it)\n\t\t\tif (fields.length === 0) {\n\t\t\t} else if (this.coversEveryNonPrimaryField(fields, entity, nonPrimaryFields)) {\n\t\t\t\tresult[op] = true\n\t\t\t} else {\n\t\t\t\tresult[op] = fields\n\t\t\t}\n\t\t}\n\t\tif (operations.delete === predicate) {\n\t\t\tresult.delete = true\n\t\t}\n\n\t\treturn result\n\t}\n\n\t/**\n\t * `read: true` re-parses as every field except the primary, so it may only stand in for a grant that\n\t * covers exactly that set. Comparing counts instead treated a grant naming the primary plus all but\n\t * one field as complete, and regenerating it silently granted the missing field.\n\t */\n\tprivate coversEveryNonPrimaryField(fields: string[], entity: Model.Entity, nonPrimaryFields: string[]): boolean {\n\t\tif (fields.includes(entity.primary)) {\n\t\t\treturn false\n\t\t}\n\t\tconst granted = new Set(fields)\n\t\treturn nonPrimaryFields.every(it => granted.has(it))\n\t}\n}\n"],"names":[],"mappings":"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"}
|