@balena/pinejs 15.0.0-true-boolean-7896b116c446d891d7a0d5e4085c02a13bc9c725 → 15.0.1-build-migrations-clarify-marking-sbvr-optional-d6d0ded8eccc6eadb2492f4697918cf0afd00215-1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. package/.dockerignore +4 -0
  2. package/.github/workflows/flowzone.yml +21 -0
  3. package/.husky/pre-commit +4 -0
  4. package/.pinejs-cache.json +1 -0
  5. package/.resinci.yml +1 -0
  6. package/.versionbot/CHANGELOG.yml +9678 -2002
  7. package/CHANGELOG.md +2976 -2
  8. package/Dockerfile +14 -0
  9. package/Gruntfile.ts +3 -6
  10. package/README.md +10 -1
  11. package/VERSION +1 -0
  12. package/build/browser.ts +1 -1
  13. package/build/config.ts +0 -1
  14. package/docker-compose.npm-test.yml +11 -0
  15. package/docs/AdvancedUsage.md +77 -63
  16. package/docs/GettingStarted.md +90 -41
  17. package/docs/Migrations.md +102 -1
  18. package/docs/ProjectConfig.md +12 -21
  19. package/docs/Testing.md +7 -0
  20. package/out/bin/abstract-sql-compiler.js +17 -17
  21. package/out/bin/abstract-sql-compiler.js.map +1 -1
  22. package/out/bin/odata-compiler.js +23 -20
  23. package/out/bin/odata-compiler.js.map +1 -1
  24. package/out/bin/sbvr-compiler.js +22 -22
  25. package/out/bin/sbvr-compiler.js.map +1 -1
  26. package/out/bin/utils.d.ts +2 -2
  27. package/out/bin/utils.js +3 -3
  28. package/out/bin/utils.js.map +1 -1
  29. package/out/config-loader/config-loader.d.ts +9 -8
  30. package/out/config-loader/config-loader.js +135 -78
  31. package/out/config-loader/config-loader.js.map +1 -1
  32. package/out/config-loader/env.d.ts +41 -16
  33. package/out/config-loader/env.js +46 -2
  34. package/out/config-loader/env.js.map +1 -1
  35. package/out/data-server/sbvr-server.d.ts +2 -19
  36. package/out/data-server/sbvr-server.js +44 -38
  37. package/out/data-server/sbvr-server.js.map +1 -1
  38. package/out/database-layer/db.d.ts +32 -14
  39. package/out/database-layer/db.js +120 -41
  40. package/out/database-layer/db.js.map +1 -1
  41. package/out/express-emulator/express.js +10 -11
  42. package/out/express-emulator/express.js.map +1 -1
  43. package/out/http-transactions/transactions.d.ts +2 -18
  44. package/out/http-transactions/transactions.js +29 -21
  45. package/out/http-transactions/transactions.js.map +1 -1
  46. package/out/migrator/async.d.ts +7 -0
  47. package/out/migrator/async.js +168 -0
  48. package/out/migrator/async.js.map +1 -0
  49. package/out/migrator/migrations.sbvr +43 -0
  50. package/out/migrator/sync.d.ts +9 -0
  51. package/out/migrator/sync.js +106 -0
  52. package/out/migrator/sync.js.map +1 -0
  53. package/out/migrator/utils.d.ts +78 -0
  54. package/out/migrator/utils.js +283 -0
  55. package/out/migrator/utils.js.map +1 -0
  56. package/out/odata-metadata/odata-metadata-generator.js +10 -13
  57. package/out/odata-metadata/odata-metadata-generator.js.map +1 -1
  58. package/out/passport-pinejs/passport-pinejs.d.ts +1 -1
  59. package/out/passport-pinejs/passport-pinejs.js +8 -7
  60. package/out/passport-pinejs/passport-pinejs.js.map +1 -1
  61. package/out/pinejs-session-store/pinejs-session-store.d.ts +1 -1
  62. package/out/pinejs-session-store/pinejs-session-store.js +20 -6
  63. package/out/pinejs-session-store/pinejs-session-store.js.map +1 -1
  64. package/out/sbvr-api/abstract-sql.d.ts +3 -2
  65. package/out/sbvr-api/abstract-sql.js +9 -9
  66. package/out/sbvr-api/abstract-sql.js.map +1 -1
  67. package/out/sbvr-api/cached-compile.js +1 -1
  68. package/out/sbvr-api/cached-compile.js.map +1 -1
  69. package/out/sbvr-api/common-types.d.ts +6 -5
  70. package/out/sbvr-api/control-flow.d.ts +8 -1
  71. package/out/sbvr-api/control-flow.js +36 -9
  72. package/out/sbvr-api/control-flow.js.map +1 -1
  73. package/out/sbvr-api/errors.d.ts +47 -40
  74. package/out/sbvr-api/errors.js +78 -77
  75. package/out/sbvr-api/errors.js.map +1 -1
  76. package/out/sbvr-api/express-extension.d.ts +4 -0
  77. package/out/sbvr-api/hooks.d.ts +16 -15
  78. package/out/sbvr-api/hooks.js +74 -48
  79. package/out/sbvr-api/hooks.js.map +1 -1
  80. package/out/sbvr-api/odata-response.d.ts +2 -2
  81. package/out/sbvr-api/odata-response.js +28 -30
  82. package/out/sbvr-api/odata-response.js.map +1 -1
  83. package/out/sbvr-api/permissions.d.ts +17 -16
  84. package/out/sbvr-api/permissions.js +369 -304
  85. package/out/sbvr-api/permissions.js.map +1 -1
  86. package/out/sbvr-api/sbvr-utils.d.ts +33 -15
  87. package/out/sbvr-api/sbvr-utils.js +397 -235
  88. package/out/sbvr-api/sbvr-utils.js.map +1 -1
  89. package/out/sbvr-api/translations.d.ts +6 -0
  90. package/out/sbvr-api/translations.js +150 -0
  91. package/out/sbvr-api/translations.js.map +1 -0
  92. package/out/sbvr-api/uri-parser.d.ts +23 -17
  93. package/out/sbvr-api/uri-parser.js +33 -27
  94. package/out/sbvr-api/uri-parser.js.map +1 -1
  95. package/out/sbvr-api/user.sbvr +2 -0
  96. package/out/server-glue/module.d.ts +6 -6
  97. package/out/server-glue/module.js +4 -2
  98. package/out/server-glue/module.js.map +1 -1
  99. package/out/server-glue/server.js +5 -5
  100. package/out/server-glue/server.js.map +1 -1
  101. package/package.json +89 -73
  102. package/pinejs.png +0 -0
  103. package/repo.yml +9 -9
  104. package/src/bin/abstract-sql-compiler.ts +5 -7
  105. package/src/bin/odata-compiler.ts +11 -13
  106. package/src/bin/sbvr-compiler.ts +11 -17
  107. package/src/bin/utils.ts +3 -5
  108. package/src/config-loader/config-loader.ts +167 -53
  109. package/src/config-loader/env.ts +106 -6
  110. package/src/data-server/sbvr-server.js +44 -38
  111. package/src/database-layer/db.ts +205 -64
  112. package/src/express-emulator/express.js +10 -11
  113. package/src/http-transactions/transactions.js +29 -21
  114. package/src/migrator/async.ts +323 -0
  115. package/src/migrator/migrations.sbvr +43 -0
  116. package/src/migrator/sync.ts +152 -0
  117. package/src/migrator/utils.ts +458 -0
  118. package/src/odata-metadata/odata-metadata-generator.ts +12 -15
  119. package/src/passport-pinejs/passport-pinejs.ts +9 -7
  120. package/src/pinejs-session-store/pinejs-session-store.ts +15 -1
  121. package/src/sbvr-api/abstract-sql.ts +17 -14
  122. package/src/sbvr-api/common-types.ts +2 -1
  123. package/src/sbvr-api/control-flow.ts +45 -11
  124. package/src/sbvr-api/errors.ts +82 -77
  125. package/src/sbvr-api/express-extension.ts +6 -1
  126. package/src/sbvr-api/hooks.ts +123 -50
  127. package/src/sbvr-api/odata-response.ts +23 -28
  128. package/src/sbvr-api/permissions.ts +548 -415
  129. package/src/sbvr-api/sbvr-utils.ts +581 -259
  130. package/src/sbvr-api/translations.ts +248 -0
  131. package/src/sbvr-api/uri-parser.ts +63 -49
  132. package/src/sbvr-api/user.sbvr +2 -0
  133. package/src/server-glue/module.ts +16 -10
  134. package/src/server-glue/server.ts +5 -5
  135. package/tsconfig.dev.json +1 -0
  136. package/tsconfig.json +1 -2
  137. package/typings/lf-to-abstract-sql.d.ts +6 -9
  138. package/typings/memoizee.d.ts +1 -1
  139. package/.github/CODEOWNERS +0 -1
  140. package/circle.yml +0 -37
  141. package/docs/todo.txt +0 -22
  142. package/out/migrator/migrator.d.ts +0 -20
  143. package/out/migrator/migrator.js +0 -188
  144. package/out/migrator/migrator.js.map +0 -1
  145. package/src/migrator/migrator.ts +0 -286
@@ -42,7 +42,7 @@ const methodPermissions = {
42
42
  MERGE: 'update',
43
43
  DELETE: 'delete',
44
44
  };
45
- const $parsePermissions = memoize((filter) => {
45
+ const $parsePermissions = env.createCache('parsePermissions', (filter) => {
46
46
  const { tree, binds } = ODataParser.parse(filter, {
47
47
  startRule: 'ProcessRule',
48
48
  rule: 'FilterByExpression',
@@ -53,9 +53,8 @@ const $parsePermissions = memoize((filter) => {
53
53
  };
54
54
  }, {
55
55
  primitive: true,
56
- max: env.cache.parsePermissions.max,
57
56
  });
58
- const rewriteBinds = ({ tree, extraBinds }, odataBinds) => {
57
+ const rewriteODataBinds = ({ tree, extraBinds }, odataBinds) => {
59
58
  const bindsLength = odataBinds.length;
60
59
  odataBinds.push(...extraBinds);
61
60
  return _.cloneDeepWith(tree, (value) => {
@@ -69,9 +68,9 @@ const rewriteBinds = ({ tree, extraBinds }, odataBinds) => {
69
68
  };
70
69
  const parsePermissions = (filter, odataBinds) => {
71
70
  const odata = $parsePermissions(filter);
72
- return rewriteBinds(odata, odataBinds);
71
+ return rewriteODataBinds(odata, odataBinds);
73
72
  };
74
- const isAnd = (x) => _.isObject(x) && 'and' in x;
73
+ const isAnd = (x) => typeof x === 'object' && 'and' in x;
75
74
  const isOr = (x) => typeof x === 'object' && 'or' in x;
76
75
  function nestedCheck(check, stringCallback) {
77
76
  if (typeof check === 'string') {
@@ -183,7 +182,10 @@ const namespaceRelationships = (relationships, alias) => {
183
182
  namespaceRelationships(relationship, alias);
184
183
  });
185
184
  };
186
- const getPermissionsLookup = memoize((permissions) => {
185
+ const getPermissionsLookup = env.createCache('permissionsLookup', (permissions, guestPermissions) => {
186
+ if (guestPermissions != null) {
187
+ permissions = [...guestPermissions, ...permissions];
188
+ }
187
189
  const permissionsLookup = {};
188
190
  for (const permission of permissions) {
189
191
  const [target, condition] = permission.split('?');
@@ -191,16 +193,19 @@ const getPermissionsLookup = memoize((permissions) => {
191
193
  permissionsLookup[target] = true;
192
194
  }
193
195
  else if (permissionsLookup[target] !== true) {
194
- if (permissionsLookup[target] == null) {
195
- permissionsLookup[target] = [];
196
- }
196
+ permissionsLookup[target] ??= [];
197
197
  permissionsLookup[target].push(condition);
198
198
  }
199
199
  }
200
+ for (const target of Object.keys(permissionsLookup)) {
201
+ const conditions = permissionsLookup[target];
202
+ if (conditions !== true) {
203
+ permissionsLookup[target] = _.uniq(conditions);
204
+ }
205
+ }
200
206
  return permissionsLookup;
201
207
  }, {
202
- primitive: true,
203
- max: env.cache.permissionsLookup.max,
208
+ normalizer: ([permissions, guestPermissions]) => `${permissions}${guestPermissions == null}`,
204
209
  });
205
210
  const $checkPermissions = (permissionsLookup, actionList, vocabulary, resourceName) => {
206
211
  const checkObject = {
@@ -245,9 +250,9 @@ const convertToLambda = (filter, identifier) => {
245
250
  return;
246
251
  }
247
252
  if (Array.isArray(object)) {
248
- object.forEach((element) => {
253
+ for (const element of object) {
249
254
  replaceObject(element);
250
- });
255
+ }
251
256
  }
252
257
  if (object.hasOwnProperty('name')) {
253
258
  object.property = { ...object };
@@ -265,7 +270,7 @@ const rewriteSubPermissionBindings = (filter, counter) => {
265
270
  if (typeof object.bind === 'number') {
266
271
  object.bind = counter + object.bind;
267
272
  }
268
- if (Array.isArray(object) || _.isObject(object)) {
273
+ if (Array.isArray(object) || typeof object === 'object') {
269
274
  _.forEach(object, (v) => {
270
275
  rewrite(v);
271
276
  });
@@ -279,7 +284,7 @@ const buildODataPermission = (permissionsLookup, actionList, vocabulary, resourc
279
284
  throw constrainedPermissionError;
280
285
  }
281
286
  if (conditionalPerms === true) {
282
- return false;
287
+ return;
283
288
  }
284
289
  const permissionFilters = nestedCheck(conditionalPerms, (permissionCheck) => {
285
290
  try {
@@ -300,32 +305,66 @@ const generateConstrainedAbstractSql = (permissionsLookup, actionList, vocabular
300
305
  const abstractSQLModel = sbvrUtils.getAbstractSqlModel({
301
306
  vocabulary,
302
307
  });
303
- const odata = uri_parser_1.memoizedParseOdata(`/${resourceName}`);
308
+ const odata = (0, uri_parser_1.memoizedParseOdata)(`/${resourceName}`);
304
309
  const collapsedPermissionFilters = buildODataPermission(permissionsLookup, actionList, vocabulary, resourceName, odata);
310
+ if (collapsePermissionFilters == null) {
311
+ return;
312
+ }
305
313
  _.set(odata, ['tree', 'options', '$filter'], collapsedPermissionFilters);
306
314
  const lambdaAlias = randomstring.generate(20);
307
315
  let inc = 0;
308
316
  const canAccessTrace = [resourceName];
317
+ const resolveBind = (maybeBind, extraBinds) => {
318
+ if ((0, odata_to_abstract_sql_1.isBindReference)(maybeBind)) {
319
+ const { bind } = maybeBind;
320
+ if (typeof bind === 'string' || bind < odata.binds.length) {
321
+ return odata.binds[bind];
322
+ }
323
+ return extraBinds[bind - odata.binds.length];
324
+ }
325
+ return maybeBind;
326
+ };
309
327
  const canAccessFunction = function (property) {
310
- delete property.method;
328
+ const { method, ...resolvedProperty } = property;
311
329
  if (!this.defaultResource) {
312
330
  throw new Error(`No resource selected in AST.`);
313
331
  }
314
- const targetResource = this.NavigateResources(this.defaultResource, property.name);
315
- const targetResourceName = odata_to_abstract_sql_1.sqlNameToODataName(targetResource.resource.name);
316
- if (canAccessTrace.includes(targetResourceName)) {
317
- throw new errors_1.PermissionError(`Permissions for ${resourceName} form a circle by the following path: ${canAccessTrace.join(' -> ')} -> ${targetResourceName}`);
332
+ const targetResource = this.NavigateResources(this.defaultResource, resolvedProperty.name);
333
+ const lambdaId = `${lambdaAlias}+${inc}`;
334
+ inc = inc + 1;
335
+ const targetResourceName = (0, odata_to_abstract_sql_1.sqlNameToODataName)(targetResource.resource.name);
336
+ const traceIndex = canAccessTrace.findIndex((rName) => rName === targetResourceName);
337
+ if (traceIndex !== -1) {
338
+ if (canAccessTrace[canAccessTrace.length - 1] !== targetResourceName) {
339
+ throw new Error(`Indirectly circular 'canAccess()' permissions are not supported, currently permissions for ${resourceName} form an indirect circle by the following path: ${canAccessTrace.join(' -> ')} -> ${targetResourceName}`);
340
+ }
341
+ const { args } = method[1];
342
+ const depthArg = resolveBind(args[0], this.extraBindVars);
343
+ if (depthArg == null) {
344
+ throw new Error(`You must specify a depth if you want to enable directly circular 'canAccess()' permissions, currently permissions for ${resourceName} form a direct circle by the following path: ${canAccessTrace.join(' -> ')} -> ${targetResourceName}`);
345
+ }
346
+ const [type, depth] = depthArg;
347
+ if (type !== 'Real' || !Number.isInteger(depth) || depth < 1) {
348
+ throw new Error('The depth for `canAccess` must be an integer >= 1');
349
+ }
350
+ if (canAccessTrace.length > depth &&
351
+ canAccessTrace[canAccessTrace.length - depth] === targetResourceName) {
352
+ resolvedProperty.lambda = {
353
+ method: 'any',
354
+ identifier: lambdaId,
355
+ expression: ['eq', true, false],
356
+ };
357
+ return this.Property(resolvedProperty);
358
+ }
318
359
  }
319
- const parentOdata = uri_parser_1.memoizedParseOdata(`/${targetResourceName}`);
360
+ const parentOdata = (0, uri_parser_1.memoizedParseOdata)(`/${targetResourceName}`);
320
361
  const collapsedParentPermissionFilters = buildODataPermission(permissionsLookup, actionList, vocabulary, targetResourceName, parentOdata);
321
- if (collapsedParentPermissionFilters === false) {
322
- throw constrainedPermissionError;
362
+ if (collapsedParentPermissionFilters == null) {
363
+ return ['Equals', ['Boolean', true], ['Boolean', true]];
323
364
  }
324
- const lambdaId = `${lambdaAlias}+${inc}`;
325
- inc = inc + 1;
326
365
  rewriteSubPermissionBindings(collapsedParentPermissionFilters, this.bindVarsLength + this.extraBindVars.length);
327
366
  convertToLambda(collapsedParentPermissionFilters, lambdaId);
328
- property.lambda = {
367
+ resolvedProperty.lambda = {
329
368
  method: 'any',
330
369
  identifier: lambdaId,
331
370
  expression: collapsedParentPermissionFilters,
@@ -333,41 +372,32 @@ const generateConstrainedAbstractSql = (permissionsLookup, actionList, vocabular
333
372
  this.extraBindVars.push(...parentOdata.binds);
334
373
  canAccessTrace.push(targetResourceName);
335
374
  try {
336
- return this.Property(property);
375
+ return this.Property(resolvedProperty);
337
376
  }
338
377
  finally {
339
378
  canAccessTrace.pop();
340
379
  }
341
380
  };
342
- const { tree, extraBindVars } = uri_parser_1.memoizedGetOData2AbstractSQL(abstractSQLModel).match(odata.tree, 'GET', [], odata.binds.length, {
381
+ const { tree, extraBindVars } = (0, uri_parser_1.memoizedGetOData2AbstractSQL)(abstractSQLModel).match(odata.tree, 'GET', [], odata.binds.length, {
343
382
  canAccess: canAccessFunction,
344
383
  });
345
384
  odata.binds.push(...extraBindVars);
346
385
  const odataBinds = odata.binds;
347
386
  const abstractSqlQuery = [...tree];
348
- const selectIndex = abstractSqlQuery.findIndex((v) => v[0] === 'Select');
349
- const select = (abstractSqlQuery[selectIndex] = [
350
- ...abstractSqlQuery[selectIndex],
351
- ]);
387
+ const select = abstractSqlQuery.find((v) => v[0] === 'Select');
352
388
  select[1] = select[1].map((selectField) => {
353
389
  if (selectField[0] === 'Alias') {
390
+ const sqlName = (0, odata_to_abstract_sql_1.odataNameToSqlName)(selectField[2]);
354
391
  const maybeField = selectField[1];
355
- const fieldType = maybeField[0];
356
- if (fieldType === 'ReferencedField' || fieldType === 'Field') {
392
+ if ((maybeField[0] === 'ReferencedField' && maybeField[2] === sqlName) ||
393
+ (maybeField[0] === 'Field' && maybeField[1] === sqlName)) {
357
394
  return maybeField;
358
395
  }
359
- return [
360
- 'Alias',
361
- maybeField,
362
- odata_to_abstract_sql_1.odataNameToSqlName(selectField[2]),
363
- ];
364
- }
365
- if (selectField.length === 2 && Array.isArray(selectField[0])) {
366
- return selectField[0];
396
+ return ['Alias', maybeField, sqlName];
367
397
  }
368
398
  return selectField;
369
399
  });
370
- return { extraBinds: odataBinds, abstractSqlQuery };
400
+ return { binds: odataBinds, abstractSql: abstractSqlQuery };
371
401
  };
372
402
  const onceGetter = (obj, propName, fn) => {
373
403
  let nullableFn = fn;
@@ -396,14 +426,14 @@ const onceGetter = (obj, propName, fn) => {
396
426
  };
397
427
  const deepFreezeExceptDefinition = (obj) => {
398
428
  Object.freeze(obj);
399
- Object.getOwnPropertyNames(obj).forEach((prop) => {
429
+ for (const prop of Object.getOwnPropertyNames(obj)) {
400
430
  if (prop !== 'definition' &&
401
431
  obj.hasOwnProperty(prop) &&
402
432
  obj[prop] !== null &&
403
433
  !['object', 'function'].includes(typeof obj[prop])) {
404
434
  deepFreezeExceptDefinition(obj);
405
435
  }
406
- });
436
+ }
407
437
  };
408
438
  const createBypassDefinition = (definition) => _.cloneDeepWith(definition, (abstractSql) => {
409
439
  if (Array.isArray(abstractSql) &&
@@ -423,12 +453,11 @@ const getAlias = (name) => {
423
453
  return `permissions${permissionsJSON}`;
424
454
  };
425
455
  const rewriteRelationship = memoizeWeak((value, name, abstractSqlModel, permissionsLookup, vocabulary, odata2AbstractSQL) => {
426
- let escapedName = odata_to_abstract_sql_1.sqlNameToODataName(name);
456
+ let escapedName = (0, odata_to_abstract_sql_1.sqlNameToODataName)(name);
427
457
  if (abstractSqlModel.tables[name]) {
428
- escapedName = odata_to_abstract_sql_1.sqlNameToODataName(abstractSqlModel.tables[name].name);
458
+ escapedName = (0, odata_to_abstract_sql_1.sqlNameToODataName)(abstractSqlModel.tables[name].name);
429
459
  }
430
460
  const rewrite = (object) => {
431
- var _a, _b;
432
461
  if ('$' in object && Array.isArray(object.$)) {
433
462
  const mapping = object.$;
434
463
  if (mapping.length === 2 &&
@@ -439,14 +468,18 @@ const rewriteRelationship = memoizeWeak((value, name, abstractSqlModel, permissi
439
468
  if (possibleTargetResourceName.endsWith('$bypass')) {
440
469
  return;
441
470
  }
442
- const targetResourceEscaped = odata_to_abstract_sql_1.sqlNameToODataName((_b = (_a = abstractSqlModel.tables[possibleTargetResourceName]) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : possibleTargetResourceName);
471
+ const targetResourceEscaped = (0, odata_to_abstract_sql_1.sqlNameToODataName)(abstractSqlModel.tables[possibleTargetResourceName]?.name ??
472
+ possibleTargetResourceName);
443
473
  if (targetResourceEscaped.includes('$')) {
444
474
  return;
445
475
  }
446
476
  let foundCanAccessLink = false;
447
477
  try {
448
- const odata = uri_parser_1.memoizedParseOdata(`/${targetResourceEscaped}`);
478
+ const odata = (0, uri_parser_1.memoizedParseOdata)(`/${targetResourceEscaped}`);
449
479
  const collapsedPermissionFilters = buildODataPermission(permissionsLookup, methodPermissions.GET, vocabulary, targetResourceEscaped, odata);
480
+ if (collapsedPermissionFilters == null) {
481
+ return;
482
+ }
450
483
  _.set(odata, ['tree', 'options', '$filter'], collapsedPermissionFilters);
451
484
  const canAccessFunction = function (property) {
452
485
  delete property.method;
@@ -454,8 +487,8 @@ const rewriteRelationship = memoizeWeak((value, name, abstractSqlModel, permissi
454
487
  throw new Error(`No resource selected in AST.`);
455
488
  }
456
489
  const targetResourceAST = this.NavigateResources(this.defaultResource, property.name);
457
- const targetResourceName = odata_to_abstract_sql_1.sqlNameToODataName(targetResourceAST.resource.name);
458
- const currentResourceName = odata_to_abstract_sql_1.sqlNameToODataName(this.defaultResource.name);
490
+ const targetResourceName = (0, odata_to_abstract_sql_1.sqlNameToODataName)(targetResourceAST.resource.name);
491
+ const currentResourceName = (0, odata_to_abstract_sql_1.sqlNameToODataName)(this.defaultResource.name);
459
492
  if (currentResourceName === targetResourceEscaped &&
460
493
  targetResourceName === escapedName) {
461
494
  foundCanAccessLink = true;
@@ -485,7 +518,7 @@ const rewriteRelationship = memoizeWeak((value, name, abstractSqlModel, permissi
485
518
  }
486
519
  }
487
520
  }
488
- if (Array.isArray(object) || _.isObject(object)) {
521
+ if (Array.isArray(object) || typeof object === 'object') {
489
522
  _.forEach(object, (v) => {
490
523
  if (typeof v !== 'string') {
491
524
  rewrite(v);
@@ -499,7 +532,7 @@ const rewriteRelationships = (abstractSqlModel, relationships, permissionsLookup
499
532
  const originalAbstractSQLModel = sbvrUtils.getAbstractSqlModel({
500
533
  vocabulary,
501
534
  });
502
- const odata2AbstractSQL = uri_parser_1.memoizedGetOData2AbstractSQL(originalAbstractSQLModel);
535
+ const odata2AbstractSQL = (0, uri_parser_1.memoizedGetOData2AbstractSQL)(originalAbstractSQLModel);
503
536
  const newRelationships = _.cloneDeep(relationships);
504
537
  _.forOwn(newRelationships, (value, name) => rewriteRelationship(value, name, abstractSqlModel, permissionsLookup, vocabulary, odata2AbstractSQL));
505
538
  return newRelationships;
@@ -507,9 +540,12 @@ const rewriteRelationships = (abstractSqlModel, relationships, permissionsLookup
507
540
  const stringifiedGetPermissions = JSON.stringify(methodPermissions.GET);
508
541
  const getBoundConstrainedMemoizer = memoizeWeak((abstractSqlModel) => memoizeWeak((permissionsLookup, vocabulary) => {
509
542
  const constrainedAbstractSqlModel = _.cloneDeep(abstractSqlModel);
510
- const origSynonyms = Object.keys(constrainedAbstractSqlModel.synonyms);
543
+ const origSynonyms = Object.entries(constrainedAbstractSqlModel.synonyms);
511
544
  constrainedAbstractSqlModel.synonyms = new Proxy(constrainedAbstractSqlModel.synonyms, {
512
- get: (synonyms, permissionSynonym) => {
545
+ get(synonyms, permissionSynonym, receiver) {
546
+ if (typeof permissionSynonym === 'symbol') {
547
+ return Reflect.get(synonyms, permissionSynonym, receiver);
548
+ }
513
549
  if (synonyms[permissionSynonym]) {
514
550
  return synonyms[permissionSynonym];
515
551
  }
@@ -517,9 +553,9 @@ const getBoundConstrainedMemoizer = memoizeWeak((abstractSqlModel) => memoizeWea
517
553
  if (!alias) {
518
554
  return;
519
555
  }
520
- origSynonyms.forEach((canonicalForm, synonym) => {
556
+ for (const [synonym, canonicalForm] of origSynonyms) {
521
557
  synonyms[`${synonym}$${alias}`] = `${canonicalForm}$${alias}`;
522
- });
558
+ }
523
559
  return synonyms[permissionSynonym];
524
560
  },
525
561
  });
@@ -529,20 +565,25 @@ const getBoundConstrainedMemoizer = memoizeWeak((abstractSqlModel) => memoizeWea
529
565
  constrainedAbstractSqlModel.tables[bypassResourceName] = {
530
566
  ...table,
531
567
  };
532
- constrainedAbstractSqlModel.tables[bypassResourceName].resourceName = bypassResourceName;
568
+ constrainedAbstractSqlModel.tables[bypassResourceName].resourceName =
569
+ bypassResourceName;
533
570
  if (table.definition) {
534
- constrainedAbstractSqlModel.tables[bypassResourceName].definition = createBypassDefinition(table.definition);
571
+ constrainedAbstractSqlModel.tables[bypassResourceName].definition =
572
+ createBypassDefinition(table.definition);
535
573
  }
536
574
  else {
537
575
  onceGetter(table, 'definition', () => constrainedAbstractSqlModel.tables[`${resourceName}$permissions${stringifiedGetPermissions}`].definition);
538
576
  }
539
577
  });
540
578
  constrainedAbstractSqlModel.tables = new Proxy(constrainedAbstractSqlModel.tables, {
541
- get: (tables, permissionResourceName) => {
579
+ get(tables, permissionResourceName, receiver) {
580
+ if (typeof permissionResourceName === 'symbol') {
581
+ return Reflect.get(tables, permissionResourceName, receiver);
582
+ }
542
583
  if (tables[permissionResourceName]) {
543
584
  return tables[permissionResourceName];
544
585
  }
545
- const [resourceName, permissionsJSON,] = permissionResourceName.split('$permissions');
586
+ const [resourceName, permissionsJSON] = permissionResourceName.split('$permissions');
546
587
  if (!permissionsJSON) {
547
588
  return;
548
589
  }
@@ -552,13 +593,16 @@ const getBoundConstrainedMemoizer = memoizeWeak((abstractSqlModel) => memoizeWea
552
593
  ...table,
553
594
  });
554
595
  permissionsTable.resourceName = permissionResourceName;
555
- onceGetter(permissionsTable, 'definition', () => generateConstrainedAbstractSql(permissionsLookup, permissions, vocabulary, odata_to_abstract_sql_1.sqlNameToODataName(permissionsTable.name)));
596
+ onceGetter(permissionsTable, 'definition', () => generateConstrainedAbstractSql(permissionsLookup, permissions, vocabulary, (0, odata_to_abstract_sql_1.sqlNameToODataName)(permissionsTable.modifyName ?? permissionsTable.name)));
556
597
  return permissionsTable;
557
598
  },
558
599
  });
559
600
  constrainedAbstractSqlModel.relationships = rewriteRelationships(constrainedAbstractSqlModel, constrainedAbstractSqlModel.relationships, permissionsLookup, vocabulary);
560
601
  constrainedAbstractSqlModel.relationships = new Proxy(constrainedAbstractSqlModel.relationships, {
561
- get: (relationships, permissionResourceName) => {
602
+ get(relationships, permissionResourceName, receiver) {
603
+ if (typeof permissionResourceName === 'symbol') {
604
+ return Reflect.get(relationships, permissionResourceName, receiver);
605
+ }
562
606
  if (relationships[permissionResourceName]) {
563
607
  return relationships[permissionResourceName];
564
608
  }
@@ -606,7 +650,7 @@ const checkPassword = async (username, password) => {
606
650
  if (!res) {
607
651
  throw new Error('Passwords do not match');
608
652
  }
609
- const permissions = await exports.getUserPermissions(userId);
653
+ const permissions = await (0, exports.getUserPermissions)(userId);
610
654
  return {
611
655
  id: userId,
612
656
  actor: actorId,
@@ -615,58 +659,60 @@ const checkPassword = async (username, password) => {
615
659
  };
616
660
  };
617
661
  exports.checkPassword = checkPassword;
618
- const getUserPermissionsQuery = _.once(() => sbvrUtils.api.Auth.prepare({
619
- resource: 'permission',
620
- passthrough: {
621
- req: exports.rootRead,
622
- },
623
- options: {
624
- $select: 'name',
625
- $filter: {
626
- $or: {
627
- is_of__user: {
628
- $any: {
629
- $alias: 'uhp',
630
- $expr: {
631
- uhp: { user: { '@': 'userId' } },
632
- $or: [
633
- {
634
- uhp: { expiry_date: null },
635
- },
636
- {
637
- uhp: {
638
- expiry_date: { $gt: { $now: null } },
662
+ const $getUserPermissions = (() => {
663
+ const getUserPermissionsQuery = _.once(() => sbvrUtils.api.Auth.prepare({
664
+ resource: 'permission',
665
+ passthrough: {
666
+ req: exports.rootRead,
667
+ },
668
+ options: {
669
+ $select: 'name',
670
+ $filter: {
671
+ $or: {
672
+ is_of__user: {
673
+ $any: {
674
+ $alias: 'uhp',
675
+ $expr: {
676
+ uhp: { user: { '@': 'userId' } },
677
+ $or: [
678
+ {
679
+ uhp: { expiry_date: null },
639
680
  },
640
- },
641
- ],
681
+ {
682
+ uhp: {
683
+ expiry_date: { $gt: { $now: null } },
684
+ },
685
+ },
686
+ ],
687
+ },
642
688
  },
643
689
  },
644
- },
645
- is_of__role: {
646
- $any: {
647
- $alias: 'rhp',
648
- $expr: {
649
- rhp: {
650
- role: {
651
- $any: {
652
- $alias: 'r',
653
- $expr: {
654
- r: {
655
- is_of__user: {
656
- $any: {
657
- $alias: 'uhr',
658
- $expr: {
659
- uhr: { user: { '@': 'userId' } },
660
- $or: [
661
- {
662
- uhr: { expiry_date: null },
663
- },
664
- {
665
- uhr: {
666
- expiry_date: { $gt: { $now: null } },
690
+ is_of__role: {
691
+ $any: {
692
+ $alias: 'rhp',
693
+ $expr: {
694
+ rhp: {
695
+ role: {
696
+ $any: {
697
+ $alias: 'r',
698
+ $expr: {
699
+ r: {
700
+ is_of__user: {
701
+ $any: {
702
+ $alias: 'uhr',
703
+ $expr: {
704
+ uhr: { user: { '@': 'userId' } },
705
+ $or: [
706
+ {
707
+ uhr: { expiry_date: null },
667
708
  },
668
- },
669
- ],
709
+ {
710
+ uhr: {
711
+ expiry_date: { $gt: { $now: null } },
712
+ },
713
+ },
714
+ ],
715
+ },
670
716
  },
671
717
  },
672
718
  },
@@ -679,13 +725,23 @@ const getUserPermissionsQuery = _.once(() => sbvrUtils.api.Auth.prepare({
679
725
  },
680
726
  },
681
727
  },
728
+ $orderby: {
729
+ name: 'asc',
730
+ },
682
731
  },
683
- $orderby: {
684
- name: 'asc',
685
- },
686
- },
687
- }));
688
- const getUserPermissions = async (userId) => {
732
+ }));
733
+ return env.createCache('userPermissions', async (userId, tx) => {
734
+ const permissions = (await getUserPermissionsQuery()({
735
+ userId,
736
+ }, undefined, { tx }));
737
+ return permissions.map((permission) => permission.name);
738
+ }, {
739
+ primitive: true,
740
+ promise: true,
741
+ normalizer: ([userId]) => `${userId}`,
742
+ });
743
+ })();
744
+ const getUserPermissions = async (userId, tx) => {
689
745
  if (typeof userId === 'string') {
690
746
  userId = parseInt(userId, 10);
691
747
  }
@@ -693,10 +749,7 @@ const getUserPermissions = async (userId) => {
693
749
  throw new Error(`User ID has to be numeric, got: ${typeof userId}`);
694
750
  }
695
751
  try {
696
- const permissions = (await getUserPermissionsQuery()({
697
- userId,
698
- }));
699
- return permissions.map((permission) => permission.name);
752
+ return await $getUserPermissions(userId, tx);
700
753
  }
701
754
  catch (err) {
702
755
  sbvrUtils.api.Auth.logger.error('Error loading user permissions', err);
@@ -704,52 +757,76 @@ const getUserPermissions = async (userId) => {
704
757
  }
705
758
  };
706
759
  exports.getUserPermissions = getUserPermissions;
707
- const getApiKeyPermissionsQuery = _.once(() => sbvrUtils.api.Auth.prepare({
708
- resource: 'permission',
709
- passthrough: {
710
- req: exports.rootRead,
711
- },
712
- options: {
713
- $select: 'name',
714
- $filter: {
715
- $or: {
716
- is_of__api_key: {
717
- $any: {
718
- $alias: 'khp',
719
- $expr: {
720
- khp: {
721
- api_key: {
722
- $any: {
723
- $alias: 'k',
724
- $expr: {
725
- k: { key: { '@': 'apiKey' } },
760
+ const $getApiKeyPermissions = (() => {
761
+ const getApiKeyPermissionsQuery = _.once(() => sbvrUtils.api.Auth.prepare({
762
+ resource: 'permission',
763
+ passthrough: {
764
+ req: exports.rootRead,
765
+ },
766
+ options: {
767
+ $select: 'name',
768
+ $filter: {
769
+ $or: {
770
+ is_of__api_key: {
771
+ $any: {
772
+ $alias: 'khp',
773
+ $expr: {
774
+ khp: {
775
+ api_key: {
776
+ $any: {
777
+ $alias: 'k',
778
+ $expr: {
779
+ k: { key: { '@': 'apiKey' } },
780
+ $or: [
781
+ {
782
+ k: { expiry_date: null },
783
+ },
784
+ {
785
+ k: {
786
+ expiry_date: { $gt: { $now: null } },
787
+ },
788
+ },
789
+ ],
790
+ },
726
791
  },
727
792
  },
728
793
  },
729
794
  },
730
795
  },
731
796
  },
732
- },
733
- is_of__role: {
734
- $any: {
735
- $alias: 'rhp',
736
- $expr: {
737
- rhp: {
738
- role: {
739
- $any: {
740
- $alias: 'r',
741
- $expr: {
742
- r: {
743
- is_of__api_key: {
744
- $any: {
745
- $alias: 'khr',
746
- $expr: {
747
- khr: {
748
- api_key: {
749
- $any: {
750
- $alias: 'k',
751
- $expr: {
752
- k: { key: { '@': 'apiKey' } },
797
+ is_of__role: {
798
+ $any: {
799
+ $alias: 'rhp',
800
+ $expr: {
801
+ rhp: {
802
+ role: {
803
+ $any: {
804
+ $alias: 'r',
805
+ $expr: {
806
+ r: {
807
+ is_of__api_key: {
808
+ $any: {
809
+ $alias: 'khr',
810
+ $expr: {
811
+ khr: {
812
+ api_key: {
813
+ $any: {
814
+ $alias: 'k',
815
+ $expr: {
816
+ k: { key: { '@': 'apiKey' } },
817
+ $or: [
818
+ {
819
+ k: { expiry_date: null },
820
+ },
821
+ {
822
+ k: {
823
+ expiry_date: {
824
+ $gt: { $now: null },
825
+ },
826
+ },
827
+ },
828
+ ],
829
+ },
753
830
  },
754
831
  },
755
832
  },
@@ -766,21 +843,28 @@ const getApiKeyPermissionsQuery = _.once(() => sbvrUtils.api.Auth.prepare({
766
843
  },
767
844
  },
768
845
  },
846
+ $orderby: {
847
+ name: 'asc',
848
+ },
769
849
  },
770
- $orderby: {
771
- name: 'asc',
772
- },
773
- },
774
- }));
775
- const getApiKeyPermissions = async (apiKey) => {
850
+ }));
851
+ return env.createCache('apiKeyPermissions', async (apiKey, tx) => {
852
+ const permissions = (await getApiKeyPermissionsQuery()({
853
+ apiKey,
854
+ }, undefined, { tx }));
855
+ return permissions.map((permission) => permission.name);
856
+ }, {
857
+ primitive: true,
858
+ promise: true,
859
+ normalizer: ([apiKey]) => apiKey,
860
+ });
861
+ })();
862
+ const getApiKeyPermissions = async (apiKey, tx) => {
776
863
  if (typeof apiKey !== 'string') {
777
864
  throw new Error('API key has to be a string, got: ' + typeof apiKey);
778
865
  }
779
866
  try {
780
- const permissions = (await getApiKeyPermissionsQuery()({
781
- apiKey,
782
- }));
783
- return permissions.map((permission) => permission.name);
867
+ return await $getApiKeyPermissions(apiKey, tx);
784
868
  }
785
869
  catch (err) {
786
870
  sbvrUtils.api.Auth.logger.error('Error loading api key permissions', err);
@@ -788,58 +872,54 @@ const getApiKeyPermissions = async (apiKey) => {
788
872
  }
789
873
  };
790
874
  exports.getApiKeyPermissions = getApiKeyPermissions;
791
- const getApiKeyActorIdQuery = _.once(() => sbvrUtils.api.Auth.prepare({
792
- resource: 'api_key',
793
- passthrough: {
794
- req: exports.rootRead,
795
- },
796
- id: {
797
- key: { '@': 'apiKey' },
798
- },
799
- options: {
800
- $select: 'is_of__actor',
801
- },
802
- }));
803
- const apiActorPermissionError = new errors_1.PermissionError();
804
- const getApiKeyActorId = async (apiKey) => {
805
- const apiKeyResult = await getApiKeyActorIdQuery()({
806
- apiKey,
875
+ const getApiKeyActorId = (() => {
876
+ const getApiKeyActorIdQuery = _.once(() => sbvrUtils.api.Auth.prepare({
877
+ resource: 'api_key',
878
+ passthrough: {
879
+ req: exports.rootRead,
880
+ },
881
+ id: {
882
+ key: { '@': 'apiKey' },
883
+ },
884
+ options: {
885
+ $select: 'is_of__actor',
886
+ $filter: {
887
+ $or: [
888
+ { expiry_date: null },
889
+ { expiry_date: { $gt: { $now: null } } },
890
+ ],
891
+ },
892
+ },
893
+ }));
894
+ const apiActorPermissionError = new errors_1.PermissionError();
895
+ return env.createCache('apiKeyActorId', async (apiKey, tx) => {
896
+ const apiKeyResult = await getApiKeyActorIdQuery()({
897
+ apiKey,
898
+ }, undefined, { tx });
899
+ if (apiKeyResult == null) {
900
+ throw apiActorPermissionError;
901
+ }
902
+ const apiKeyActorID = apiKeyResult.is_of__actor.__id;
903
+ if (apiKeyActorID == null) {
904
+ throw new Error('API key is not linked to a actor?!');
905
+ }
906
+ return apiKeyActorID;
907
+ }, {
908
+ promise: true,
909
+ primitive: true,
910
+ normalizer: ([apiKey]) => apiKey,
807
911
  });
808
- if (apiKeyResult == null) {
809
- throw apiActorPermissionError;
810
- }
811
- const apiKeyActorID = apiKeyResult.is_of__actor.__id;
812
- if (apiKeyActorID == null) {
813
- throw new Error('API key is not linked to a actor?!');
814
- }
815
- return apiKeyActorID;
816
- };
817
- const checkApiKey = async (req, apiKey) => {
818
- if (apiKey == null || req.apiKey != null) {
819
- return;
820
- }
821
- let permissions;
822
- try {
823
- permissions = await exports.getApiKeyPermissions(apiKey);
824
- }
825
- catch (err) {
826
- console.warn('Error with API key:', err);
827
- permissions = [];
828
- }
829
- let actor;
830
- if (permissions.length > 0) {
831
- actor = await getApiKeyActorId(apiKey);
832
- }
833
- const resolvedApiKey = {
912
+ })();
913
+ const checkApiKey = async (apiKey, tx) => {
914
+ const permissions = await (0, exports.getApiKeyPermissions)(apiKey, tx);
915
+ const actor = await getApiKeyActorId(apiKey, tx);
916
+ return {
834
917
  key: apiKey,
835
918
  permissions,
919
+ actor,
836
920
  };
837
- if (actor != null) {
838
- resolvedApiKey.actor = actor;
839
- }
840
- return resolvedApiKey;
841
921
  };
842
- const resolveAuthHeader = async (req, expectedScheme = 'Bearer') => {
922
+ const resolveAuthHeader = async (req, expectedScheme = 'Bearer', tx) => {
843
923
  const auth = req.header('Authorization');
844
924
  if (!auth) {
845
925
  return;
@@ -852,52 +932,48 @@ const resolveAuthHeader = async (req, expectedScheme = 'Bearer') => {
852
932
  if (scheme.toLowerCase() !== expectedScheme.toLowerCase()) {
853
933
  return;
854
934
  }
855
- return await checkApiKey(req, apiKey);
935
+ return await checkApiKey(apiKey, tx);
856
936
  };
857
937
  exports.resolveAuthHeader = resolveAuthHeader;
858
938
  const customAuthorizationMiddleware = (expectedScheme = 'Bearer') => {
859
939
  expectedScheme = expectedScheme.toLowerCase();
860
940
  return async (req, _res, next) => {
861
941
  try {
862
- const apiKey = await exports.resolveAuthHeader(req, expectedScheme);
942
+ const apiKey = await (0, exports.resolveAuthHeader)(req, expectedScheme);
863
943
  if (apiKey) {
864
944
  req.apiKey = apiKey;
865
945
  }
866
946
  }
867
947
  finally {
868
- next === null || next === void 0 ? void 0 : next();
948
+ next?.();
869
949
  }
870
950
  };
871
951
  };
872
952
  exports.customAuthorizationMiddleware = customAuthorizationMiddleware;
873
- exports.authorizationMiddleware = exports.customAuthorizationMiddleware();
874
- const resolveApiKey = async (req, paramName = 'apikey') => {
875
- const apiKey = req.params[paramName] != null
876
- ? req.params[paramName]
877
- : req.body[paramName] != null
878
- ? req.body[paramName]
879
- : req.query[paramName];
880
- return await checkApiKey(req, apiKey);
953
+ exports.authorizationMiddleware = (0, exports.customAuthorizationMiddleware)();
954
+ const resolveApiKey = async (req, paramName = 'apikey', tx) => {
955
+ const apiKey = req.params[paramName] ?? req.body[paramName] ?? req.query[paramName];
956
+ if (apiKey == null) {
957
+ return;
958
+ }
959
+ return await checkApiKey(apiKey, tx);
881
960
  };
882
961
  exports.resolveApiKey = resolveApiKey;
883
962
  const customApiKeyMiddleware = (paramName = 'apikey') => {
884
- if (paramName == null) {
885
- paramName = 'apikey';
886
- }
887
963
  return async (req, _res, next) => {
888
964
  try {
889
- const apiKey = await exports.resolveApiKey(req, paramName);
965
+ const apiKey = await (0, exports.resolveApiKey)(req, paramName);
890
966
  if (apiKey) {
891
967
  req.apiKey = apiKey;
892
968
  }
893
969
  }
894
970
  finally {
895
- next === null || next === void 0 ? void 0 : next();
971
+ next?.();
896
972
  }
897
973
  };
898
974
  };
899
975
  exports.customApiKeyMiddleware = customApiKeyMiddleware;
900
- exports.apiKeyMiddleware = exports.customApiKeyMiddleware();
976
+ exports.apiKeyMiddleware = (0, exports.customApiKeyMiddleware)();
901
977
  const checkPermissions = async (req, actionList, resourceName, vocabulary) => {
902
978
  const permissionsLookup = await getReqPermissions(req);
903
979
  return $checkPermissions(permissionsLookup, actionList, vocabulary, resourceName);
@@ -905,10 +981,10 @@ const checkPermissions = async (req, actionList, resourceName, vocabulary) => {
905
981
  exports.checkPermissions = checkPermissions;
906
982
  const checkPermissionsMiddleware = (action) => async (req, res, next) => {
907
983
  try {
908
- const allowed = await exports.checkPermissions(req, action);
984
+ const allowed = await (0, exports.checkPermissions)(req, action);
909
985
  switch (allowed) {
910
986
  case false:
911
- res.sendStatus(401);
987
+ res.status(401).end();
912
988
  return;
913
989
  case true:
914
990
  next();
@@ -918,11 +994,12 @@ const checkPermissionsMiddleware = (action) => async (req, res, next) => {
918
994
  }
919
995
  }
920
996
  catch (err) {
921
- sbvrUtils.api.Auth.logger.error('Error checking permissions', err, err.stack);
922
- res.sendStatus(503);
997
+ sbvrUtils.api.Auth.logger.error('Error checking permissions', err);
998
+ res.status(503).end();
923
999
  }
924
1000
  };
925
1001
  exports.checkPermissionsMiddleware = checkPermissionsMiddleware;
1002
+ let guestPermissionsInitialized = false;
926
1003
  const getGuestPermissions = memoize(async () => {
927
1004
  const result = (await sbvrUtils.api.Auth.get({
928
1005
  resource: 'user',
@@ -939,75 +1016,56 @@ const getGuestPermissions = memoize(async () => {
939
1016
  if (result == null) {
940
1017
  throw new Error('No guest user');
941
1018
  }
942
- return _.uniq(await exports.getUserPermissions(result.id));
943
- }, { promise: true });
944
- const getReqPermissions = async (req, odataBinds = []) => {
945
- const [guestPermissions] = await Promise.all([
946
- getGuestPermissions(),
947
- (async () => {
948
- if (req.apiKey != null &&
949
- req.apiKey.actor == null &&
950
- req.apiKey.permissions != null &&
951
- req.apiKey.permissions.length > 0) {
952
- const actorId = await getApiKeyActorId(req.apiKey.key);
953
- req.apiKey.actor = actorId;
954
- }
955
- })(),
956
- ]);
1019
+ const guestPermissions = _.uniq(await (0, exports.getUserPermissions)(result.id));
957
1020
  if (guestPermissions.some((p) => DEFAULT_ACTOR_BIND_REGEX.test(p))) {
958
1021
  throw new Error('Guest permissions cannot reference actors');
959
1022
  }
960
- let permissions = guestPermissions;
961
- let actorIndex = 0;
962
- const addActorPermissions = (actorId, actorPermissions) => {
963
- let actorBind = DEFAULT_ACTOR_BIND;
964
- if (actorIndex > 0) {
965
- actorBind += actorIndex;
966
- actorPermissions = actorPermissions.map((actorPermission) => actorPermission.replace(DEFAULT_ACTOR_BIND_REGEX, actorBind));
1023
+ guestPermissionsInitialized = true;
1024
+ return guestPermissions;
1025
+ }, { promise: true });
1026
+ const getReqPermissions = async (req, odataBinds = []) => {
1027
+ const guestPermissions = await (async () => {
1028
+ if (guestPermissionsInitialized === false &&
1029
+ (req.user === exports.root.user || req.user === exports.rootRead.user)) {
1030
+ return [];
967
1031
  }
968
- odataBinds[actorBind] = ['Real', actorId];
969
- actorIndex++;
970
- permissions = permissions.concat(actorPermissions);
1032
+ return await getGuestPermissions();
1033
+ })();
1034
+ let actorPermissions = [];
1035
+ const addActorPermissions = (actorId, perms) => {
1036
+ odataBinds[DEFAULT_ACTOR_BIND] = ['Real', actorId];
1037
+ actorPermissions = perms;
971
1038
  };
972
- if (req.user != null && req.user.permissions != null) {
1039
+ if (req.user?.permissions != null) {
973
1040
  addActorPermissions(req.user.actor, req.user.permissions);
974
1041
  }
975
- else if (req.apiKey != null && req.apiKey.permissions != null) {
1042
+ else if (req.apiKey?.permissions != null) {
976
1043
  addActorPermissions(req.apiKey.actor, req.apiKey.permissions);
977
1044
  }
978
- permissions = _.uniq(permissions);
979
- return getPermissionsLookup(permissions);
1045
+ return getPermissionsLookup(actorPermissions, guestPermissions);
980
1046
  };
981
1047
  const addPermissions = async (req, request) => {
982
- var _a, _b, _c, _d;
983
- const { vocabulary, resourceName, odataQuery, odataBinds } = request;
984
- let { method } = request;
1048
+ const { resourceName, odataQuery, odataBinds } = request;
1049
+ const vocabulary = _.last(request.translateVersions);
985
1050
  let abstractSqlModel = sbvrUtils.getAbstractSqlModel(request);
986
- method = method.toUpperCase();
987
- const isMetadataEndpoint = uri_parser_1.metadataEndpoints.includes(resourceName) || method === 'OPTIONS';
988
- let permissionType;
989
- if (request.permissionType != null) {
990
- permissionType = request.permissionType;
991
- }
992
- else if (isMetadataEndpoint) {
993
- permissionType = 'model';
994
- }
995
- else {
996
- const methodPermission = methodPermissions[method];
997
- if (methodPermission != null) {
998
- permissionType = methodPermission;
1051
+ let { permissionType } = request;
1052
+ if (permissionType == null) {
1053
+ const method = request.method.toUpperCase();
1054
+ const isMetadataEndpoint = method === 'OPTIONS' || uri_parser_1.metadataEndpoints.includes(resourceName);
1055
+ if (isMetadataEndpoint) {
1056
+ permissionType = 'model';
999
1057
  }
1000
1058
  else {
1001
- console.warn('Unknown method for permissions type check: ', method);
1002
- permissionType = 'all';
1059
+ const methodPermission = methodPermissions[method];
1060
+ if (methodPermission != null) {
1061
+ permissionType = methodPermission;
1062
+ }
1063
+ else {
1064
+ console.warn('Unknown method for permissions type check: ', method);
1065
+ permissionType = 'all';
1066
+ }
1003
1067
  }
1004
1068
  }
1005
- let permissions = (_b = (_a = req.user) === null || _a === void 0 ? void 0 : _a.permissions) !== null && _b !== void 0 ? _b : [];
1006
- permissions = permissions.concat((_d = (_c = req.apiKey) === null || _c === void 0 ? void 0 : _c.permissions) !== null && _d !== void 0 ? _d : []);
1007
- if (permissions.length > 0 &&
1008
- $checkPermissions(getPermissionsLookup(permissions), permissionType, vocabulary) === true) {
1009
- return;
1010
- }
1011
1069
  const permissionsLookup = await getReqPermissions(req, odataBinds);
1012
1070
  request.abstractSqlModel = abstractSqlModel = memoizedGetConstrainedModel(abstractSqlModel, permissionsLookup, vocabulary);
1013
1071
  if (!_.isEqual(permissionType, methodPermissions.GET)) {
@@ -1051,22 +1109,25 @@ exports.config = {
1051
1109
  ALTER TABLE "role-has-permission"
1052
1110
  ADD COLUMN IF NOT EXISTS "modified at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL;
1053
1111
  `,
1112
+ '14.42.0-api-key-expiry-date': `
1113
+ ALTER TABLE "api key"
1114
+ ADD COLUMN IF NOT EXISTS "expiry date" TIMESTAMP NULL;
1115
+ `,
1054
1116
  },
1055
1117
  },
1056
1118
  ],
1057
1119
  };
1058
1120
  const setup = () => {
1059
- hooks_1.addHook('all', 'all', 'all', {
1121
+ (0, hooks_1.addHook)('all', 'all', 'all', {
1060
1122
  sideEffects: false,
1061
1123
  readOnlyTx: true,
1062
- PREPARSE: ({ req }) => exports.apiKeyMiddleware(req),
1124
+ PREPARSE: ({ req }) => (0, exports.apiKeyMiddleware)(req),
1063
1125
  POSTPARSE: async ({ req, request, }) => {
1064
1126
  if (request.abstractSqlQuery != null) {
1065
1127
  return;
1066
1128
  }
1067
1129
  if (request.method === 'POST' &&
1068
- request.odataQuery.property != null &&
1069
- request.odataQuery.property.resource === 'canAccess') {
1130
+ request.odataQuery.property?.resource === 'canAccess') {
1070
1131
  if (request.odataQuery.key == null) {
1071
1132
  throw new errors_1.BadRequestError();
1072
1133
  }
@@ -1086,6 +1147,7 @@ const setup = () => {
1086
1147
  }
1087
1148
  const abstractSqlModel = sbvrUtils.getAbstractSqlModel(request);
1088
1149
  request.resourceName = request.resourceName.slice(0, -'#canAccess'.length);
1150
+ request.originalResourceName = request.originalResourceName.slice(0, -'#canAccess'.length);
1089
1151
  const resourceName = sbvrUtils.resolveSynonym(request);
1090
1152
  const resourceTable = abstractSqlModel.tables[resourceName];
1091
1153
  if (resourceTable == null) {
@@ -1101,15 +1163,18 @@ const setup = () => {
1101
1163
  request.method = 'GET';
1102
1164
  request.custom.isAction = 'canAccess';
1103
1165
  }
1104
- await exports.addPermissions(req, request);
1166
+ await (0, exports.addPermissions)(req, request);
1105
1167
  },
1106
- PRERESPOND: ({ request, data }) => {
1107
- if (request.custom.isAction === 'canAccess' && _.isEmpty(data)) {
1168
+ PRERESPOND: ({ request, response }) => {
1169
+ if (request.custom.isAction === 'canAccess' &&
1170
+ (response.body == null ||
1171
+ typeof response.body === 'string' ||
1172
+ _.isEmpty(response.body?.d))) {
1108
1173
  throw new errors_1.PermissionError();
1109
1174
  }
1110
1175
  },
1111
1176
  });
1112
- hooks_1.addPureHook('POST', 'Auth', 'user', {
1177
+ (0, hooks_1.addPureHook)('POST', 'Auth', 'user', {
1113
1178
  POSTPARSE: async ({ request, api }) => {
1114
1179
  const result = await api.post({
1115
1180
  resource: 'actor',
@@ -1118,7 +1183,7 @@ const setup = () => {
1118
1183
  request.values.actor = result.id;
1119
1184
  },
1120
1185
  });
1121
- hooks_1.addPureHook('DELETE', 'Auth', 'user', {
1186
+ (0, hooks_1.addPureHook)('DELETE', 'Auth', 'user', {
1122
1187
  POSTRUN: ({ request, api }) => api.delete({
1123
1188
  resource: 'actor',
1124
1189
  id: request.values.actor,