@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.
- package/dist/features/functions/controller.d.ts +2 -0
- package/dist/features/functions/controller.d.ts.map +1 -1
- package/dist/features/functions/controller.js +244 -19
- package/dist/services/api/index.d.ts +4 -0
- package/dist/services/api/index.d.ts.map +1 -1
- package/dist/services/api/utils.d.ts +1 -0
- package/dist/services/api/utils.d.ts.map +1 -1
- package/dist/services/index.d.ts +4 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/utils.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/utils.js +17 -1
- package/dist/utils/context/helpers.d.ts +12 -0
- package/dist/utils/context/helpers.d.ts.map +1 -1
- package/dist/utils/roles/helpers.d.ts.map +1 -1
- package/dist/utils/roles/helpers.js +19 -4
- package/dist/utils/roles/interface.d.ts +10 -6
- package/dist/utils/roles/interface.d.ts.map +1 -1
- package/dist/utils/roles/machines/commonValidators.js +2 -2
- package/dist/utils/roles/machines/fieldPermissions.d.ts +8 -0
- package/dist/utils/roles/machines/fieldPermissions.d.ts.map +1 -0
- package/dist/utils/roles/machines/fieldPermissions.js +67 -0
- package/dist/utils/roles/machines/read/A/index.d.ts.map +1 -1
- package/dist/utils/roles/machines/read/A/index.js +4 -3
- package/dist/utils/roles/machines/read/C/index.d.ts.map +1 -1
- package/dist/utils/roles/machines/read/C/index.js +16 -16
- package/dist/utils/roles/machines/read/C/validators.js +2 -2
- package/dist/utils/roles/machines/read/D/index.js +1 -1
- package/dist/utils/roles/machines/read/D/validators.d.ts +1 -1
- package/dist/utils/roles/machines/read/D/validators.d.ts.map +1 -1
- package/dist/utils/roles/machines/read/D/validators.js +19 -21
- package/dist/utils/roles/machines/write/B/index.d.ts.map +1 -1
- package/dist/utils/roles/machines/write/B/index.js +12 -9
- package/dist/utils/roles/machines/write/C/index.js +1 -1
- package/dist/utils/roles/machines/write/C/validators.d.ts +1 -1
- package/dist/utils/roles/machines/write/C/validators.d.ts.map +1 -1
- package/dist/utils/roles/machines/write/C/validators.js +16 -21
- package/package.json +1 -1
- package/src/features/functions/__tests__/watch-filter.test.ts +116 -0
- package/src/features/functions/controller.ts +282 -22
- package/src/features/triggers/__tests__/index.test.ts +2 -2
- package/src/services/mongodb-atlas/__tests__/findOneAndUpdate.test.ts +1 -1
- package/src/services/mongodb-atlas/utils.ts +19 -4
- package/src/utils/__tests__/STEP_A_STATES.test.ts +24 -2
- package/src/utils/__tests__/STEP_C_STATES.test.ts +61 -27
- package/src/utils/__tests__/STEP_D_STATES.test.ts +9 -9
- package/src/utils/__tests__/WRITE_STEP_B_STATES.test.ts +184 -0
- package/src/utils/__tests__/checkAdditionalFieldsFn.test.ts +2 -2
- package/src/utils/__tests__/checkFieldsPropertyExists.test.ts +13 -0
- package/src/utils/__tests__/checkIsValidFieldNameFn.test.ts +52 -121
- package/src/utils/__tests__/evaluateTopLevelReadFn.test.ts +10 -1
- package/src/utils/__tests__/evaluateTopLevelWriteFn.test.ts +21 -5
- package/src/utils/roles/helpers.ts +18 -4
- package/src/utils/roles/interface.ts +13 -6
- package/src/utils/roles/machines/commonValidators.ts +1 -1
- package/src/utils/roles/machines/fieldPermissions.ts +86 -0
- package/src/utils/roles/machines/read/A/index.ts +4 -3
- package/src/utils/roles/machines/read/C/index.ts +18 -18
- package/src/utils/roles/machines/read/C/validators.ts +2 -2
- package/src/utils/roles/machines/read/D/index.ts +1 -1
- package/src/utils/roles/machines/read/D/validators.ts +12 -25
- package/src/utils/roles/machines/write/B/index.ts +12 -9
- package/src/utils/roles/machines/write/C/index.ts +1 -1
- 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
|
|
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,
|
|
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':
|
|
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?:
|
|
4
|
-
write?:
|
|
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;
|
|
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
|
|
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 =
|
|
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":"
|
|
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
|
-
|
|
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":"
|
|
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
|
|
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
|
-
|
|
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
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
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
|
|
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: (
|
|
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":"
|
|
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
|
|
16
|
+
return (0, fieldPermissions_1.hasAdditionalFieldsDefined)(role);
|
|
6
17
|
};
|
|
7
18
|
exports.checkAdditionalFieldsFn = checkAdditionalFieldsFn;
|
|
8
|
-
const checkIsValidFieldNameFn = (
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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,
|
|
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
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
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: (
|
|
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":"
|
|
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
|
|
15
|
+
return (0, fieldPermissions_1.hasAdditionalFieldsDefined)(role);
|
|
6
16
|
};
|
|
7
17
|
exports.checkAdditionalFieldsFn = checkAdditionalFieldsFn;
|
|
8
|
-
const checkIsValidFieldNameFn = (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
@@ -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
|
+
})
|