@flowerforce/flowerbase 1.8.4-beta.1 → 1.8.4-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/features/functions/interface.d.ts +1 -0
- package/dist/features/functions/interface.d.ts.map +1 -1
- package/dist/features/functions/utils.js +1 -1
- package/dist/monitoring/utils.d.ts +1 -1
- package/dist/services/mongodb-atlas/index.js +11 -11
- package/dist/utils/context/helpers.d.ts +3 -3
- package/dist/utils/context/index.d.ts.map +1 -1
- package/dist/utils/context/index.js +91 -14
- package/dist/utils/roles/helpers.d.ts +1 -0
- package/dist/utils/roles/helpers.d.ts.map +1 -1
- package/dist/utils/roles/helpers.js +77 -8
- package/dist/utils/roles/machines/utils.d.ts +2 -0
- package/dist/utils/roles/machines/utils.d.ts.map +1 -1
- package/dist/utils/roles/machines/utils.js +33 -1
- package/package.json +1 -1
- package/src/features/functions/interface.ts +4 -1
- package/src/features/functions/utils.ts +3 -3
- package/src/services/mongodb-atlas/index.ts +150 -150
- package/src/utils/__tests__/contextExecuteCompatibility.test.ts +40 -0
- package/src/utils/__tests__/evaluateExpression.test.ts +89 -0
- package/src/utils/__tests__/getWinningRole.test.ts +17 -0
- package/src/utils/context/index.ts +142 -13
- package/src/utils/roles/helpers.ts +107 -14
- package/src/utils/roles/machines/utils.ts +34 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/features/functions/interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE1C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG;
|
|
1
|
+
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/features/functions/interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE1C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG;IACpD,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AAEhD,MAAM,MAAM,uBAAuB,GAAG;IACpC,GAAG,EAAE,eAAe,CAAA;IACpB,aAAa,EAAE,SAAS,CAAA;IACxB,SAAS,EAAE,KAAK,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,aAAa,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAC,MAAM,UAAU,CAAC,oBAAoB,CAAC,CAAC,CAAA;IACvF,KAAK,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAA;IACvC,MAAM,EAAE,QAAQ,CAAA;IAChB,MAAM,CAAC,EAAE,QAAQ,CAAA;IACjB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,OAAO,CAAC,EAAE,QAAQ,CAAA;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,EAAE,QAAQ,CAAA;IAClB,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,QAAQ,EAAE,QAAQ,EAAE,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,KAAK,0BAA0B,GAAG;IAChC,aAAa,EAAE,SAAS,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;CACb,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,CAC/B,GAAG,EAAE,eAAe,EACpB,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,0BAA0B,KACjD,OAAO,CAAC,IAAI,CAAC,CAAA"}
|
|
@@ -46,7 +46,7 @@ const loadFunctions = (...args_1) => __awaiter(void 0, [...args_1], void 0, func
|
|
|
46
46
|
throw new Error(`File ${name}.js or ${name}.ts not found`);
|
|
47
47
|
}
|
|
48
48
|
code = fs_1.default.readFileSync(fnPath, 'utf-8');
|
|
49
|
-
acc[name] = Object.assign({ code }, opts);
|
|
49
|
+
acc[name] = Object.assign({ code, sourcePath: fnPath }, opts);
|
|
50
50
|
return acc;
|
|
51
51
|
}, {});
|
|
52
52
|
return functions;
|
|
@@ -77,7 +77,7 @@ export declare const getErrorDetails: (error: unknown) => {
|
|
|
77
77
|
};
|
|
78
78
|
export declare const pickHeaders: (headers: FastifyRequest["headers"]) => unknown;
|
|
79
79
|
export declare const createEventStore: (maxAgeMs: number, maxEvents: number) => EventStore;
|
|
80
|
-
export declare const classifyRequest: (url: string) => "function" | "
|
|
80
|
+
export declare const classifyRequest: (url: string) => "function" | "api" | "auth" | "http_endpoint" | "trigger" | "http";
|
|
81
81
|
export declare const createEventId: () => string;
|
|
82
82
|
export declare const buildRulesMeta: (meta?: MonitMeta) => {
|
|
83
83
|
collection: string;
|
|
@@ -490,7 +490,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
|
|
|
490
490
|
emitMongoEvent('findOne');
|
|
491
491
|
return null;
|
|
492
492
|
}
|
|
493
|
-
const winningRole = (0, utils_2.
|
|
493
|
+
const winningRole = yield (0, utils_2.getWinningRoleAsync)(result, user, roles);
|
|
494
494
|
logDebug('findOne winningRole', {
|
|
495
495
|
collection: collName,
|
|
496
496
|
winningRoleName: (_a = winningRole === null || winningRole === void 0 ? void 0 : winningRole.name) !== null && _a !== void 0 ? _a : null,
|
|
@@ -546,7 +546,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
|
|
|
546
546
|
const formattedQuery = (0, utils_3.getFormattedQuery)(filters, query, user);
|
|
547
547
|
// Retrieve the document to check permissions before deleting
|
|
548
548
|
const result = yield collection.findOne(buildAndQuery(formattedQuery));
|
|
549
|
-
const winningRole = (0, utils_2.
|
|
549
|
+
const winningRole = yield (0, utils_2.getWinningRoleAsync)(result, user, roles);
|
|
550
550
|
logDebug('delete winningRole', {
|
|
551
551
|
collection: collName,
|
|
552
552
|
userId: getUserId(user),
|
|
@@ -600,7 +600,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
|
|
|
600
600
|
try {
|
|
601
601
|
if (!run_as_system) {
|
|
602
602
|
(0, utils_3.checkDenyOperation)(normalizedRules, collection.collectionName, model_1.CRUD_OPERATIONS.CREATE);
|
|
603
|
-
const winningRole = (0, utils_2.
|
|
603
|
+
const winningRole = yield (0, utils_2.getWinningRoleAsync)(data, user, roles);
|
|
604
604
|
const { status, document } = winningRole
|
|
605
605
|
? yield (0, machines_1.checkValidation)(winningRole, {
|
|
606
606
|
type: 'insert',
|
|
@@ -673,7 +673,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
|
|
|
673
673
|
}
|
|
674
674
|
throw new Error('Update not permitted');
|
|
675
675
|
}
|
|
676
|
-
const winningRole = (0, utils_2.
|
|
676
|
+
const winningRole = yield (0, utils_2.getWinningRoleAsync)(result, user, roles);
|
|
677
677
|
// Check if the update data contains MongoDB update operators (e.g., $set, $inc)
|
|
678
678
|
const updatedPaths = getUpdatedPaths(normalizedData);
|
|
679
679
|
const docToCheck = applyDocumentUpdateOperators(result, normalizedData);
|
|
@@ -756,7 +756,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
|
|
|
756
756
|
: [applyDocumentUpdateOperators(currentDoc, normalizedData)];
|
|
757
757
|
docToCheck = computedDoc;
|
|
758
758
|
}
|
|
759
|
-
const winningRole = (0, utils_2.
|
|
759
|
+
const winningRole = yield (0, utils_2.getWinningRoleAsync)(docToCheck, user, roles);
|
|
760
760
|
const { status, document } = winningRole
|
|
761
761
|
? yield (0, machines_1.checkValidation)(winningRole, {
|
|
762
762
|
type: validationType,
|
|
@@ -776,7 +776,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
|
|
|
776
776
|
emitMongoEvent('findOneAndUpdate');
|
|
777
777
|
return updateResult;
|
|
778
778
|
}
|
|
779
|
-
const readRole = (0, utils_2.
|
|
779
|
+
const readRole = yield (0, utils_2.getWinningRoleAsync)(updateResult, user, roles);
|
|
780
780
|
const readResult = readRole
|
|
781
781
|
? yield (0, machines_1.checkValidation)(readRole, {
|
|
782
782
|
type: 'read',
|
|
@@ -844,7 +844,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
|
|
|
844
844
|
const response = yield originalToArray();
|
|
845
845
|
const filteredResponse = yield Promise.all(response.map((currentDoc) => __awaiter(void 0, void 0, void 0, function* () {
|
|
846
846
|
var _a;
|
|
847
|
-
const winningRole = (0, utils_2.
|
|
847
|
+
const winningRole = yield (0, utils_2.getWinningRoleAsync)(currentDoc, user, roles);
|
|
848
848
|
logDebug('find winningRole', {
|
|
849
849
|
collection: collName,
|
|
850
850
|
userId: getUserId(user),
|
|
@@ -976,7 +976,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
|
|
|
976
976
|
const isValidChange = (change) => __awaiter(void 0, void 0, void 0, function* () {
|
|
977
977
|
const { fullDocument, updateDescription } = change;
|
|
978
978
|
const hasFullDocument = !!fullDocument;
|
|
979
|
-
const winningRole = (0, utils_2.
|
|
979
|
+
const winningRole = yield (0, utils_2.getWinningRoleAsync)(fullDocument, user, roles);
|
|
980
980
|
const fullDocumentValidation = winningRole
|
|
981
981
|
? yield (0, machines_1.checkValidation)(winningRole, {
|
|
982
982
|
type: 'read',
|
|
@@ -1088,7 +1088,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
|
|
|
1088
1088
|
(0, utils_3.checkDenyOperation)(normalizedRules, collection.collectionName, model_1.CRUD_OPERATIONS.CREATE);
|
|
1089
1089
|
// Validate each document against user's roles
|
|
1090
1090
|
const filteredItems = yield Promise.all(documents.map((currentDoc) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1091
|
-
const winningRole = (0, utils_2.
|
|
1091
|
+
const winningRole = yield (0, utils_2.getWinningRoleAsync)(currentDoc, user, roles);
|
|
1092
1092
|
const { status, document } = winningRole
|
|
1093
1093
|
? yield (0, machines_1.checkValidation)(winningRole, {
|
|
1094
1094
|
type: 'insert',
|
|
@@ -1133,7 +1133,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
|
|
|
1133
1133
|
const updatedPaths = getUpdatedPaths(normalizedData);
|
|
1134
1134
|
const docsToCheck = result.map((currentDoc) => applyDocumentUpdateOperators(currentDoc, normalizedData));
|
|
1135
1135
|
const filteredItems = yield Promise.all(docsToCheck.map((currentDoc, index) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1136
|
-
const winningRole = (0, utils_2.
|
|
1136
|
+
const winningRole = yield (0, utils_2.getWinningRoleAsync)(currentDoc, user, roles);
|
|
1137
1137
|
const { status, document } = winningRole
|
|
1138
1138
|
? yield (0, machines_1.checkValidation)(winningRole, {
|
|
1139
1139
|
type: 'write',
|
|
@@ -1187,7 +1187,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
|
|
|
1187
1187
|
const data = yield collection.find({ $and: formattedQuery }).toArray();
|
|
1188
1188
|
// Filter and validate each document based on user's roles
|
|
1189
1189
|
const filteredItems = yield Promise.all(data.map((currentDoc) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1190
|
-
const winningRole = (0, utils_2.
|
|
1190
|
+
const winningRole = yield (0, utils_2.getWinningRoleAsync)(currentDoc, user, roles);
|
|
1191
1191
|
const { status, document } = winningRole
|
|
1192
1192
|
? yield (0, machines_1.checkValidation)(winningRole, {
|
|
1193
1193
|
type: 'delete',
|
|
@@ -35,11 +35,11 @@ export declare const generateContextData: ({ user, services, app, rules, current
|
|
|
35
35
|
request: {
|
|
36
36
|
remoteIPAddress: string | undefined;
|
|
37
37
|
id?: string | undefined;
|
|
38
|
-
host?: string | undefined;
|
|
39
|
-
method?: string | undefined;
|
|
40
|
-
url?: string | undefined;
|
|
41
38
|
ips?: string[];
|
|
39
|
+
host?: string | undefined;
|
|
42
40
|
hostname?: string | undefined;
|
|
41
|
+
url?: string | undefined;
|
|
42
|
+
method?: string | undefined;
|
|
43
43
|
ip?: string | undefined;
|
|
44
44
|
};
|
|
45
45
|
user: unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/context/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/context/index.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AA8RnD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,EACpC,IAAI,EACJ,GAAG,EACH,KAAK,EACL,IAAI,EACJ,eAAe,EACf,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,eAAsB,EACtB,OAAO,EACP,OAAO,EACR,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CA2G1C;AAED,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,GAAG,EACH,KAAK,EACL,IAAI,EACJ,eAAe,EACf,aAAa,EACb,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,eAAsB,EACtB,OAAO,EACR,EAAE,qBAAqB,GAAG,OAAO,CA0BjC"}
|
|
@@ -14,6 +14,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.GenerateContext = GenerateContext;
|
|
16
16
|
exports.GenerateContextSync = GenerateContextSync;
|
|
17
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
17
18
|
const node_module_1 = require("node:module");
|
|
18
19
|
const node_path_1 = __importDefault(require("node:path"));
|
|
19
20
|
const node_url_1 = require("node:url");
|
|
@@ -89,6 +90,27 @@ const wrapEsmModule = (code) => {
|
|
|
89
90
|
].join('\n');
|
|
90
91
|
return `${prelude}\n${code}\n${trailer}`;
|
|
91
92
|
};
|
|
93
|
+
const transpileSandboxModule = (code) => {
|
|
94
|
+
const exportedNames = [];
|
|
95
|
+
let transformed = code.includes('import ')
|
|
96
|
+
? transformImportsToRequire(code)
|
|
97
|
+
: code;
|
|
98
|
+
transformed = transformed.replace(/^\s*export\s+function\s+([A-Za-z_$][\w$]*)\s*\(/gm, (_match, name) => {
|
|
99
|
+
exportedNames.push(name);
|
|
100
|
+
return `function ${name}(`;
|
|
101
|
+
});
|
|
102
|
+
transformed = transformed.replace(/^\s*export\s+(const|let|var|class)\s+([A-Za-z_$][\w$]*)/gm, (_match, kind, name) => {
|
|
103
|
+
exportedNames.push(name);
|
|
104
|
+
return `${kind} ${name}`;
|
|
105
|
+
});
|
|
106
|
+
transformed = transformed.replace(/^\s*export\s+default\s+/gm, 'module.exports = ');
|
|
107
|
+
if (exportedNames.length === 0) {
|
|
108
|
+
return transformed;
|
|
109
|
+
}
|
|
110
|
+
return `${transformed}\n${[...new Set(exportedNames)]
|
|
111
|
+
.map((name) => `exports.${name} = ${name}`)
|
|
112
|
+
.join('\n')}`;
|
|
113
|
+
};
|
|
92
114
|
const resolveImportTarget = (specifier, customRequire) => {
|
|
93
115
|
try {
|
|
94
116
|
const resolved = customRequire.resolve(specifier);
|
|
@@ -109,6 +131,53 @@ const shouldFallbackFromVmModules = (error) => {
|
|
|
109
131
|
const code = error.code;
|
|
110
132
|
return code === 'ERR_VM_MODULES_DISABLED' || code === 'ERR_VM_MODULES_NOT_SUPPORTED';
|
|
111
133
|
};
|
|
134
|
+
const resolveModulePath = (specifier, parentFile) => {
|
|
135
|
+
const parentDir = node_path_1.default.dirname(parentFile);
|
|
136
|
+
const basePath = node_path_1.default.resolve(parentDir, specifier);
|
|
137
|
+
const candidates = [
|
|
138
|
+
basePath,
|
|
139
|
+
`${basePath}.js`,
|
|
140
|
+
`${basePath}.ts`,
|
|
141
|
+
node_path_1.default.join(basePath, 'index.js'),
|
|
142
|
+
node_path_1.default.join(basePath, 'index.ts')
|
|
143
|
+
];
|
|
144
|
+
return candidates.find((candidate) => {
|
|
145
|
+
try {
|
|
146
|
+
return node_fs_1.default.statSync(candidate).isFile();
|
|
147
|
+
}
|
|
148
|
+
catch (_a) {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
const executeSandboxModule = ({ code, contextData, filePath, moduleCache }) => {
|
|
154
|
+
var _a;
|
|
155
|
+
if (moduleCache.has(filePath)) {
|
|
156
|
+
return moduleCache.get(filePath);
|
|
157
|
+
}
|
|
158
|
+
const sandboxModule = { exports: {} };
|
|
159
|
+
moduleCache.set(filePath, sandboxModule.exports);
|
|
160
|
+
const baseRequire = (0, node_module_1.createRequire)(filePath);
|
|
161
|
+
const localRequire = ((specifier) => {
|
|
162
|
+
if (specifier.startsWith('.') || specifier.startsWith('/')) {
|
|
163
|
+
const resolvedPath = resolveModulePath(specifier, filePath);
|
|
164
|
+
if (resolvedPath) {
|
|
165
|
+
return executeSandboxModule({
|
|
166
|
+
code: node_fs_1.default.readFileSync(resolvedPath, 'utf-8'),
|
|
167
|
+
contextData,
|
|
168
|
+
filePath: resolvedPath,
|
|
169
|
+
moduleCache
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return baseRequire(specifier);
|
|
174
|
+
});
|
|
175
|
+
const vmContext = vm_1.default.createContext(Object.assign(Object.assign({}, contextData), { require: localRequire, exports: sandboxModule.exports, module: sandboxModule, __filename: filePath, __dirname: node_path_1.default.dirname(filePath), __fb_require: localRequire, __fb_filename: filePath, __fb_dirname: node_path_1.default.dirname(filePath) }));
|
|
176
|
+
vm_1.default.runInContext(transpileSandboxModule(code), vmContext, { filename: filePath });
|
|
177
|
+
sandboxModule.exports = (_a = resolveExport(vmContext)) !== null && _a !== void 0 ? _a : sandboxModule.exports;
|
|
178
|
+
moduleCache.set(filePath, sandboxModule.exports);
|
|
179
|
+
return sandboxModule.exports;
|
|
180
|
+
};
|
|
112
181
|
const isExportedFunction = (value) => typeof value === 'function';
|
|
113
182
|
const getDefaultExport = (value) => {
|
|
114
183
|
if (!value || typeof value !== 'object')
|
|
@@ -128,11 +197,25 @@ const resolveExport = (ctx) => {
|
|
|
128
197
|
return contextExports;
|
|
129
198
|
return (_e = getDefaultExport(moduleExports)) !== null && _e !== void 0 ? _e : getDefaultExport(contextExports);
|
|
130
199
|
};
|
|
131
|
-
const buildVmContext = (contextData) => {
|
|
132
|
-
var _a, _b;
|
|
200
|
+
const buildVmContext = (contextData, currentFunction) => {
|
|
201
|
+
var _a, _b, _c;
|
|
133
202
|
const sandboxModule = { exports: {} };
|
|
134
|
-
const entryFile = (
|
|
135
|
-
const
|
|
203
|
+
const entryFile = (_c = (_a = currentFunction === null || currentFunction === void 0 ? void 0 : currentFunction.sourcePath) !== null && _a !== void 0 ? _a : (_b = require.main) === null || _b === void 0 ? void 0 : _b.filename) !== null && _c !== void 0 ? _c : process.cwd();
|
|
204
|
+
const moduleCache = new Map();
|
|
205
|
+
const customRequire = ((specifier) => {
|
|
206
|
+
if ((specifier.startsWith('.') || specifier.startsWith('/')) && (currentFunction === null || currentFunction === void 0 ? void 0 : currentFunction.sourcePath)) {
|
|
207
|
+
const resolvedPath = resolveModulePath(specifier, currentFunction.sourcePath);
|
|
208
|
+
if (resolvedPath) {
|
|
209
|
+
return executeSandboxModule({
|
|
210
|
+
code: node_fs_1.default.readFileSync(resolvedPath, 'utf-8'),
|
|
211
|
+
contextData,
|
|
212
|
+
filePath: resolvedPath,
|
|
213
|
+
moduleCache
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return (0, node_module_1.createRequire)(entryFile)(specifier);
|
|
218
|
+
});
|
|
136
219
|
const vmContext = vm_1.default.createContext(Object.assign(Object.assign({}, contextData), { require: customRequire, exports: sandboxModule.exports, module: sandboxModule, __filename,
|
|
137
220
|
__dirname, __fb_require: customRequire, __fb_filename: __filename, __fb_dirname: __dirname }));
|
|
138
221
|
return { sandboxModule, entryFile, customRequire, vmContext };
|
|
@@ -169,7 +252,7 @@ function GenerateContext(_a) {
|
|
|
169
252
|
GenerateContextSync,
|
|
170
253
|
request
|
|
171
254
|
});
|
|
172
|
-
const { sandboxModule, entryFile, customRequire, vmContext } = buildVmContext(contextData);
|
|
255
|
+
const { sandboxModule, entryFile, customRequire, vmContext } = buildVmContext(contextData, functionToRun);
|
|
173
256
|
const vmModules = vm_1.default;
|
|
174
257
|
const hasStaticImport = /\bimport\s+/.test(functionToRun.code);
|
|
175
258
|
let usedVmModules = false;
|
|
@@ -214,10 +297,7 @@ function GenerateContext(_a) {
|
|
|
214
297
|
}
|
|
215
298
|
}
|
|
216
299
|
if (!usedVmModules) {
|
|
217
|
-
|
|
218
|
-
? transformImportsToRequire(functionToRun.code)
|
|
219
|
-
: functionToRun.code;
|
|
220
|
-
vm_1.default.runInContext(codeToRun, vmContext);
|
|
300
|
+
vm_1.default.runInContext(transpileSandboxModule(functionToRun.code), vmContext, { filename: entryFile });
|
|
221
301
|
}
|
|
222
302
|
sandboxModule.exports = (_a = resolveExport(vmContext)) !== null && _a !== void 0 ? _a : sandboxModule.exports;
|
|
223
303
|
if (deserializeArgs) {
|
|
@@ -247,11 +327,8 @@ function GenerateContextSync({ args, app, rules, user, currentFunction, function
|
|
|
247
327
|
GenerateContextSync,
|
|
248
328
|
request
|
|
249
329
|
});
|
|
250
|
-
const { sandboxModule, vmContext } = buildVmContext(contextData);
|
|
251
|
-
|
|
252
|
-
? transformImportsToRequire(functionToRun.code)
|
|
253
|
-
: functionToRun.code;
|
|
254
|
-
vm_1.default.runInContext(codeToRun, vmContext);
|
|
330
|
+
const { sandboxModule, entryFile, vmContext } = buildVmContext(contextData, functionToRun);
|
|
331
|
+
vm_1.default.runInContext(transpileSandboxModule(functionToRun.code), vmContext, { filename: entryFile });
|
|
255
332
|
sandboxModule.exports = (_a = resolveExport(vmContext)) !== null && _a !== void 0 ? _a : sandboxModule.exports;
|
|
256
333
|
const fn = sandboxModule.exports;
|
|
257
334
|
if (deserializeArgs) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PermissionExpression } from './interface';
|
|
2
2
|
import { MachineContext } from './machines/interface';
|
|
3
|
+
export declare const evaluateExpandedExpression: (expression: unknown, params: MachineContext["params"], user?: MachineContext["user"]) => Promise<boolean>;
|
|
3
4
|
export declare const evaluateExpression: (params: MachineContext["params"], expression?: PermissionExpression, user?: MachineContext["user"]) => Promise<boolean>;
|
|
4
5
|
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/roles/helpers.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/roles/helpers.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AA+DrD,eAAO,MAAM,0BAA0B,GACrC,YAAY,OAAO,EACnB,QAAQ,cAAc,CAAC,QAAQ,CAAC,EAChC,OAAO,cAAc,CAAC,MAAM,CAAC,KAC5B,OAAO,CAAC,OAAO,CAwDjB,CAAA;AAED,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,cAAc,CAAC,QAAQ,CAAC,EAChC,aAAa,oBAAoB,EACjC,OAAO,cAAc,CAAC,MAAM,CAAC,KAC5B,OAAO,CAAC,OAAO,CAMjB,CAAA"}
|
|
@@ -12,13 +12,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.evaluateExpression = void 0;
|
|
15
|
+
exports.evaluateExpression = exports.evaluateExpandedExpression = void 0;
|
|
16
16
|
const services_1 = require("../../services");
|
|
17
17
|
const state_1 = require("../../state");
|
|
18
18
|
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 andConditions = ['$and', '%and'];
|
|
23
|
+
const orConditions = ['$or', '%or'];
|
|
22
24
|
const normalizeUserRole = (user) => {
|
|
23
25
|
if (!user)
|
|
24
26
|
return user;
|
|
@@ -34,17 +36,84 @@ const normalizeUserRole = (user) => {
|
|
|
34
36
|
? Object.assign(Object.assign({}, candidate), { role: customRole })
|
|
35
37
|
: user;
|
|
36
38
|
};
|
|
39
|
+
const buildEvaluationContext = (params, user) => {
|
|
40
|
+
var _a, _b, _c;
|
|
41
|
+
const normalizedUser = normalizeUserRole(user);
|
|
42
|
+
return Object.assign(Object.assign(Object.assign({}, ((_a = params.expansions) !== null && _a !== void 0 ? _a : {})), ((_b = params.cursor) !== null && _b !== void 0 ? _b : {})), { '%%root': params.cursor, '%%prevRoot': (_c = params.expansions) === null || _c === void 0 ? void 0 : _c['%%prevRoot'], '%%user': normalizedUser, '%%true': true, '%%false': false });
|
|
43
|
+
};
|
|
44
|
+
const getFunctionCondition = (expression) => {
|
|
45
|
+
if (!expression || typeof expression !== 'object' || Array.isArray(expression)) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const entries = Object.entries(expression);
|
|
49
|
+
if (entries.length !== 1) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const [key, value] = entries[0];
|
|
53
|
+
if (!functionsConditions.includes(key)) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return Object.prototype.hasOwnProperty.call(value, '%function')
|
|
60
|
+
? [key, value]
|
|
61
|
+
: null;
|
|
62
|
+
};
|
|
63
|
+
const evaluateExpandedExpression = (expression, params, user) => __awaiter(void 0, void 0, void 0, function* () {
|
|
64
|
+
if (typeof expression === 'boolean') {
|
|
65
|
+
return expression;
|
|
66
|
+
}
|
|
67
|
+
if (!expression || typeof expression !== 'object') {
|
|
68
|
+
return Boolean(expression);
|
|
69
|
+
}
|
|
70
|
+
const block = expression;
|
|
71
|
+
const functionCondition = getFunctionCondition(block);
|
|
72
|
+
if (functionCondition) {
|
|
73
|
+
return evaluateComplexExpression(functionCondition, params, user);
|
|
74
|
+
}
|
|
75
|
+
const andKey = andConditions.find((key) => Object.prototype.hasOwnProperty.call(block, key));
|
|
76
|
+
if (andKey) {
|
|
77
|
+
const conditions = Array.isArray(block[andKey]) ? block[andKey] : [];
|
|
78
|
+
if (!conditions.length)
|
|
79
|
+
return true;
|
|
80
|
+
for (const condition of conditions) {
|
|
81
|
+
if (!(yield (0, exports.evaluateExpandedExpression)(condition, params, user))) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
const orKey = orConditions.find((key) => Object.prototype.hasOwnProperty.call(block, key));
|
|
88
|
+
if (orKey) {
|
|
89
|
+
const conditions = Array.isArray(block[orKey]) ? block[orKey] : [];
|
|
90
|
+
if (!conditions.length)
|
|
91
|
+
return true;
|
|
92
|
+
for (const condition of conditions) {
|
|
93
|
+
if (yield (0, exports.evaluateExpandedExpression)(condition, params, user)) {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
const keys = Object.keys(block);
|
|
100
|
+
if (keys.length > 1) {
|
|
101
|
+
for (const key of keys) {
|
|
102
|
+
if (!(yield (0, exports.evaluateExpandedExpression)({ [key]: block[key] }, params, user))) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
return utils_1.default.checkRule(block, buildEvaluationContext(params, user), {});
|
|
109
|
+
});
|
|
110
|
+
exports.evaluateExpandedExpression = evaluateExpandedExpression;
|
|
37
111
|
const evaluateExpression = (params, expression, user) => __awaiter(void 0, void 0, void 0, function* () {
|
|
38
|
-
var _a;
|
|
39
112
|
if (!expression || typeof expression === 'boolean')
|
|
40
113
|
return !!expression;
|
|
41
|
-
const
|
|
42
|
-
const value = Object.assign(Object.assign(Object.assign({}, params.expansions), params.cursor), { '%%root': params.cursor, '%%prevRoot': (_a = params.expansions) === null || _a === void 0 ? void 0 : _a['%%prevRoot'], '%%user': normalizedUser, '%%true': true, '%%false': false });
|
|
114
|
+
const value = buildEvaluationContext(params, user);
|
|
43
115
|
const conditions = (0, rules_1.expandQuery)(expression, value);
|
|
44
|
-
|
|
45
|
-
return complexCondition
|
|
46
|
-
? yield evaluateComplexExpression(complexCondition, params, normalizedUser)
|
|
47
|
-
: utils_1.default.checkRule(conditions, value, {});
|
|
116
|
+
return (0, exports.evaluateExpandedExpression)(conditions, params, user);
|
|
48
117
|
});
|
|
49
118
|
exports.evaluateExpression = evaluateExpression;
|
|
50
119
|
const evaluateComplexExpression = (condition, params, user) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -12,6 +12,7 @@ import { LogMachineInfoParams } from './interface';
|
|
|
12
12
|
* @returns {Role | null} - Returns the first role that matches the `apply_when` condition, or `null` if none match.
|
|
13
13
|
*/
|
|
14
14
|
export declare const getWinningRole: (document: OptionalId<Document> | null, user: User, roles?: Role[]) => Role | null;
|
|
15
|
+
export declare const getWinningRoleAsync: (document: OptionalId<Document> | null, user: User, roles?: Role[]) => Promise<Role | null>;
|
|
15
16
|
/**
|
|
16
17
|
* Checks if the `apply_when` condition is valid for the given user and document.
|
|
17
18
|
*
|
|
@@ -22,6 +23,7 @@ export declare const getWinningRole: (document: OptionalId<Document> | null, use
|
|
|
22
23
|
* @returns {boolean} - Returns `true` if at least one valid rule is found, otherwise `false`.
|
|
23
24
|
*/
|
|
24
25
|
export declare const checkApplyWhen: (apply_when: Role["apply_when"], user: User, document: OptionalId<Document> | null) => boolean;
|
|
26
|
+
export declare const checkApplyWhenAsync: (apply_when: Role["apply_when"], user: User, document: OptionalId<Document> | null) => Promise<boolean>;
|
|
25
27
|
/**
|
|
26
28
|
* Logs machine step information if logging is enabled.
|
|
27
29
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/utils/roles/machines/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/utils/roles/machines/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAIzC,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AACnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,EACrC,MAAM,IAAI,EACV,QAAO,IAAI,EAAO,KACjB,IAAI,GAAG,IAQT,CAAA;AAED,eAAO,MAAM,mBAAmB,GAC9B,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,EACrC,MAAM,IAAI,EACV,QAAO,IAAI,EAAO,KACjB,OAAO,CAAC,IAAI,GAAG,IAAI,CAQrB,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,YAAY,IAAI,CAAC,YAAY,CAAC,EAC9B,MAAM,IAAI,EACV,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,YAQtC,CAAA;AAED,eAAO,MAAM,mBAAmB,GAC9B,YAAY,IAAI,CAAC,YAAY,CAAC,EAC9B,MAAM,IAAI,EACV,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,qBActC,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,GAAI,sCAK5B,oBAAoB,SAEtB,CAAA"}
|
|
@@ -1,7 +1,17 @@
|
|
|
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
|
-
exports.logMachineInfo = exports.checkApplyWhen = exports.getWinningRole = void 0;
|
|
12
|
+
exports.logMachineInfo = exports.checkApplyWhenAsync = exports.checkApplyWhen = exports.getWinningRoleAsync = exports.getWinningRole = void 0;
|
|
4
13
|
const utils_1 = require("../../../services/mongodb-atlas/utils");
|
|
14
|
+
const helpers_1 = require("../helpers");
|
|
5
15
|
/**
|
|
6
16
|
* Determines the first applicable role for a given user and document.
|
|
7
17
|
*
|
|
@@ -22,6 +32,17 @@ const getWinningRole = (document, user, roles = []) => {
|
|
|
22
32
|
return null;
|
|
23
33
|
};
|
|
24
34
|
exports.getWinningRole = getWinningRole;
|
|
35
|
+
const getWinningRoleAsync = (document_1, user_1, ...args_1) => __awaiter(void 0, [document_1, user_1, ...args_1], void 0, function* (document, user, roles = []) {
|
|
36
|
+
if (!roles.length)
|
|
37
|
+
return null;
|
|
38
|
+
for (const role of roles) {
|
|
39
|
+
if (yield (0, exports.checkApplyWhenAsync)(role.apply_when, user, document)) {
|
|
40
|
+
return role;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
});
|
|
45
|
+
exports.getWinningRoleAsync = getWinningRoleAsync;
|
|
25
46
|
/**
|
|
26
47
|
* Checks if the `apply_when` condition is valid for the given user and document.
|
|
27
48
|
*
|
|
@@ -40,6 +61,17 @@ const checkApplyWhen = (apply_when, user, document) => {
|
|
|
40
61
|
return !!validRule.length;
|
|
41
62
|
};
|
|
42
63
|
exports.checkApplyWhen = checkApplyWhen;
|
|
64
|
+
const checkApplyWhenAsync = (apply_when, user, document) => __awaiter(void 0, void 0, void 0, function* () {
|
|
65
|
+
return (0, helpers_1.evaluateExpression)({
|
|
66
|
+
type: 'read',
|
|
67
|
+
roles: [],
|
|
68
|
+
cursor: document,
|
|
69
|
+
expansions: {
|
|
70
|
+
'%%prevRoot': undefined
|
|
71
|
+
}
|
|
72
|
+
}, apply_when, user);
|
|
73
|
+
});
|
|
74
|
+
exports.checkApplyWhenAsync = checkApplyWhenAsync;
|
|
43
75
|
/**
|
|
44
76
|
* Logs machine step information if logging is enabled.
|
|
45
77
|
*
|
package/package.json
CHANGED
|
@@ -10,7 +10,10 @@ export interface FunctionConfig {
|
|
|
10
10
|
disable_arg_logs?: boolean
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export type Function = Omit<FunctionConfig, 'name'> & {
|
|
13
|
+
export type Function = Omit<FunctionConfig, 'name'> & {
|
|
14
|
+
code: string
|
|
15
|
+
sourcePath?: string
|
|
16
|
+
}
|
|
14
17
|
|
|
15
18
|
export type Functions = Record<string, Function>
|
|
16
19
|
|
|
@@ -27,7 +27,7 @@ export const loadFunctions = async (rootDir = process.cwd()): Promise<Functions>
|
|
|
27
27
|
throw new Error(`File ${name}.js or ${name}.ts not found`)
|
|
28
28
|
}
|
|
29
29
|
code = fs.readFileSync(fnPath, 'utf-8')
|
|
30
|
-
acc[name] = { code, ...opts }
|
|
30
|
+
acc[name] = { code, sourcePath: fnPath, ...opts }
|
|
31
31
|
|
|
32
32
|
return acc
|
|
33
33
|
}, {} as Functions)
|
|
@@ -74,8 +74,8 @@ export const executeQuery = async ({
|
|
|
74
74
|
typeof projection !== 'undefined'
|
|
75
75
|
? parsedProjection
|
|
76
76
|
: parsedOptions &&
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
typeof parsedOptions === 'object' &&
|
|
78
|
+
'projection' in parsedOptions
|
|
79
79
|
? (parsedOptions as Document).projection
|
|
80
80
|
: undefined
|
|
81
81
|
return {
|