@contember/schema-utils 2.2.0-alpha.3 → 2.2.0-alpha.6

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 (67) hide show
  1. package/dist/development/src/acl/schemaUtils.cjs +4 -1
  2. package/dist/development/src/acl/schemaUtils.cjs.map +1 -1
  3. package/dist/development/src/acl/schemaUtils.js +4 -1
  4. package/dist/development/src/acl/schemaUtils.js.map +1 -1
  5. package/dist/development/src/definition-generator/AclDefinitionCodeGenerator.cjs +19 -5
  6. package/dist/development/src/definition-generator/AclDefinitionCodeGenerator.cjs.map +1 -1
  7. package/dist/development/src/definition-generator/AclDefinitionCodeGenerator.js +19 -5
  8. package/dist/development/src/definition-generator/AclDefinitionCodeGenerator.js.map +1 -1
  9. package/dist/development/src/validation/MembershipResolver.cjs +3 -2
  10. package/dist/development/src/validation/MembershipResolver.cjs.map +1 -1
  11. package/dist/development/src/validation/MembershipResolver.js +3 -2
  12. package/dist/development/src/validation/MembershipResolver.js.map +1 -1
  13. package/dist/production/src/acl/schemaUtils.cjs +4 -1
  14. package/dist/production/src/acl/schemaUtils.cjs.map +1 -1
  15. package/dist/production/src/acl/schemaUtils.js +4 -1
  16. package/dist/production/src/acl/schemaUtils.js.map +1 -1
  17. package/dist/production/src/definition-generator/AclDefinitionCodeGenerator.cjs +19 -5
  18. package/dist/production/src/definition-generator/AclDefinitionCodeGenerator.cjs.map +1 -1
  19. package/dist/production/src/definition-generator/AclDefinitionCodeGenerator.js +19 -5
  20. package/dist/production/src/definition-generator/AclDefinitionCodeGenerator.js.map +1 -1
  21. package/dist/production/src/validation/MembershipResolver.cjs +3 -2
  22. package/dist/production/src/validation/MembershipResolver.cjs.map +1 -1
  23. package/dist/production/src/validation/MembershipResolver.js +3 -2
  24. package/dist/production/src/validation/MembershipResolver.js.map +1 -1
  25. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  26. package/dist/types/acl/AllowAllPermissionFactory.d.ts.map +1 -1
  27. package/dist/types/acl/PredicateDefinitionProcessor.d.ts.map +1 -1
  28. package/dist/types/acl/schemaUtils.d.ts.map +1 -1
  29. package/dist/types/dataUtils.d.ts +1 -1
  30. package/dist/types/dataUtils.d.ts.map +1 -1
  31. package/dist/types/definition-generator/AclDefinitionCodeGenerator.d.ts +6 -0
  32. package/dist/types/definition-generator/AclDefinitionCodeGenerator.d.ts.map +1 -1
  33. package/dist/types/definition-generator/DefinitionCodeGenerator.d.ts.map +1 -1
  34. package/dist/types/definition-generator/DefinitionNamingConventions.d.ts.map +1 -1
  35. package/dist/types/definition-generator/TsDefinitionGenerator.d.ts.map +1 -1
  36. package/dist/types/json-schema/collectUnsupportedKeywords.d.ts.map +1 -1
  37. package/dist/types/json-schema/jsonSchemaToTsType.d.ts.map +1 -1
  38. package/dist/types/json-schema/keywords.d.ts +2 -2
  39. package/dist/types/json-schema/keywords.d.ts.map +1 -1
  40. package/dist/types/lax/LaxSchemaConverter.d.ts.map +1 -1
  41. package/dist/types/lax/schema.d.ts +87 -87
  42. package/dist/types/model/NamingConventions.d.ts.map +1 -1
  43. package/dist/types/model/modelUtils.d.ts.map +1 -1
  44. package/dist/types/model/resolveDefaultColumnType.d.ts.map +1 -1
  45. package/dist/types/schemaChecksum.d.ts.map +1 -1
  46. package/dist/types/schemaFilter.d.ts.map +1 -1
  47. package/dist/types/schemaNormalizer.d.ts.map +1 -1
  48. package/dist/types/tsconfig.tsbuildinfo +1 -1
  49. package/dist/types/type-schema/condition.d.ts.map +1 -1
  50. package/dist/types/type-schema/settings.d.ts +3 -3
  51. package/dist/types/type-schema/where.d.ts.map +1 -1
  52. package/dist/types/utils/deepCompare.d.ts.map +1 -1
  53. package/dist/types/utils/isIt.d.ts.map +1 -1
  54. package/dist/types/utils/printJsValue.d.ts.map +1 -1
  55. package/dist/types/validation/AclValidator.d.ts.map +1 -1
  56. package/dist/types/validation/ActionsValidator.d.ts.map +1 -1
  57. package/dist/types/validation/MembershipResolver.d.ts.map +1 -1
  58. package/dist/types/validation/ModelValidator.d.ts.map +1 -1
  59. package/dist/types/validation/SchemaValidator.d.ts.map +1 -1
  60. package/dist/types/validation/ValidationValidator.d.ts.map +1 -1
  61. package/dist/types/validation/errors.d.ts.map +1 -1
  62. package/package.json +4 -4
  63. package/src/acl/schemaUtils.ts +8 -1
  64. package/src/definition-generator/AclDefinitionCodeGenerator.ts +22 -6
  65. package/src/validation/MembershipResolver.ts +5 -2
  66. package/tests/cases/unit/membershipResolver.test.ts +36 -1
  67. package/tests/cases/unit/tsDefinitionGenerator.test.ts +36 -0
@@ -1,7 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const getRoleVariables = (roleName, schema) => {
4
- const role = schema.roles[roleName];
4
+ const role = Object.prototype.hasOwnProperty.call(schema.roles, roleName) ? schema.roles[roleName] : void 0;
5
+ if (!role) {
6
+ return {};
7
+ }
5
8
  return (role.inherits || []).reduce(
6
9
  (acc, inheritsFrom) => ({ ...getRoleVariables(inheritsFrom, schema), ...acc }),
7
10
  role.variables
@@ -1 +1 @@
1
- {"version":3,"file":"schemaUtils.cjs","sources":["../../../../../packages/schema-utils/src/acl/schemaUtils.ts"],"sourcesContent":["import { Acl } from '@contember/schema'\n\nexport const getRoleVariables = (roleName: string, schema: Acl.Schema): Acl.Variables => {\n\tconst role = schema.roles[roleName]\n\treturn (role.inherits || []).reduce<Acl.Variables>(\n\t\t(acc, inheritsFrom) => ({ ...getRoleVariables(inheritsFrom, schema), ...acc }),\n\t\trole.variables,\n\t)\n}\n"],"names":[],"mappings":";;AAEO,MAAM,mBAAmB,CAAC,UAAkB,WAAsC;AACxF,QAAM,OAAO,OAAO,MAAM,QAAQ;AAClC,UAAQ,KAAK,YAAY,CAAA,GAAI;AAAA,IAC5B,CAAC,KAAK,kBAAkB,EAAE,GAAG,iBAAiB,cAAc,MAAM,GAAG,GAAG;IACxE,KAAK;AAAA,EAAA;AAEP;;"}
1
+ {"version":3,"file":"schemaUtils.cjs","sources":["../../../../../packages/schema-utils/src/acl/schemaUtils.ts"],"sourcesContent":["import { Acl } from '@contember/schema'\n\nexport const getRoleVariables = (roleName: string, schema: Acl.Schema): Acl.Variables => {\n\t// Own-property lookup: a role name like `__proto__` / `constructor`, or an `inherits` entry pointing\n\t// at a role absent from the schema, must resolve to \"no such role\" (→ no variables) instead of an\n\t// inherited Object.prototype member — otherwise `role.inherits` / `role.variables` below throw on a\n\t// non-role value (e.g. a sign-in granting such a role would roll back the whole transaction).\n\tconst role = Object.prototype.hasOwnProperty.call(schema.roles, roleName) ? schema.roles[roleName] : undefined\n\tif (!role) {\n\t\treturn {}\n\t}\n\treturn (role.inherits || []).reduce<Acl.Variables>(\n\t\t(acc, inheritsFrom) => ({ ...getRoleVariables(inheritsFrom, schema), ...acc }),\n\t\trole.variables,\n\t)\n}\n"],"names":[],"mappings":";;AAEO,MAAM,mBAAmB,CAAC,UAAkB,WAAsC;AAKxF,QAAM,OAAO,OAAO,UAAU,eAAe,KAAK,OAAO,OAAO,QAAQ,IAAI,OAAO,MAAM,QAAQ,IAAI;AACrG,MAAI,CAAC,MAAM;AACV,WAAO,CAAA;AAAA,EACR;AACA,UAAQ,KAAK,YAAY,CAAA,GAAI;AAAA,IAC5B,CAAC,KAAK,kBAAkB,EAAE,GAAG,iBAAiB,cAAc,MAAM,GAAG,GAAG;IACxE,KAAK;AAAA,EAAA;AAEP;;"}
@@ -1,5 +1,8 @@
1
1
  const getRoleVariables = (roleName, schema) => {
2
- const role = schema.roles[roleName];
2
+ const role = Object.prototype.hasOwnProperty.call(schema.roles, roleName) ? schema.roles[roleName] : void 0;
3
+ if (!role) {
4
+ return {};
5
+ }
3
6
  return (role.inherits || []).reduce(
4
7
  (acc, inheritsFrom) => ({ ...getRoleVariables(inheritsFrom, schema), ...acc }),
5
8
  role.variables
@@ -1 +1 @@
1
- {"version":3,"file":"schemaUtils.js","sources":["../../../../../packages/schema-utils/src/acl/schemaUtils.ts"],"sourcesContent":["import { Acl } from '@contember/schema'\n\nexport const getRoleVariables = (roleName: string, schema: Acl.Schema): Acl.Variables => {\n\tconst role = schema.roles[roleName]\n\treturn (role.inherits || []).reduce<Acl.Variables>(\n\t\t(acc, inheritsFrom) => ({ ...getRoleVariables(inheritsFrom, schema), ...acc }),\n\t\trole.variables,\n\t)\n}\n"],"names":[],"mappings":"AAEO,MAAM,mBAAmB,CAAC,UAAkB,WAAsC;AACxF,QAAM,OAAO,OAAO,MAAM,QAAQ;AAClC,UAAQ,KAAK,YAAY,CAAA,GAAI;AAAA,IAC5B,CAAC,KAAK,kBAAkB,EAAE,GAAG,iBAAiB,cAAc,MAAM,GAAG,GAAG;IACxE,KAAK;AAAA,EAAA;AAEP;"}
1
+ {"version":3,"file":"schemaUtils.js","sources":["../../../../../packages/schema-utils/src/acl/schemaUtils.ts"],"sourcesContent":["import { Acl } from '@contember/schema'\n\nexport const getRoleVariables = (roleName: string, schema: Acl.Schema): Acl.Variables => {\n\t// Own-property lookup: a role name like `__proto__` / `constructor`, or an `inherits` entry pointing\n\t// at a role absent from the schema, must resolve to \"no such role\" (→ no variables) instead of an\n\t// inherited Object.prototype member — otherwise `role.inherits` / `role.variables` below throw on a\n\t// non-role value (e.g. a sign-in granting such a role would roll back the whole transaction).\n\tconst role = Object.prototype.hasOwnProperty.call(schema.roles, roleName) ? schema.roles[roleName] : undefined\n\tif (!role) {\n\t\treturn {}\n\t}\n\treturn (role.inherits || []).reduce<Acl.Variables>(\n\t\t(acc, inheritsFrom) => ({ ...getRoleVariables(inheritsFrom, schema), ...acc }),\n\t\trole.variables,\n\t)\n}\n"],"names":[],"mappings":"AAEO,MAAM,mBAAmB,CAAC,UAAkB,WAAsC;AAKxF,QAAM,OAAO,OAAO,UAAU,eAAe,KAAK,OAAO,OAAO,QAAQ,IAAI,OAAO,MAAM,QAAQ,IAAI;AACrG,MAAI,CAAC,MAAM;AACV,WAAO,CAAA;AAAA,EACR;AACA,UAAQ,KAAK,YAAY,CAAA,GAAI;AAAA,IAC5B,CAAC,KAAK,kBAAkB,EAAE,GAAG,iBAAiB,cAAc,MAAM,GAAG,GAAG;IACxE,KAAK;AAAA,EAAA;AAEP;"}
@@ -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 numberOfEntityFieldsWithoutId = Object.keys(entity.fields).length - 1;
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
- numberOfEntityFieldsWithoutId
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
- numberOfEntityFieldsWithoutId
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, numberOfEntityFieldsWithoutId }) {
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.length === numberOfEntityFieldsWithoutId) {
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/index.js'\nimport { IndentDecider, Literal, printJsValue } from '../utils/printJsValue.js'\nimport { Acl, Model, Schema } from '@contember/schema'\nimport { DefinitionNamingConventions } from './DefinitionNamingConventions.js'\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 numberOfEntityFieldsWithoutId = Object.keys(entity.fields).length - 1\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\tnumberOfEntityFieldsWithoutId,\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\tnumberOfEntityFieldsWithoutId,\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, numberOfEntityFieldsWithoutId }: {\n\t\toperations: Acl.EntityOperations\n\t\tpredicate: Acl.Predicate\n\t\tnumberOfEntityFieldsWithoutId: number\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 (fields.length === numberOfEntityFieldsWithoutId) {\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"],"names":["DefinitionNamingConventions","printJsValue","Acl","schema","PredicateDefinitionProcessor","Literal"],"mappings":"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"}
1
+ {"version":3,"file":"AclDefinitionCodeGenerator.cjs","sources":["../../../../../packages/schema-utils/src/definition-generator/AclDefinitionCodeGenerator.ts"],"sourcesContent":["import { PredicateDefinitionProcessor } from '../acl/index.js'\nimport { IndentDecider, Literal, printJsValue } from '../utils/printJsValue.js'\nimport { Acl, Model, Schema } from '@contember/schema'\nimport { DefinitionNamingConventions } from './DefinitionNamingConventions.js'\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 numberOfEntityFieldsWithoutId = Object.keys(entity.fields).length - 1;
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
- numberOfEntityFieldsWithoutId
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
- numberOfEntityFieldsWithoutId
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, numberOfEntityFieldsWithoutId }) {
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.length === numberOfEntityFieldsWithoutId) {
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/index.js'\nimport { IndentDecider, Literal, printJsValue } from '../utils/printJsValue.js'\nimport { Acl, Model, Schema } from '@contember/schema'\nimport { DefinitionNamingConventions } from './DefinitionNamingConventions.js'\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 numberOfEntityFieldsWithoutId = Object.keys(entity.fields).length - 1\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\tnumberOfEntityFieldsWithoutId,\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\tnumberOfEntityFieldsWithoutId,\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, numberOfEntityFieldsWithoutId }: {\n\t\toperations: Acl.EntityOperations\n\t\tpredicate: Acl.Predicate\n\t\tnumberOfEntityFieldsWithoutId: number\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 (fields.length === numberOfEntityFieldsWithoutId) {\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"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AclDefinitionCodeGenerator.js","sources":["../../../../../packages/schema-utils/src/definition-generator/AclDefinitionCodeGenerator.ts"],"sourcesContent":["import { PredicateDefinitionProcessor } from '../acl/index.js'\nimport { IndentDecider, Literal, printJsValue } from '../utils/printJsValue.js'\nimport { Acl, Model, Schema } from '@contember/schema'\nimport { DefinitionNamingConventions } from './DefinitionNamingConventions.js'\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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"}
@@ -17,7 +17,7 @@ class MembershipResolver {
17
17
  const parsed = [];
18
18
  const roles = acl.roles;
19
19
  for (const membership of memberships) {
20
- if (!roles[membership.role]) {
20
+ if (!Object.prototype.hasOwnProperty.call(roles, membership.role)) {
21
21
  errors.push(new MembershipValidationError(
22
22
  membership.role,
23
23
  "roleNotFound"
@@ -27,7 +27,8 @@ class MembershipResolver {
27
27
  }
28
28
  const roleVariables = schemaUtils.getRoleVariables(membership.role, acl);
29
29
  for (const variable of membership.variables) {
30
- if (!roleVariables[variable.name] || !isAssumed && roleVariables[variable.name].type === schema.Acl.VariableType.predefined) {
30
+ const roleVariable = Object.prototype.hasOwnProperty.call(roleVariables, variable.name) ? roleVariables[variable.name] : void 0;
31
+ if (!roleVariable || !isAssumed && roleVariable.type === schema.Acl.VariableType.predefined) {
31
32
  errors.push(
32
33
  new MembershipValidationError(
33
34
  membership.role,
@@ -1 +1 @@
1
- {"version":3,"file":"MembershipResolver.cjs","sources":["../../../../../packages/schema-utils/src/validation/MembershipResolver.ts"],"sourcesContent":["import { Acl, Input } from '@contember/schema'\nimport { getRoleVariables } from '../acl/index.js'\nimport { conditionSchema } from '../type-schema/index.js'\nimport { assertNever } from '../utils/index.js'\n\nexport class MembershipResolver {\n\tpublic static UnknownIdentity = { identityId: '00000000-0000-0000-0000-000000000000' }\n\n\tresolve(\n\t\tacl: Acl.Schema,\n\t\tmemberships: readonly Acl.Membership[],\n\t\tidentity: {\n\t\t\tidentityId: string\n\t\t\tpersonId?: string\n\t\t},\n\t\tisAssumed: boolean,\n\t): MembershipReadResult {\n\t\tconst errors: MembershipValidationError[] = []\n\t\tconst parsed: ParsedMembership[] = []\n\t\tconst roles = acl.roles\n\t\tfor (const membership of memberships) {\n\t\t\tif (!roles[membership.role]) {\n\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.ROLE_NOT_FOUND))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst roleVariables = getRoleVariables(membership.role, acl)\n\t\t\tfor (const variable of membership.variables) {\n\t\t\t\tif (!roleVariables[variable.name] || (!isAssumed && roleVariables[variable.name].type === Acl.VariableType.predefined)) {\n\t\t\t\t\terrors.push(\n\t\t\t\t\t\tnew MembershipValidationError(\n\t\t\t\t\t\t\tmembership.role,\n\t\t\t\t\t\t\tMembershipValidationErrorType.VARIABLE_NOT_FOUND,\n\t\t\t\t\t\t\tvariable.name,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst [membershipErrors, parsedVariables] = this.readMembershipVariables(roleVariables, membership, identity, isAssumed)\n\t\t\terrors.push(...membershipErrors)\n\t\t\tparsed.push({\n\t\t\t\trole: membership.role,\n\t\t\t\tvariables: parsedVariables,\n\t\t\t})\n\t\t}\n\t\treturn new MembershipReadResult(errors, parsed)\n\t}\n\n\tprivate readMembershipVariables(roleVariables: Acl.Variables, membership: Acl.Membership, identity: {\n\t\tidentityId: string\n\t\tpersonId?: string\n\t}, isAssumed: boolean): [MembershipValidationError[], ParsedMembershipVariable[]] {\n\t\tconst errors: MembershipValidationError[] = []\n\t\tconst parsedVariables: ParsedMembershipVariable[] = []\n\t\tfor (const [name, variable] of Object.entries(roleVariables)) {\n\t\t\tconst inputVariable = membership.variables.find(it => it.name === name)\n\t\t\tif (variable.type === Acl.VariableType.predefined) {\n\t\t\t\tswitch (variable.value) {\n\t\t\t\t\tcase 'identityID':\n\t\t\t\t\t\tparsedVariables.push({\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\tcondition: { in: isAssumed && inputVariable ? inputVariable.values : [identity.identityId] },\n\t\t\t\t\t\t})\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase 'personID':\n\t\t\t\t\t\tparsedVariables.push({\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\tcondition: { in: isAssumed && inputVariable ? inputVariable.values : identity.personId ? [identity.personId] : [] },\n\t\t\t\t\t\t})\n\t\t\t\t\t\tbreak\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tparsedVariables.push({ name, condition: { never: true } })\n\t\t\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.VARIABLE_INVALID, name))\n\t\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (!inputVariable) {\n\t\t\t\tif (variable.fallback) {\n\t\t\t\t\tparsedVariables.push({ name, condition: variable.fallback })\n\t\t\t\t} else {\n\t\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.VARIABLE_EMPTY, name))\n\t\t\t\t\tparsedVariables.push({ name, condition: { never: true } })\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (variable.type === Acl.VariableType.entity) {\n\t\t\t\tparsedVariables.push({ name, condition: { in: inputVariable.values } }) // todo: cast to int where primary is int\n\t\t\t} else if (variable.type === Acl.VariableType.condition) {\n\t\t\t\ttry {\n\t\t\t\t\tconst conditions = inputVariable.values.map(it => conditionSchema()(JSON.parse(it)))\n\t\t\t\t\tparsedVariables.push({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tcondition: conditions.length === 1 ? conditions[0] : { or: conditions },\n\t\t\t\t\t})\n\t\t\t\t} catch {\n\t\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.VARIABLE_INVALID, name))\n\t\t\t\t\tparsedVariables.push({ name, condition: { never: true } })\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tassertNever(variable)\n\t\t\t}\n\t\t}\n\t\treturn [errors, parsedVariables]\n\t}\n}\n\nexport class MembershipReadResult {\n\tconstructor(\n\t\tpublic readonly errors: MembershipValidationError[],\n\t\tpublic readonly memberships: ParsedMembership[],\n\t) {\n\t}\n}\n\nexport class MembershipValidationError {\n\tconstructor(\n\t\tpublic readonly role: string,\n\t\tpublic readonly error: MembershipValidationErrorType,\n\t\tpublic readonly variable?: string,\n\t) {\n\t}\n}\n\nexport enum MembershipValidationErrorType {\n\tROLE_NOT_FOUND = 'roleNotFound',\n\tVARIABLE_NOT_FOUND = 'variableNotFound',\n\tVARIABLE_EMPTY = 'variableEmpty',\n\tVARIABLE_INVALID = 'variableInvalid',\n}\n\nexport interface ParsedMembershipVariable {\n\treadonly name: string\n\treadonly condition: Input.Condition\n}\n\nexport interface ParsedMembership {\n\treadonly role: string\n\treadonly variables: readonly ParsedMembershipVariable[]\n}\n"],"names":["getRoleVariables","Acl","conditionSchema","assertNever","MembershipValidationErrorType"],"mappings":"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"}
1
+ {"version":3,"file":"MembershipResolver.cjs","sources":["../../../../../packages/schema-utils/src/validation/MembershipResolver.ts"],"sourcesContent":["import { Acl, Input } from '@contember/schema'\nimport { getRoleVariables } from '../acl/index.js'\nimport { conditionSchema } from '../type-schema/index.js'\nimport { assertNever } from '../utils/index.js'\n\nexport class MembershipResolver {\n\tpublic static UnknownIdentity = { identityId: '00000000-0000-0000-0000-000000000000' }\n\n\tresolve(\n\t\tacl: Acl.Schema,\n\t\tmemberships: readonly Acl.Membership[],\n\t\tidentity: {\n\t\t\tidentityId: string\n\t\t\tpersonId?: string\n\t\t},\n\t\tisAssumed: boolean,\n\t): MembershipReadResult {\n\t\tconst errors: MembershipValidationError[] = []\n\t\tconst parsed: ParsedMembership[] = []\n\t\tconst roles = acl.roles\n\t\tfor (const membership of memberships) {\n\t\t\t// own-property lookup so a role named `__proto__` / `constructor` resolves to \"not found\"\n\t\t\t// rather than an inherited Object.prototype member\n\t\t\tif (!Object.prototype.hasOwnProperty.call(roles, membership.role)) {\n\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.ROLE_NOT_FOUND))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst roleVariables = getRoleVariables(membership.role, acl)\n\t\t\tfor (const variable of membership.variables) {\n\t\t\t\tconst roleVariable = Object.prototype.hasOwnProperty.call(roleVariables, variable.name) ? roleVariables[variable.name] : undefined\n\t\t\t\tif (!roleVariable || (!isAssumed && roleVariable.type === Acl.VariableType.predefined)) {\n\t\t\t\t\terrors.push(\n\t\t\t\t\t\tnew MembershipValidationError(\n\t\t\t\t\t\t\tmembership.role,\n\t\t\t\t\t\t\tMembershipValidationErrorType.VARIABLE_NOT_FOUND,\n\t\t\t\t\t\t\tvariable.name,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst [membershipErrors, parsedVariables] = this.readMembershipVariables(roleVariables, membership, identity, isAssumed)\n\t\t\terrors.push(...membershipErrors)\n\t\t\tparsed.push({\n\t\t\t\trole: membership.role,\n\t\t\t\tvariables: parsedVariables,\n\t\t\t})\n\t\t}\n\t\treturn new MembershipReadResult(errors, parsed)\n\t}\n\n\tprivate readMembershipVariables(roleVariables: Acl.Variables, membership: Acl.Membership, identity: {\n\t\tidentityId: string\n\t\tpersonId?: string\n\t}, isAssumed: boolean): [MembershipValidationError[], ParsedMembershipVariable[]] {\n\t\tconst errors: MembershipValidationError[] = []\n\t\tconst parsedVariables: ParsedMembershipVariable[] = []\n\t\tfor (const [name, variable] of Object.entries(roleVariables)) {\n\t\t\tconst inputVariable = membership.variables.find(it => it.name === name)\n\t\t\tif (variable.type === Acl.VariableType.predefined) {\n\t\t\t\tswitch (variable.value) {\n\t\t\t\t\tcase 'identityID':\n\t\t\t\t\t\tparsedVariables.push({\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\tcondition: { in: isAssumed && inputVariable ? inputVariable.values : [identity.identityId] },\n\t\t\t\t\t\t})\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase 'personID':\n\t\t\t\t\t\tparsedVariables.push({\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\tcondition: { in: isAssumed && inputVariable ? inputVariable.values : identity.personId ? [identity.personId] : [] },\n\t\t\t\t\t\t})\n\t\t\t\t\t\tbreak\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tparsedVariables.push({ name, condition: { never: true } })\n\t\t\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.VARIABLE_INVALID, name))\n\t\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (!inputVariable) {\n\t\t\t\tif (variable.fallback) {\n\t\t\t\t\tparsedVariables.push({ name, condition: variable.fallback })\n\t\t\t\t} else {\n\t\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.VARIABLE_EMPTY, name))\n\t\t\t\t\tparsedVariables.push({ name, condition: { never: true } })\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (variable.type === Acl.VariableType.entity) {\n\t\t\t\tparsedVariables.push({ name, condition: { in: inputVariable.values } }) // todo: cast to int where primary is int\n\t\t\t} else if (variable.type === Acl.VariableType.condition) {\n\t\t\t\ttry {\n\t\t\t\t\tconst conditions = inputVariable.values.map(it => conditionSchema()(JSON.parse(it)))\n\t\t\t\t\tparsedVariables.push({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tcondition: conditions.length === 1 ? conditions[0] : { or: conditions },\n\t\t\t\t\t})\n\t\t\t\t} catch {\n\t\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.VARIABLE_INVALID, name))\n\t\t\t\t\tparsedVariables.push({ name, condition: { never: true } })\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tassertNever(variable)\n\t\t\t}\n\t\t}\n\t\treturn [errors, parsedVariables]\n\t}\n}\n\nexport class MembershipReadResult {\n\tconstructor(\n\t\tpublic readonly errors: MembershipValidationError[],\n\t\tpublic readonly memberships: ParsedMembership[],\n\t) {\n\t}\n}\n\nexport class MembershipValidationError {\n\tconstructor(\n\t\tpublic readonly role: string,\n\t\tpublic readonly error: MembershipValidationErrorType,\n\t\tpublic readonly variable?: string,\n\t) {\n\t}\n}\n\nexport enum MembershipValidationErrorType {\n\tROLE_NOT_FOUND = 'roleNotFound',\n\tVARIABLE_NOT_FOUND = 'variableNotFound',\n\tVARIABLE_EMPTY = 'variableEmpty',\n\tVARIABLE_INVALID = 'variableInvalid',\n}\n\nexport interface ParsedMembershipVariable {\n\treadonly name: string\n\treadonly condition: Input.Condition\n}\n\nexport interface ParsedMembership {\n\treadonly role: string\n\treadonly variables: readonly ParsedMembershipVariable[]\n}\n"],"names":["getRoleVariables","Acl","conditionSchema","assertNever","MembershipValidationErrorType"],"mappings":"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"}
@@ -15,7 +15,7 @@ class MembershipResolver {
15
15
  const parsed = [];
16
16
  const roles = acl.roles;
17
17
  for (const membership of memberships) {
18
- if (!roles[membership.role]) {
18
+ if (!Object.prototype.hasOwnProperty.call(roles, membership.role)) {
19
19
  errors.push(new MembershipValidationError(
20
20
  membership.role,
21
21
  "roleNotFound"
@@ -25,7 +25,8 @@ class MembershipResolver {
25
25
  }
26
26
  const roleVariables = getRoleVariables(membership.role, acl);
27
27
  for (const variable of membership.variables) {
28
- if (!roleVariables[variable.name] || !isAssumed && roleVariables[variable.name].type === Acl.VariableType.predefined) {
28
+ const roleVariable = Object.prototype.hasOwnProperty.call(roleVariables, variable.name) ? roleVariables[variable.name] : void 0;
29
+ if (!roleVariable || !isAssumed && roleVariable.type === Acl.VariableType.predefined) {
29
30
  errors.push(
30
31
  new MembershipValidationError(
31
32
  membership.role,
@@ -1 +1 @@
1
- {"version":3,"file":"MembershipResolver.js","sources":["../../../../../packages/schema-utils/src/validation/MembershipResolver.ts"],"sourcesContent":["import { Acl, Input } from '@contember/schema'\nimport { getRoleVariables } from '../acl/index.js'\nimport { conditionSchema } from '../type-schema/index.js'\nimport { assertNever } from '../utils/index.js'\n\nexport class MembershipResolver {\n\tpublic static UnknownIdentity = { identityId: '00000000-0000-0000-0000-000000000000' }\n\n\tresolve(\n\t\tacl: Acl.Schema,\n\t\tmemberships: readonly Acl.Membership[],\n\t\tidentity: {\n\t\t\tidentityId: string\n\t\t\tpersonId?: string\n\t\t},\n\t\tisAssumed: boolean,\n\t): MembershipReadResult {\n\t\tconst errors: MembershipValidationError[] = []\n\t\tconst parsed: ParsedMembership[] = []\n\t\tconst roles = acl.roles\n\t\tfor (const membership of memberships) {\n\t\t\tif (!roles[membership.role]) {\n\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.ROLE_NOT_FOUND))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst roleVariables = getRoleVariables(membership.role, acl)\n\t\t\tfor (const variable of membership.variables) {\n\t\t\t\tif (!roleVariables[variable.name] || (!isAssumed && roleVariables[variable.name].type === Acl.VariableType.predefined)) {\n\t\t\t\t\terrors.push(\n\t\t\t\t\t\tnew MembershipValidationError(\n\t\t\t\t\t\t\tmembership.role,\n\t\t\t\t\t\t\tMembershipValidationErrorType.VARIABLE_NOT_FOUND,\n\t\t\t\t\t\t\tvariable.name,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst [membershipErrors, parsedVariables] = this.readMembershipVariables(roleVariables, membership, identity, isAssumed)\n\t\t\terrors.push(...membershipErrors)\n\t\t\tparsed.push({\n\t\t\t\trole: membership.role,\n\t\t\t\tvariables: parsedVariables,\n\t\t\t})\n\t\t}\n\t\treturn new MembershipReadResult(errors, parsed)\n\t}\n\n\tprivate readMembershipVariables(roleVariables: Acl.Variables, membership: Acl.Membership, identity: {\n\t\tidentityId: string\n\t\tpersonId?: string\n\t}, isAssumed: boolean): [MembershipValidationError[], ParsedMembershipVariable[]] {\n\t\tconst errors: MembershipValidationError[] = []\n\t\tconst parsedVariables: ParsedMembershipVariable[] = []\n\t\tfor (const [name, variable] of Object.entries(roleVariables)) {\n\t\t\tconst inputVariable = membership.variables.find(it => it.name === name)\n\t\t\tif (variable.type === Acl.VariableType.predefined) {\n\t\t\t\tswitch (variable.value) {\n\t\t\t\t\tcase 'identityID':\n\t\t\t\t\t\tparsedVariables.push({\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\tcondition: { in: isAssumed && inputVariable ? inputVariable.values : [identity.identityId] },\n\t\t\t\t\t\t})\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase 'personID':\n\t\t\t\t\t\tparsedVariables.push({\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\tcondition: { in: isAssumed && inputVariable ? inputVariable.values : identity.personId ? [identity.personId] : [] },\n\t\t\t\t\t\t})\n\t\t\t\t\t\tbreak\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tparsedVariables.push({ name, condition: { never: true } })\n\t\t\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.VARIABLE_INVALID, name))\n\t\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (!inputVariable) {\n\t\t\t\tif (variable.fallback) {\n\t\t\t\t\tparsedVariables.push({ name, condition: variable.fallback })\n\t\t\t\t} else {\n\t\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.VARIABLE_EMPTY, name))\n\t\t\t\t\tparsedVariables.push({ name, condition: { never: true } })\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (variable.type === Acl.VariableType.entity) {\n\t\t\t\tparsedVariables.push({ name, condition: { in: inputVariable.values } }) // todo: cast to int where primary is int\n\t\t\t} else if (variable.type === Acl.VariableType.condition) {\n\t\t\t\ttry {\n\t\t\t\t\tconst conditions = inputVariable.values.map(it => conditionSchema()(JSON.parse(it)))\n\t\t\t\t\tparsedVariables.push({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tcondition: conditions.length === 1 ? conditions[0] : { or: conditions },\n\t\t\t\t\t})\n\t\t\t\t} catch {\n\t\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.VARIABLE_INVALID, name))\n\t\t\t\t\tparsedVariables.push({ name, condition: { never: true } })\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tassertNever(variable)\n\t\t\t}\n\t\t}\n\t\treturn [errors, parsedVariables]\n\t}\n}\n\nexport class MembershipReadResult {\n\tconstructor(\n\t\tpublic readonly errors: MembershipValidationError[],\n\t\tpublic readonly memberships: ParsedMembership[],\n\t) {\n\t}\n}\n\nexport class MembershipValidationError {\n\tconstructor(\n\t\tpublic readonly role: string,\n\t\tpublic readonly error: MembershipValidationErrorType,\n\t\tpublic readonly variable?: string,\n\t) {\n\t}\n}\n\nexport enum MembershipValidationErrorType {\n\tROLE_NOT_FOUND = 'roleNotFound',\n\tVARIABLE_NOT_FOUND = 'variableNotFound',\n\tVARIABLE_EMPTY = 'variableEmpty',\n\tVARIABLE_INVALID = 'variableInvalid',\n}\n\nexport interface ParsedMembershipVariable {\n\treadonly name: string\n\treadonly condition: Input.Condition\n}\n\nexport interface ParsedMembership {\n\treadonly role: string\n\treadonly variables: readonly ParsedMembershipVariable[]\n}\n"],"names":["MembershipValidationErrorType"],"mappings":"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"}
1
+ {"version":3,"file":"MembershipResolver.js","sources":["../../../../../packages/schema-utils/src/validation/MembershipResolver.ts"],"sourcesContent":["import { Acl, Input } from '@contember/schema'\nimport { getRoleVariables } from '../acl/index.js'\nimport { conditionSchema } from '../type-schema/index.js'\nimport { assertNever } from '../utils/index.js'\n\nexport class MembershipResolver {\n\tpublic static UnknownIdentity = { identityId: '00000000-0000-0000-0000-000000000000' }\n\n\tresolve(\n\t\tacl: Acl.Schema,\n\t\tmemberships: readonly Acl.Membership[],\n\t\tidentity: {\n\t\t\tidentityId: string\n\t\t\tpersonId?: string\n\t\t},\n\t\tisAssumed: boolean,\n\t): MembershipReadResult {\n\t\tconst errors: MembershipValidationError[] = []\n\t\tconst parsed: ParsedMembership[] = []\n\t\tconst roles = acl.roles\n\t\tfor (const membership of memberships) {\n\t\t\t// own-property lookup so a role named `__proto__` / `constructor` resolves to \"not found\"\n\t\t\t// rather than an inherited Object.prototype member\n\t\t\tif (!Object.prototype.hasOwnProperty.call(roles, membership.role)) {\n\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.ROLE_NOT_FOUND))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst roleVariables = getRoleVariables(membership.role, acl)\n\t\t\tfor (const variable of membership.variables) {\n\t\t\t\tconst roleVariable = Object.prototype.hasOwnProperty.call(roleVariables, variable.name) ? roleVariables[variable.name] : undefined\n\t\t\t\tif (!roleVariable || (!isAssumed && roleVariable.type === Acl.VariableType.predefined)) {\n\t\t\t\t\terrors.push(\n\t\t\t\t\t\tnew MembershipValidationError(\n\t\t\t\t\t\t\tmembership.role,\n\t\t\t\t\t\t\tMembershipValidationErrorType.VARIABLE_NOT_FOUND,\n\t\t\t\t\t\t\tvariable.name,\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst [membershipErrors, parsedVariables] = this.readMembershipVariables(roleVariables, membership, identity, isAssumed)\n\t\t\terrors.push(...membershipErrors)\n\t\t\tparsed.push({\n\t\t\t\trole: membership.role,\n\t\t\t\tvariables: parsedVariables,\n\t\t\t})\n\t\t}\n\t\treturn new MembershipReadResult(errors, parsed)\n\t}\n\n\tprivate readMembershipVariables(roleVariables: Acl.Variables, membership: Acl.Membership, identity: {\n\t\tidentityId: string\n\t\tpersonId?: string\n\t}, isAssumed: boolean): [MembershipValidationError[], ParsedMembershipVariable[]] {\n\t\tconst errors: MembershipValidationError[] = []\n\t\tconst parsedVariables: ParsedMembershipVariable[] = []\n\t\tfor (const [name, variable] of Object.entries(roleVariables)) {\n\t\t\tconst inputVariable = membership.variables.find(it => it.name === name)\n\t\t\tif (variable.type === Acl.VariableType.predefined) {\n\t\t\t\tswitch (variable.value) {\n\t\t\t\t\tcase 'identityID':\n\t\t\t\t\t\tparsedVariables.push({\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\tcondition: { in: isAssumed && inputVariable ? inputVariable.values : [identity.identityId] },\n\t\t\t\t\t\t})\n\t\t\t\t\t\tbreak\n\t\t\t\t\tcase 'personID':\n\t\t\t\t\t\tparsedVariables.push({\n\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\tcondition: { in: isAssumed && inputVariable ? inputVariable.values : identity.personId ? [identity.personId] : [] },\n\t\t\t\t\t\t})\n\t\t\t\t\t\tbreak\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tparsedVariables.push({ name, condition: { never: true } })\n\t\t\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.VARIABLE_INVALID, name))\n\t\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (!inputVariable) {\n\t\t\t\tif (variable.fallback) {\n\t\t\t\t\tparsedVariables.push({ name, condition: variable.fallback })\n\t\t\t\t} else {\n\t\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.VARIABLE_EMPTY, name))\n\t\t\t\t\tparsedVariables.push({ name, condition: { never: true } })\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif (variable.type === Acl.VariableType.entity) {\n\t\t\t\tparsedVariables.push({ name, condition: { in: inputVariable.values } }) // todo: cast to int where primary is int\n\t\t\t} else if (variable.type === Acl.VariableType.condition) {\n\t\t\t\ttry {\n\t\t\t\t\tconst conditions = inputVariable.values.map(it => conditionSchema()(JSON.parse(it)))\n\t\t\t\t\tparsedVariables.push({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tcondition: conditions.length === 1 ? conditions[0] : { or: conditions },\n\t\t\t\t\t})\n\t\t\t\t} catch {\n\t\t\t\t\terrors.push(new MembershipValidationError(membership.role, MembershipValidationErrorType.VARIABLE_INVALID, name))\n\t\t\t\t\tparsedVariables.push({ name, condition: { never: true } })\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tassertNever(variable)\n\t\t\t}\n\t\t}\n\t\treturn [errors, parsedVariables]\n\t}\n}\n\nexport class MembershipReadResult {\n\tconstructor(\n\t\tpublic readonly errors: MembershipValidationError[],\n\t\tpublic readonly memberships: ParsedMembership[],\n\t) {\n\t}\n}\n\nexport class MembershipValidationError {\n\tconstructor(\n\t\tpublic readonly role: string,\n\t\tpublic readonly error: MembershipValidationErrorType,\n\t\tpublic readonly variable?: string,\n\t) {\n\t}\n}\n\nexport enum MembershipValidationErrorType {\n\tROLE_NOT_FOUND = 'roleNotFound',\n\tVARIABLE_NOT_FOUND = 'variableNotFound',\n\tVARIABLE_EMPTY = 'variableEmpty',\n\tVARIABLE_INVALID = 'variableInvalid',\n}\n\nexport interface ParsedMembershipVariable {\n\treadonly name: string\n\treadonly condition: Input.Condition\n}\n\nexport interface ParsedMembership {\n\treadonly role: string\n\treadonly variables: readonly ParsedMembershipVariable[]\n}\n"],"names":["MembershipValidationErrorType"],"mappings":"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"}
@@ -1,7 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const getRoleVariables = (roleName, schema) => {
4
- const role = schema.roles[roleName];
4
+ const role = Object.prototype.hasOwnProperty.call(schema.roles, roleName) ? schema.roles[roleName] : void 0;
5
+ if (!role) {
6
+ return {};
7
+ }
5
8
  return (role.inherits || []).reduce(
6
9
  (acc, inheritsFrom) => ({ ...getRoleVariables(inheritsFrom, schema), ...acc }),
7
10
  role.variables
@@ -1 +1 @@
1
- {"version":3,"file":"schemaUtils.cjs","sources":["../../../../../packages/schema-utils/src/acl/schemaUtils.ts"],"sourcesContent":["import { Acl } from '@contember/schema'\n\nexport const getRoleVariables = (roleName: string, schema: Acl.Schema): Acl.Variables => {\n\tconst role = schema.roles[roleName]\n\treturn (role.inherits || []).reduce<Acl.Variables>(\n\t\t(acc, inheritsFrom) => ({ ...getRoleVariables(inheritsFrom, schema), ...acc }),\n\t\trole.variables,\n\t)\n}\n"],"names":[],"mappings":";;AAEO,MAAM,mBAAmB,CAAC,UAAkB,WAAsC;AACxF,QAAM,OAAO,OAAO,MAAM,QAAQ;AAClC,UAAQ,KAAK,YAAY,CAAA,GAAI;AAAA,IAC5B,CAAC,KAAK,kBAAkB,EAAE,GAAG,iBAAiB,cAAc,MAAM,GAAG,GAAG;IACxE,KAAK;AAAA,EAAA;AAEP;;"}
1
+ {"version":3,"file":"schemaUtils.cjs","sources":["../../../../../packages/schema-utils/src/acl/schemaUtils.ts"],"sourcesContent":["import { Acl } from '@contember/schema'\n\nexport const getRoleVariables = (roleName: string, schema: Acl.Schema): Acl.Variables => {\n\t// Own-property lookup: a role name like `__proto__` / `constructor`, or an `inherits` entry pointing\n\t// at a role absent from the schema, must resolve to \"no such role\" (→ no variables) instead of an\n\t// inherited Object.prototype member — otherwise `role.inherits` / `role.variables` below throw on a\n\t// non-role value (e.g. a sign-in granting such a role would roll back the whole transaction).\n\tconst role = Object.prototype.hasOwnProperty.call(schema.roles, roleName) ? schema.roles[roleName] : undefined\n\tif (!role) {\n\t\treturn {}\n\t}\n\treturn (role.inherits || []).reduce<Acl.Variables>(\n\t\t(acc, inheritsFrom) => ({ ...getRoleVariables(inheritsFrom, schema), ...acc }),\n\t\trole.variables,\n\t)\n}\n"],"names":[],"mappings":";;AAEO,MAAM,mBAAmB,CAAC,UAAkB,WAAsC;AAKxF,QAAM,OAAO,OAAO,UAAU,eAAe,KAAK,OAAO,OAAO,QAAQ,IAAI,OAAO,MAAM,QAAQ,IAAI;AACrG,MAAI,CAAC,MAAM;AACV,WAAO,CAAA;AAAA,EACR;AACA,UAAQ,KAAK,YAAY,CAAA,GAAI;AAAA,IAC5B,CAAC,KAAK,kBAAkB,EAAE,GAAG,iBAAiB,cAAc,MAAM,GAAG,GAAG;IACxE,KAAK;AAAA,EAAA;AAEP;;"}
@@ -1,5 +1,8 @@
1
1
  const getRoleVariables = (roleName, schema) => {
2
- const role = schema.roles[roleName];
2
+ const role = Object.prototype.hasOwnProperty.call(schema.roles, roleName) ? schema.roles[roleName] : void 0;
3
+ if (!role) {
4
+ return {};
5
+ }
3
6
  return (role.inherits || []).reduce(
4
7
  (acc, inheritsFrom) => ({ ...getRoleVariables(inheritsFrom, schema), ...acc }),
5
8
  role.variables
@@ -1 +1 @@
1
- {"version":3,"file":"schemaUtils.js","sources":["../../../../../packages/schema-utils/src/acl/schemaUtils.ts"],"sourcesContent":["import { Acl } from '@contember/schema'\n\nexport const getRoleVariables = (roleName: string, schema: Acl.Schema): Acl.Variables => {\n\tconst role = schema.roles[roleName]\n\treturn (role.inherits || []).reduce<Acl.Variables>(\n\t\t(acc, inheritsFrom) => ({ ...getRoleVariables(inheritsFrom, schema), ...acc }),\n\t\trole.variables,\n\t)\n}\n"],"names":[],"mappings":"AAEO,MAAM,mBAAmB,CAAC,UAAkB,WAAsC;AACxF,QAAM,OAAO,OAAO,MAAM,QAAQ;AAClC,UAAQ,KAAK,YAAY,CAAA,GAAI;AAAA,IAC5B,CAAC,KAAK,kBAAkB,EAAE,GAAG,iBAAiB,cAAc,MAAM,GAAG,GAAG;IACxE,KAAK;AAAA,EAAA;AAEP;"}
1
+ {"version":3,"file":"schemaUtils.js","sources":["../../../../../packages/schema-utils/src/acl/schemaUtils.ts"],"sourcesContent":["import { Acl } from '@contember/schema'\n\nexport const getRoleVariables = (roleName: string, schema: Acl.Schema): Acl.Variables => {\n\t// Own-property lookup: a role name like `__proto__` / `constructor`, or an `inherits` entry pointing\n\t// at a role absent from the schema, must resolve to \"no such role\" (→ no variables) instead of an\n\t// inherited Object.prototype member — otherwise `role.inherits` / `role.variables` below throw on a\n\t// non-role value (e.g. a sign-in granting such a role would roll back the whole transaction).\n\tconst role = Object.prototype.hasOwnProperty.call(schema.roles, roleName) ? schema.roles[roleName] : undefined\n\tif (!role) {\n\t\treturn {}\n\t}\n\treturn (role.inherits || []).reduce<Acl.Variables>(\n\t\t(acc, inheritsFrom) => ({ ...getRoleVariables(inheritsFrom, schema), ...acc }),\n\t\trole.variables,\n\t)\n}\n"],"names":[],"mappings":"AAEO,MAAM,mBAAmB,CAAC,UAAkB,WAAsC;AAKxF,QAAM,OAAO,OAAO,UAAU,eAAe,KAAK,OAAO,OAAO,QAAQ,IAAI,OAAO,MAAM,QAAQ,IAAI;AACrG,MAAI,CAAC,MAAM;AACV,WAAO,CAAA;AAAA,EACR;AACA,UAAQ,KAAK,YAAY,CAAA,GAAI;AAAA,IAC5B,CAAC,KAAK,kBAAkB,EAAE,GAAG,iBAAiB,cAAc,MAAM,GAAG,GAAG;IACxE,KAAK;AAAA,EAAA;AAEP;"}
@@ -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 numberOfEntityFieldsWithoutId = Object.keys(entity.fields).length - 1;
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
- numberOfEntityFieldsWithoutId
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
- numberOfEntityFieldsWithoutId
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, numberOfEntityFieldsWithoutId }) {
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.length === numberOfEntityFieldsWithoutId) {
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/index.js'\nimport { IndentDecider, Literal, printJsValue } from '../utils/printJsValue.js'\nimport { Acl, Model, Schema } from '@contember/schema'\nimport { DefinitionNamingConventions } from './DefinitionNamingConventions.js'\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 numberOfEntityFieldsWithoutId = Object.keys(entity.fields).length - 1\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\tnumberOfEntityFieldsWithoutId,\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\tnumberOfEntityFieldsWithoutId,\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, numberOfEntityFieldsWithoutId }: {\n\t\toperations: Acl.EntityOperations\n\t\tpredicate: Acl.Predicate\n\t\tnumberOfEntityFieldsWithoutId: number\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 (fields.length === numberOfEntityFieldsWithoutId) {\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"],"names":["DefinitionNamingConventions","printJsValue","Acl","schema","PredicateDefinitionProcessor","Literal"],"mappings":"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"}
1
+ {"version":3,"file":"AclDefinitionCodeGenerator.cjs","sources":["../../../../../packages/schema-utils/src/definition-generator/AclDefinitionCodeGenerator.ts"],"sourcesContent":["import { PredicateDefinitionProcessor } from '../acl/index.js'\nimport { IndentDecider, Literal, printJsValue } from '../utils/printJsValue.js'\nimport { Acl, Model, Schema } from '@contember/schema'\nimport { DefinitionNamingConventions } from './DefinitionNamingConventions.js'\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 numberOfEntityFieldsWithoutId = Object.keys(entity.fields).length - 1;
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
- numberOfEntityFieldsWithoutId
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
- numberOfEntityFieldsWithoutId
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, numberOfEntityFieldsWithoutId }) {
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.length === numberOfEntityFieldsWithoutId) {
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