@flowerforce/flowerbase 1.7.5-beta.2 → 1.7.5-beta.4

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 (63) hide show
  1. package/dist/features/functions/controller.d.ts +2 -0
  2. package/dist/features/functions/controller.d.ts.map +1 -1
  3. package/dist/features/functions/controller.js +244 -19
  4. package/dist/services/api/index.d.ts +4 -0
  5. package/dist/services/api/index.d.ts.map +1 -1
  6. package/dist/services/api/utils.d.ts +1 -0
  7. package/dist/services/api/utils.d.ts.map +1 -1
  8. package/dist/services/index.d.ts +4 -0
  9. package/dist/services/index.d.ts.map +1 -1
  10. package/dist/services/mongodb-atlas/utils.d.ts.map +1 -1
  11. package/dist/services/mongodb-atlas/utils.js +17 -1
  12. package/dist/utils/context/helpers.d.ts +12 -0
  13. package/dist/utils/context/helpers.d.ts.map +1 -1
  14. package/dist/utils/roles/helpers.d.ts.map +1 -1
  15. package/dist/utils/roles/helpers.js +19 -4
  16. package/dist/utils/roles/interface.d.ts +10 -6
  17. package/dist/utils/roles/interface.d.ts.map +1 -1
  18. package/dist/utils/roles/machines/commonValidators.js +2 -2
  19. package/dist/utils/roles/machines/fieldPermissions.d.ts +8 -0
  20. package/dist/utils/roles/machines/fieldPermissions.d.ts.map +1 -0
  21. package/dist/utils/roles/machines/fieldPermissions.js +67 -0
  22. package/dist/utils/roles/machines/read/A/index.d.ts.map +1 -1
  23. package/dist/utils/roles/machines/read/A/index.js +4 -3
  24. package/dist/utils/roles/machines/read/C/index.d.ts.map +1 -1
  25. package/dist/utils/roles/machines/read/C/index.js +16 -16
  26. package/dist/utils/roles/machines/read/C/validators.js +2 -2
  27. package/dist/utils/roles/machines/read/D/index.js +1 -1
  28. package/dist/utils/roles/machines/read/D/validators.d.ts +1 -1
  29. package/dist/utils/roles/machines/read/D/validators.d.ts.map +1 -1
  30. package/dist/utils/roles/machines/read/D/validators.js +19 -21
  31. package/dist/utils/roles/machines/write/B/index.d.ts.map +1 -1
  32. package/dist/utils/roles/machines/write/B/index.js +12 -9
  33. package/dist/utils/roles/machines/write/C/index.js +1 -1
  34. package/dist/utils/roles/machines/write/C/validators.d.ts +1 -1
  35. package/dist/utils/roles/machines/write/C/validators.d.ts.map +1 -1
  36. package/dist/utils/roles/machines/write/C/validators.js +16 -21
  37. package/package.json +1 -1
  38. package/src/features/functions/__tests__/watch-filter.test.ts +116 -0
  39. package/src/features/functions/controller.ts +282 -22
  40. package/src/features/triggers/__tests__/index.test.ts +2 -2
  41. package/src/services/mongodb-atlas/__tests__/findOneAndUpdate.test.ts +1 -1
  42. package/src/services/mongodb-atlas/utils.ts +19 -4
  43. package/src/utils/__tests__/STEP_A_STATES.test.ts +24 -2
  44. package/src/utils/__tests__/STEP_C_STATES.test.ts +61 -27
  45. package/src/utils/__tests__/STEP_D_STATES.test.ts +9 -9
  46. package/src/utils/__tests__/WRITE_STEP_B_STATES.test.ts +184 -0
  47. package/src/utils/__tests__/checkAdditionalFieldsFn.test.ts +2 -2
  48. package/src/utils/__tests__/checkFieldsPropertyExists.test.ts +13 -0
  49. package/src/utils/__tests__/checkIsValidFieldNameFn.test.ts +52 -121
  50. package/src/utils/__tests__/evaluateTopLevelReadFn.test.ts +10 -1
  51. package/src/utils/__tests__/evaluateTopLevelWriteFn.test.ts +21 -5
  52. package/src/utils/roles/helpers.ts +18 -4
  53. package/src/utils/roles/interface.ts +13 -6
  54. package/src/utils/roles/machines/commonValidators.ts +1 -1
  55. package/src/utils/roles/machines/fieldPermissions.ts +86 -0
  56. package/src/utils/roles/machines/read/A/index.ts +4 -3
  57. package/src/utils/roles/machines/read/C/index.ts +18 -18
  58. package/src/utils/roles/machines/read/C/validators.ts +2 -2
  59. package/src/utils/roles/machines/read/D/index.ts +1 -1
  60. package/src/utils/roles/machines/read/D/validators.ts +12 -25
  61. package/src/utils/roles/machines/write/B/index.ts +12 -9
  62. package/src/utils/roles/machines/write/C/index.ts +1 -1
  63. package/src/utils/roles/machines/write/C/validators.ts +9 -26
@@ -19,26 +19,41 @@ const context_1 = require("../context");
19
19
  const rules_1 = require("../rules");
20
20
  const utils_1 = __importDefault(require("../rules-matcher/utils"));
21
21
  const functionsConditions = ['%%true', '%%false'];
22
+ const normalizeUserRole = (user) => {
23
+ if (!user)
24
+ return user;
25
+ if (typeof user !== 'object')
26
+ return user;
27
+ const candidate = user;
28
+ if (typeof candidate.role === 'string')
29
+ return user;
30
+ const customRole = typeof candidate.custom_data === 'object' && candidate.custom_data !== null
31
+ ? candidate.custom_data.role
32
+ : undefined;
33
+ return typeof customRole === 'string' ? Object.assign(Object.assign({}, candidate), { role: customRole }) : user;
34
+ };
22
35
  const evaluateExpression = (params, expression, user) => __awaiter(void 0, void 0, void 0, function* () {
23
36
  if (!expression || typeof expression === 'boolean')
24
37
  return !!expression;
25
- const value = Object.assign(Object.assign(Object.assign({}, params.expansions), params.cursor), { '%%user': user, '%%true': true });
38
+ const normalizedUser = normalizeUserRole(user);
39
+ const value = Object.assign(Object.assign(Object.assign({}, params.expansions), params.cursor), { '%%user': normalizedUser, '%%true': true });
26
40
  const conditions = (0, rules_1.expandQuery)(expression, value);
27
41
  const complexCondition = Object.entries(conditions).find(([key]) => functionsConditions.includes(key));
28
42
  return complexCondition
29
- ? yield evaluateComplexExpression(complexCondition, params, user)
43
+ ? yield evaluateComplexExpression(complexCondition, params, normalizedUser)
30
44
  : utils_1.default.checkRule(conditions, value, {});
31
45
  });
32
46
  exports.evaluateExpression = evaluateExpression;
33
47
  const evaluateComplexExpression = (condition, params, user) => __awaiter(void 0, void 0, void 0, function* () {
34
48
  var _a;
35
49
  const [key, config] = condition;
50
+ const normalizedUser = normalizeUserRole(user);
36
51
  const functionConfig = config['%function'];
37
52
  const { name, arguments: fnArguments } = functionConfig;
38
53
  const functionsList = state_1.StateManager.select('functions');
39
54
  const app = state_1.StateManager.select('app');
40
55
  const currentFunction = functionsList[name];
41
- const expansionContext = Object.assign(Object.assign(Object.assign({}, params.expansions), params.cursor), { '%%root': params.cursor, '%%user': user, '%%true': true, '%%false': false });
56
+ const expansionContext = Object.assign(Object.assign(Object.assign({}, params.expansions), params.cursor), { '%%root': params.cursor, '%%user': normalizedUser, '%%true': true, '%%false': false });
42
57
  const expandedArguments = fnArguments && fnArguments.length
43
58
  ? ((_a = (0, rules_1.expandQuery)({ args: fnArguments }, expansionContext)
44
59
  .args) !== null && _a !== void 0 ? _a : [])
@@ -47,7 +62,7 @@ const evaluateComplexExpression = (condition, params, user) => __awaiter(void 0,
47
62
  args: expandedArguments,
48
63
  app,
49
64
  rules: state_1.StateManager.select("rules"),
50
- user,
65
+ user: normalizedUser,
51
66
  currentFunction,
52
67
  functionName: name,
53
68
  functionsList,
@@ -1,7 +1,13 @@
1
- export type PermissionExpression = boolean;
1
+ export type PermissionExpression = boolean | Record<string, unknown>;
2
2
  export type FieldPermissionExpression = {
3
- read?: boolean;
4
- write?: boolean;
3
+ read?: PermissionExpression;
4
+ write?: PermissionExpression;
5
+ fields?: {
6
+ [K: string]: FieldPermissionExpression;
7
+ };
8
+ };
9
+ export type AdditionalFieldsPermissionExpression = FieldPermissionExpression | {
10
+ [K: string]: FieldPermissionExpression;
5
11
  };
6
12
  export interface DocumentFiltersPermissions {
7
13
  read?: PermissionExpression;
@@ -19,9 +25,7 @@ export interface Role {
19
25
  fields?: {
20
26
  [K: string]: FieldPermissionExpression;
21
27
  };
22
- additional_fields?: {
23
- [K: string]: FieldPermissionExpression;
24
- };
28
+ additional_fields?: AdditionalFieldsPermissionExpression;
25
29
  }
26
30
  export interface Params {
27
31
  roles: Role[];
@@ -1 +1 @@
1
- {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/utils/roles/interface.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAAG,OAAO,CAAA;AAE1C,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,MAAM,WAAW,0BAA0B;IACzC,IAAI,CAAC,EAAE,oBAAoB,CAAA;IAC3B,KAAK,CAAC,EAAE,oBAAoB,CAAA;CAC7B;AAED,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAA;IAEZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC/B,MAAM,CAAC,EAAE,oBAAoB,CAAA;IAC7B,gBAAgB,CAAC,EAAE,0BAA0B,CAAA;IAC7C,IAAI,CAAC,EAAE,oBAAoB,CAAA;IAC3B,KAAK,CAAC,EAAE,oBAAoB,CAAA;IAC5B,MAAM,CAAC,EAAE,oBAAoB,CAAA;IAC7B,MAAM,CAAC,EAAE,oBAAoB,CAAA;IAC7B,MAAM,CAAC,EAAE;QACP,CAAC,CAAC,EAAE,MAAM,GAAG,yBAAyB,CAAA;KACvC,CAAA;IACD,iBAAiB,CAAC,EAAE;QAClB,CAAC,CAAC,EAAE,MAAM,GAAG,yBAAyB,CAAA;KACvC,CAAA;CACF;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,IAAI,EAAE,CAAA;IAEb,MAAM,EAAE,GAAG,CAAA;IAEX,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC/B,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAA;CACxD;AAGD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA"}
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/utils/roles/interface.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEpE,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,CAAC,EAAE,oBAAoB,CAAA;IAC3B,KAAK,CAAC,EAAE,oBAAoB,CAAA;IAC5B,MAAM,CAAC,EAAE;QACP,CAAC,CAAC,EAAE,MAAM,GAAG,yBAAyB,CAAA;KACvC,CAAA;CACF,CAAA;AAED,MAAM,MAAM,oCAAoC,GAC5C,yBAAyB,GACzB;IACE,CAAC,CAAC,EAAE,MAAM,GAAG,yBAAyB,CAAA;CACvC,CAAA;AAEL,MAAM,WAAW,0BAA0B;IACzC,IAAI,CAAC,EAAE,oBAAoB,CAAA;IAC3B,KAAK,CAAC,EAAE,oBAAoB,CAAA;CAC7B;AAED,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,MAAM,CAAA;IAEZ,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC/B,MAAM,CAAC,EAAE,oBAAoB,CAAA;IAC7B,gBAAgB,CAAC,EAAE,0BAA0B,CAAA;IAC7C,IAAI,CAAC,EAAE,oBAAoB,CAAA;IAC3B,KAAK,CAAC,EAAE,oBAAoB,CAAA;IAC5B,MAAM,CAAC,EAAE,oBAAoB,CAAA;IAC7B,MAAM,CAAC,EAAE,oBAAoB,CAAA;IAC7B,MAAM,CAAC,EAAE;QACP,CAAC,CAAC,EAAE,MAAM,GAAG,yBAAyB,CAAA;KACvC,CAAA;IACD,iBAAiB,CAAC,EAAE,oCAAoC,CAAA;CACzD;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,IAAI,EAAE,CAAA;IAEb,MAAM,EAAE,GAAG,CAAA;IAEX,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAC/B,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAA;CACxD;AAGD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA"}
@@ -29,9 +29,9 @@ const evaluateTopLevelPermissionsFn = (_a, currentType_1) => __awaiter(void 0, [
29
29
  });
30
30
  exports.evaluateTopLevelPermissionsFn = evaluateTopLevelPermissionsFn;
31
31
  const checkFieldsPropertyExists = ({ role }) => {
32
- var _a, _b;
32
+ var _a;
33
33
  const hasFields = !!Object.keys((_a = role === null || role === void 0 ? void 0 : role.fields) !== null && _a !== void 0 ? _a : {}).length;
34
- const hasAdditional = !!Object.keys((_b = role === null || role === void 0 ? void 0 : role.additional_fields) !== null && _b !== void 0 ? _b : {}).length;
34
+ const hasAdditional = typeof (role === null || role === void 0 ? void 0 : role.additional_fields) !== 'undefined';
35
35
  return hasFields || hasAdditional;
36
36
  };
37
37
  exports.checkFieldsPropertyExists = checkFieldsPropertyExists;
@@ -0,0 +1,8 @@
1
+ import { Document } from 'mongodb';
2
+ import { Role } from '../interface';
3
+ import { MachineContext } from './interface';
4
+ export declare const hasAdditionalFieldsDefined: (role?: Role) => boolean;
5
+ export declare const filterDocumentByFieldPermissions: (context: Pick<MachineContext, "params" | "role" | "user">, mode: "read" | "write", options?: {
6
+ defaultAllow?: boolean;
7
+ }) => Promise<Document>;
8
+ //# sourceMappingURL=fieldPermissions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fieldPermissions.d.ts","sourceRoot":"","sources":["../../../../src/utils/roles/machines/fieldPermissions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAElC,OAAO,EAGL,IAAI,EACL,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AA4C5C,eAAO,MAAM,0BAA0B,GAAI,OAAO,IAAI,YACN,CAAA;AAEhD,eAAO,MAAM,gCAAgC,GAC3C,SAAS,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC,EACzD,MAAM,MAAM,GAAG,OAAO,EACtB,UAAU;IACR,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,KACA,OAAO,CAAC,QAAQ,CAyBlB,CAAA"}
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.filterDocumentByFieldPermissions = exports.hasAdditionalFieldsDefined = void 0;
13
+ const helpers_1 = require("../helpers");
14
+ const isObject = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
15
+ const isFieldPermissionExpression = (value) => isObject(value) && ('read' in value || 'write' in value);
16
+ const getAdditionalFieldPermission = (additionalFields, fieldName) => {
17
+ if (!additionalFields || !isObject(additionalFields))
18
+ return undefined;
19
+ const byField = additionalFields[fieldName];
20
+ if (isFieldPermissionExpression(byField)) {
21
+ return byField;
22
+ }
23
+ if (isFieldPermissionExpression(additionalFields)) {
24
+ return additionalFields;
25
+ }
26
+ return undefined;
27
+ };
28
+ const canReadField = (context, permission) => __awaiter(void 0, void 0, void 0, function* () {
29
+ if (!permission)
30
+ return false;
31
+ const read = yield (0, helpers_1.evaluateExpression)(context.params, permission.read, context.user);
32
+ if (read)
33
+ return true;
34
+ return yield (0, helpers_1.evaluateExpression)(context.params, permission.write, context.user);
35
+ });
36
+ const canWriteField = (context, permission) => __awaiter(void 0, void 0, void 0, function* () {
37
+ if (!permission)
38
+ return false;
39
+ return yield (0, helpers_1.evaluateExpression)(context.params, permission.write, context.user);
40
+ });
41
+ const hasAdditionalFieldsDefined = (role) => typeof (role === null || role === void 0 ? void 0 : role.additional_fields) !== 'undefined';
42
+ exports.hasAdditionalFieldsDefined = hasAdditionalFieldsDefined;
43
+ const filterDocumentByFieldPermissions = (context, mode, options) => __awaiter(void 0, void 0, void 0, function* () {
44
+ var _a, _b;
45
+ const source = (_a = context.params) === null || _a === void 0 ? void 0 : _a.cursor;
46
+ if (!isObject(source))
47
+ return {};
48
+ const document = {};
49
+ const fields = (_b = context.role.fields) !== null && _b !== void 0 ? _b : {};
50
+ const additionalFields = context.role.additional_fields;
51
+ for (const [key, value] of Object.entries(source)) {
52
+ const fieldPermission = fields[key];
53
+ const permission = fieldPermission !== null && fieldPermission !== void 0 ? fieldPermission : getAdditionalFieldPermission(additionalFields, key);
54
+ let allowed = (options === null || options === void 0 ? void 0 : options.defaultAllow) === true;
55
+ if (permission) {
56
+ allowed =
57
+ mode === 'read'
58
+ ? yield canReadField(context, permission)
59
+ : yield canWriteField(context, permission);
60
+ }
61
+ if (allowed) {
62
+ document[key] = value;
63
+ }
64
+ }
65
+ return document;
66
+ });
67
+ exports.filterDocumentByFieldPermissions = filterDocumentByFieldPermissions;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/roles/machines/read/A/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAGxC,eAAO,MAAM,aAAa,EAAE,MAuB3B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/roles/machines/read/A/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAGxC,eAAO,MAAM,aAAa,EAAE,MAuB3B,CAAA"}
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.STEP_A_STATES = void 0;
13
+ const commonValidators_1 = require("../../commonValidators");
13
14
  const utils_1 = require("../../utils");
14
15
  exports.STEP_A_STATES = {
15
16
  checkSearchRequest: (_a) => __awaiter(void 0, [_a], void 0, function* ({ context, next, goToNextValidationStage }) {
@@ -24,14 +25,14 @@ exports.STEP_A_STATES = {
24
25
  }
25
26
  return goToNextValidationStage();
26
27
  }),
27
- evaluateSearch: (_a) => __awaiter(void 0, [_a], void 0, function* ({ context, endValidation }) {
28
+ evaluateSearch: (_a) => __awaiter(void 0, [_a], void 0, function* ({ context, endValidation, goToNextValidationStage }) {
28
29
  (0, utils_1.logMachineInfo)({
29
30
  enabled: context.enableLog,
30
31
  machine: 'A',
31
32
  step: 2,
32
33
  stepName: 'evaluateSearch'
33
34
  });
34
- // NOTE -> we don't support search operations
35
- return endValidation({ success: false });
35
+ const check = yield (0, commonValidators_1.evaluateTopLevelPermissionsFn)(context, 'search');
36
+ return check ? goToNextValidationStage() : endValidation({ success: false });
36
37
  })
37
38
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/roles/machines/read/C/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAGxC,eAAO,MAAM,aAAa,EAAE,MAyC3B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/roles/machines/read/C/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAQxC,eAAO,MAAM,aAAa,EAAE,MAyC3B,CAAA"}
@@ -10,10 +10,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.STEP_C_STATES = void 0;
13
- const validators_1 = require("./validators");
14
13
  const utils_1 = require("../../utils");
14
+ const validators_1 = require("./validators");
15
15
  exports.STEP_C_STATES = {
16
- evaluateTopLevelRead: (_a) => __awaiter(void 0, [_a], void 0, function* ({ context, next, endValidation }) {
16
+ evaluateTopLevelRead: (_a) => __awaiter(void 0, [_a], void 0, function* ({ context, next }) {
17
17
  (0, utils_1.logMachineInfo)({
18
18
  enabled: context.enableLog,
19
19
  machine: 'C',
@@ -21,12 +21,7 @@ exports.STEP_C_STATES = {
21
21
  stepName: 'evaluateTopLevelRead'
22
22
  });
23
23
  const check = yield (0, validators_1.evaluateTopLevelReadFn)(context);
24
- if (check) {
25
- return (0, validators_1.checkFieldsPropertyExists)(context)
26
- ? next('checkFieldsProperty')
27
- : endValidation({ success: true });
28
- }
29
- return next('evaluateTopLevelWrite', { check });
24
+ return next('evaluateTopLevelWrite', { readCheck: check });
30
25
  }),
31
26
  evaluateTopLevelWrite: (_a) => __awaiter(void 0, [_a], void 0, function* ({ context, next, endValidation }) {
32
27
  var _b;
@@ -36,12 +31,18 @@ exports.STEP_C_STATES = {
36
31
  step: 2,
37
32
  stepName: 'evaluateTopLevelWrite'
38
33
  });
39
- const check = yield (0, validators_1.evaluateTopLevelWriteFn)(context);
40
- if (check)
41
- return endValidation({ success: true });
42
- return ((_b = context === null || context === void 0 ? void 0 : context.prevParams) === null || _b === void 0 ? void 0 : _b.check) === false
43
- ? endValidation({ success: false })
44
- : next('checkFieldsProperty');
34
+ const writeCheck = yield (0, validators_1.evaluateTopLevelWriteFn)(context);
35
+ const readCheck = (_b = context === null || context === void 0 ? void 0 : context.prevParams) === null || _b === void 0 ? void 0 : _b.readCheck;
36
+ if (readCheck === true || writeCheck === true) {
37
+ return (0, validators_1.checkFieldsPropertyExists)(context)
38
+ ? next('checkFieldsProperty')
39
+ : endValidation({ success: true });
40
+ }
41
+ if (readCheck === false)
42
+ return endValidation({ success: false });
43
+ return (0, validators_1.checkFieldsPropertyExists)(context)
44
+ ? next('checkFieldsProperty')
45
+ : endValidation({ success: false });
45
46
  }),
46
47
  checkFieldsProperty: (_a) => __awaiter(void 0, [_a], void 0, function* ({ context, goToNextValidationStage }) {
47
48
  (0, utils_1.logMachineInfo)({
@@ -50,7 +51,6 @@ exports.STEP_C_STATES = {
50
51
  step: 3,
51
52
  stepName: 'checkFieldsProperty'
52
53
  });
53
- const check = (0, validators_1.checkFieldsPropertyExists)(context);
54
- return goToNextValidationStage(check ? 'checkIsValidFieldName' : 'checkAdditionalFields');
54
+ return goToNextValidationStage('checkIsValidFieldName');
55
55
  })
56
56
  };
@@ -12,14 +12,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.checkFieldsPropertyExists = exports.evaluateTopLevelWriteFn = exports.evaluateTopLevelReadFn = void 0;
13
13
  const commonValidators_1 = require("../../commonValidators");
14
14
  const evaluateTopLevelReadFn = (context) => __awaiter(void 0, void 0, void 0, function* () {
15
- if (context.params.type !== 'read') {
15
+ if (!['read', 'search'].includes(context.params.type)) {
16
16
  return false;
17
17
  }
18
18
  return (0, commonValidators_1.evaluateTopLevelPermissionsFn)(context, 'read');
19
19
  });
20
20
  exports.evaluateTopLevelReadFn = evaluateTopLevelReadFn;
21
21
  const evaluateTopLevelWriteFn = (context) => __awaiter(void 0, void 0, void 0, function* () {
22
- if (!['read', 'write'].includes(context.params.type)) {
22
+ if (!['read', 'search', 'write'].includes(context.params.type)) {
23
23
  return undefined;
24
24
  }
25
25
  return (0, commonValidators_1.evaluateTopLevelPermissionsFn)(context, 'write');
@@ -19,7 +19,7 @@ const runCheckIsValidFieldName = (_a) => __awaiter(void 0, [_a], void 0, functio
19
19
  step: 2,
20
20
  stepName: 'checkIsValidFieldName'
21
21
  });
22
- const document = (0, validators_1.checkIsValidFieldNameFn)(context);
22
+ const document = yield (0, validators_1.checkIsValidFieldNameFn)(context);
23
23
  return endValidation({ success: !!Object.keys(document).length, document });
24
24
  });
25
25
  exports.STEP_D_STATES = {
@@ -1,4 +1,4 @@
1
1
  import { MachineContext } from '../../interface';
2
2
  export declare const checkAdditionalFieldsFn: ({ role }: MachineContext) => boolean;
3
- export declare const checkIsValidFieldNameFn: ({ role, params }: MachineContext) => {};
3
+ export declare const checkIsValidFieldNameFn: (context: MachineContext) => Promise<import("bson/bson").Document>;
4
4
  //# sourceMappingURL=validators.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/roles/machines/read/D/validators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,eAAO,MAAM,uBAAuB,GAAI,UAAU,cAAc,YAE/D,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAI,kBAAkB,cAAc,OAyBvE,CAAA"}
1
+ {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/roles/machines/read/D/validators.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,eAAO,MAAM,uBAAuB,GAAI,UAAU,cAAc,YAE/D,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAU,SAAS,cAAc,0CAOpE,CAAA"}
@@ -1,28 +1,26 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
12
  exports.checkIsValidFieldNameFn = exports.checkAdditionalFieldsFn = void 0;
13
+ const commonValidators_1 = require("../../commonValidators");
14
+ const fieldPermissions_1 = require("../../fieldPermissions");
4
15
  const checkAdditionalFieldsFn = ({ role }) => {
5
- return !!Object.keys(role.additional_fields || {}).length;
16
+ return (0, fieldPermissions_1.hasAdditionalFieldsDefined)(role);
6
17
  };
7
18
  exports.checkAdditionalFieldsFn = checkAdditionalFieldsFn;
8
- const checkIsValidFieldNameFn = ({ role, params }) => {
9
- const { cursor } = params;
10
- const { fields = {}, additional_fields = {} } = role;
11
- const rulesOnId = !!(fields['_id'] || additional_fields['_id']);
12
- const filteredDocument = Object.entries(cursor).reduce((filteredDocument, [key, value]) => {
13
- var _a, _b;
14
- if (fields[key]) {
15
- return role.fields[key].read || role.fields[key].write
16
- ? Object.assign(Object.assign({}, filteredDocument), { [key]: value }) : filteredDocument;
17
- }
18
- if (additional_fields[key]) {
19
- return ((_a = additional_fields[key]) === null || _a === void 0 ? void 0 : _a.read) || ((_b = additional_fields[key]) === null || _b === void 0 ? void 0 : _b.write)
20
- ? Object.assign(Object.assign({}, filteredDocument), { [key]: value }) : filteredDocument;
21
- }
22
- return Object.assign(Object.assign({}, filteredDocument), { [key]: value });
23
- }, {});
24
- return rulesOnId || cursor._id === undefined
25
- ? filteredDocument
26
- : Object.assign(Object.assign({}, filteredDocument), { _id: cursor._id });
27
- };
19
+ const checkIsValidFieldNameFn = (context) => __awaiter(void 0, void 0, void 0, function* () {
20
+ const readCheck = yield (0, commonValidators_1.evaluateTopLevelPermissionsFn)(context, 'read');
21
+ const writeCheck = yield (0, commonValidators_1.evaluateTopLevelPermissionsFn)(context, 'write');
22
+ return yield (0, fieldPermissions_1.filterDocumentByFieldPermissions)(context, 'read', {
23
+ defaultAllow: readCheck === true || writeCheck === true
24
+ });
25
+ });
28
26
  exports.checkIsValidFieldNameFn = checkIsValidFieldNameFn;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/roles/machines/write/B/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAGxC,eAAO,MAAM,aAAa,EAAE,MA6D3B,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/roles/machines/write/B/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAGxC,eAAO,MAAM,aAAa,EAAE,MAgE3B,CAAA"}
@@ -43,13 +43,13 @@ exports.STEP_B_STATES = {
43
43
  stepName: 'evaluateTopLevelWrite'
44
44
  });
45
45
  const check = yield (0, commonValidators_1.evaluateTopLevelPermissionsFn)(context, 'write');
46
- if (check)
47
- return context.params.type === 'insert'
48
- ? next('evaluateTopLevelInsert')
49
- : endValidation({ success: true });
50
- return check === false
51
- ? endValidation({ success: false })
52
- : next('checkFieldsProperty');
46
+ if (check) {
47
+ return next('evaluateTopLevelInsert');
48
+ }
49
+ if (check === false) {
50
+ return endValidation({ success: false });
51
+ }
52
+ return next('checkFieldsProperty');
53
53
  }),
54
54
  checkFieldsProperty: (_a) => __awaiter(void 0, [_a], void 0, function* ({ context, goToNextValidationStage }) {
55
55
  (0, utils_1.logMachineInfo)({
@@ -61,7 +61,7 @@ exports.STEP_B_STATES = {
61
61
  const check = (0, commonValidators_1.checkFieldsPropertyExists)(context);
62
62
  return goToNextValidationStage(check ? 'checkIsValidFieldName' : 'checkAdditionalFields');
63
63
  }),
64
- evaluateTopLevelInsert: (_a) => __awaiter(void 0, [_a], void 0, function* ({ context, endValidation }) {
64
+ evaluateTopLevelInsert: (_a) => __awaiter(void 0, [_a], void 0, function* ({ context, next, endValidation }) {
65
65
  (0, utils_1.logMachineInfo)({
66
66
  enabled: context.enableLog,
67
67
  machine: 'B',
@@ -69,6 +69,9 @@ exports.STEP_B_STATES = {
69
69
  stepName: 'evaluateTopLevelInsert'
70
70
  });
71
71
  const check = yield (0, commonValidators_1.evaluateTopLevelPermissionsFn)(context, 'insert');
72
- return endValidation({ success: !!check });
72
+ if (!check) {
73
+ return endValidation({ success: false });
74
+ }
75
+ return endValidation({ success: true });
73
76
  })
74
77
  };
@@ -30,7 +30,7 @@ exports.STEP_C_STATES = {
30
30
  step: 2,
31
31
  stepName: 'checkIsValidFieldName'
32
32
  });
33
- const document = (0, validators_1.checkIsValidFieldNameFn)(context);
33
+ const document = yield (0, validators_1.checkIsValidFieldNameFn)(context);
34
34
  return endValidation({ success: !!Object.keys(document).length, document });
35
35
  })
36
36
  };
@@ -1,4 +1,4 @@
1
1
  import { MachineContext } from '../../interface';
2
2
  export declare const checkAdditionalFieldsFn: ({ role }: MachineContext) => boolean;
3
- export declare const checkIsValidFieldNameFn: ({ role, params }: MachineContext) => {};
3
+ export declare const checkIsValidFieldNameFn: (context: MachineContext) => Promise<import("bson/bson").Document>;
4
4
  //# sourceMappingURL=validators.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/roles/machines/write/C/validators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,eAAO,MAAM,uBAAuB,GAAI,UAAU,cAAc,YAE/D,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAI,kBAAkB,cAAc,OAyBvE,CAAA"}
1
+ {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/roles/machines/write/C/validators.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,eAAO,MAAM,uBAAuB,GAAI,UAAU,cAAc,YAE/D,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAU,SAAS,cAAc,0CAIpE,CAAA"}
@@ -1,28 +1,23 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
12
  exports.checkIsValidFieldNameFn = exports.checkAdditionalFieldsFn = void 0;
13
+ const fieldPermissions_1 = require("../../fieldPermissions");
4
14
  const checkAdditionalFieldsFn = ({ role }) => {
5
- return !!Object.keys(role.additional_fields || {}).length;
15
+ return (0, fieldPermissions_1.hasAdditionalFieldsDefined)(role);
6
16
  };
7
17
  exports.checkAdditionalFieldsFn = checkAdditionalFieldsFn;
8
- const checkIsValidFieldNameFn = ({ role, params }) => {
9
- const { cursor } = params;
10
- const { fields = {}, additional_fields = {} } = role;
11
- const rulesOnId = !!(fields['_id'] || additional_fields['_id']);
12
- const filteredDocument = Object.entries(cursor).reduce((filteredDocument, [key, value]) => {
13
- var _a;
14
- if (fields[key]) {
15
- return role.fields[key].write
16
- ? Object.assign(Object.assign({}, filteredDocument), { [key]: value }) : filteredDocument;
17
- }
18
- if (additional_fields[key]) {
19
- return ((_a = additional_fields[key]) === null || _a === void 0 ? void 0 : _a.write)
20
- ? Object.assign(Object.assign({}, filteredDocument), { [key]: value }) : filteredDocument;
21
- }
22
- return Object.assign(Object.assign({}, filteredDocument), { [key]: value });
23
- }, {});
24
- return rulesOnId || cursor._id === undefined
25
- ? filteredDocument
26
- : Object.assign(Object.assign({}, filteredDocument), { _id: cursor._id });
27
- };
18
+ const checkIsValidFieldNameFn = (context) => __awaiter(void 0, void 0, void 0, function* () {
19
+ return yield (0, fieldPermissions_1.filterDocumentByFieldPermissions)(context, 'write', {
20
+ defaultAllow: typeof context.role.write !== 'undefined'
21
+ });
22
+ });
28
23
  exports.checkIsValidFieldNameFn = checkIsValidFieldNameFn;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowerforce/flowerbase",
3
- "version": "1.7.5-beta.2",
3
+ "version": "1.7.5-beta.4",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,116 @@
1
+ import { ObjectId } from 'mongodb'
2
+ import { mapWatchFilterToChangeStreamMatch, mapWatchFilterToDocumentQuery } from '../controller'
3
+
4
+ describe('watch filter mapping', () => {
5
+ it('keeps change-event fields untouched and prefixes only document fields', () => {
6
+ const input = {
7
+ accountId: '699efbc09729e3b79f79e9b4',
8
+ $and: [
9
+ {
10
+ $or: [
11
+ { requestId: '69a282a75cd849c244e001ca' },
12
+ { 'fullDocument.requestId': '69a282a75cd849c244e001ca' }
13
+ ]
14
+ },
15
+ {
16
+ $or: [
17
+ { operationType: 'insert' },
18
+ { operationType: 'replace' },
19
+ {
20
+ operationType: 'update',
21
+ 'updateDescription.updatedFields.stage': { $exists: true }
22
+ }
23
+ ]
24
+ }
25
+ ]
26
+ }
27
+
28
+ const output = mapWatchFilterToChangeStreamMatch(input)
29
+
30
+ expect(output).toEqual({
31
+ 'fullDocument.accountId': '699efbc09729e3b79f79e9b4',
32
+ $and: [
33
+ {
34
+ $or: [
35
+ { 'fullDocument.requestId': '69a282a75cd849c244e001ca' },
36
+ { 'fullDocument.requestId': '69a282a75cd849c244e001ca' }
37
+ ]
38
+ },
39
+ {
40
+ $or: [
41
+ { operationType: 'insert' },
42
+ { operationType: 'replace' },
43
+ {
44
+ operationType: 'update',
45
+ 'updateDescription.updatedFields.stage': { $exists: true }
46
+ }
47
+ ]
48
+ }
49
+ ]
50
+ })
51
+
52
+ const outputJson = JSON.stringify(output)
53
+ expect(outputJson).not.toContain('fullDocument.fullDocument.')
54
+ expect(outputJson).not.toContain('fullDocument.operationType')
55
+ })
56
+
57
+ it('supports stage-change filters and strips event-only clauses for document readability checks', () => {
58
+ const input = {
59
+ 'fullDocument.accountId': '699efbc09729e3b79f79e9b4',
60
+ $and: [
61
+ {
62
+ $or: [
63
+ { 'fullDocument.requestId': '69a282a75cd849c244e001ca' },
64
+ { 'fullDocument.requestId': '69a282a75cd849c244e001ca' }
65
+ ]
66
+ },
67
+ {
68
+ $or: [
69
+ { operationType: 'insert' },
70
+ { operationType: 'replace' },
71
+ {
72
+ operationType: 'update',
73
+ 'updateDescription.updatedFields.stage': { $exists: true }
74
+ }
75
+ ]
76
+ }
77
+ ]
78
+ }
79
+
80
+ const watchMatch = mapWatchFilterToChangeStreamMatch(input)
81
+ expect(watchMatch).toEqual(input)
82
+
83
+ const documentQuery = mapWatchFilterToDocumentQuery(input)
84
+ expect(documentQuery).toEqual({
85
+ accountId: '699efbc09729e3b79f79e9b4',
86
+ $and: [
87
+ {
88
+ $or: [
89
+ { requestId: '69a282a75cd849c244e001ca' },
90
+ { requestId: '69a282a75cd849c244e001ca' }
91
+ ]
92
+ }
93
+ ]
94
+ })
95
+
96
+ const documentQueryJson = JSON.stringify(documentQuery)
97
+ expect(documentQueryJson).not.toContain('operationType')
98
+ expect(documentQueryJson).not.toContain('updateDescription')
99
+ })
100
+
101
+ it('preserves ObjectId values in both change-stream and document mappings', () => {
102
+ const id = new ObjectId('69a282a75cd849c244e001ca')
103
+ const input = {
104
+ 'fullDocument._id': id,
105
+ operationType: 'update',
106
+ 'updateDescription.updatedFields.stage': { $exists: true }
107
+ }
108
+
109
+ const watchMatch = mapWatchFilterToChangeStreamMatch(input) as Record<string, unknown>
110
+ expect(watchMatch['fullDocument._id']).toEqual(id)
111
+
112
+ const documentQuery = mapWatchFilterToDocumentQuery(input) as Record<string, unknown>
113
+ expect(documentQuery._id).toEqual(id)
114
+ expect(documentQuery.operationType).toBeUndefined()
115
+ })
116
+ })