@flowerforce/flowerbase 1.3.1-beta.3 → 1.3.1-beta.5
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/utils.d.ts +8 -7
- package/dist/features/functions/utils.d.ts.map +1 -1
- package/dist/features/functions/utils.js +16 -3
- package/dist/services/mongodb-atlas/index.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/index.js +30 -3
- package/dist/utils/crypto/index.js +1 -1
- package/package.json +1 -1
- package/src/features/functions/utils.ts +19 -5
- package/src/services/mongodb-atlas/index.ts +37 -3
- package/src/utils/crypto/index.ts +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Document } from 'mongodb';
|
|
1
2
|
import { ExecuteQueryParams, Functions } from './interface';
|
|
2
3
|
/**
|
|
3
4
|
* > Loads the functions config json file
|
|
@@ -15,12 +16,12 @@ export declare const executeQuery: ({ currentMethod, query, update, filter, opti
|
|
|
15
16
|
findOne: () => Promise<unknown>;
|
|
16
17
|
count: () => Promise<number>;
|
|
17
18
|
deleteOne: () => Promise<unknown>;
|
|
18
|
-
insertOne: () => Promise<import("mongodb
|
|
19
|
-
updateOne: () => Promise<unknown> | import("mongodb
|
|
20
|
-
findOneAndUpdate: () => Promise<
|
|
21
|
-
aggregate: () => Promise<
|
|
22
|
-
insertMany: () => Promise<import("mongodb
|
|
23
|
-
updateMany: () => Promise<import("mongodb
|
|
24
|
-
deleteMany: () => Promise<import("mongodb
|
|
19
|
+
insertOne: () => Promise<import("mongodb").InsertOneResult<Document>>;
|
|
20
|
+
updateOne: () => Promise<unknown> | import("mongodb").FindCursor<any> | import("mongodb").ChangeStream<Document, Document> | import("mongodb").AggregationCursor<Document>;
|
|
21
|
+
findOneAndUpdate: () => Promise<Document | null>;
|
|
22
|
+
aggregate: () => Promise<Document[]>;
|
|
23
|
+
insertMany: () => Promise<import("mongodb").InsertManyResult<Document>>;
|
|
24
|
+
updateMany: () => Promise<import("mongodb").UpdateResult<Document>>;
|
|
25
|
+
deleteMany: () => Promise<import("mongodb").DeleteResult>;
|
|
25
26
|
}>;
|
|
26
27
|
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/features/functions/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/features/functions/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAGlC,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE3D;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAU,gBAAuB,KAAG,OAAO,CAAC,SAAS,CAwB9E,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAU,+GAWhC,kBAAkB;;;;;;;;;;;;EA4EpB,CAAA"}
|
|
@@ -70,16 +70,29 @@ const executeQuery = (_a) => __awaiter(void 0, [_a], void 0, function* ({ curren
|
|
|
70
70
|
: typeof returnNewDocument === 'boolean'
|
|
71
71
|
? { returnDocument: returnNewDocument ? 'after' : 'before' }
|
|
72
72
|
: undefined;
|
|
73
|
+
const parsedOptions = resolvedOptions ? bson_1.EJSON.deserialize(resolvedOptions) : undefined;
|
|
73
74
|
return {
|
|
74
75
|
find: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
75
|
-
return yield
|
|
76
|
+
return yield (() => {
|
|
77
|
+
const cursor = currentMethod(bson_1.EJSON.deserialize(resolvedQuery));
|
|
78
|
+
if (parsedOptions === null || parsedOptions === void 0 ? void 0 : parsedOptions.sort) {
|
|
79
|
+
cursor.sort(parsedOptions.sort);
|
|
80
|
+
}
|
|
81
|
+
if (typeof (parsedOptions === null || parsedOptions === void 0 ? void 0 : parsedOptions.skip) === 'number') {
|
|
82
|
+
cursor.skip(parsedOptions.skip);
|
|
83
|
+
}
|
|
84
|
+
if (typeof (parsedOptions === null || parsedOptions === void 0 ? void 0 : parsedOptions.limit) === 'number') {
|
|
85
|
+
cursor.limit(parsedOptions.limit);
|
|
86
|
+
}
|
|
87
|
+
return cursor.toArray();
|
|
88
|
+
})();
|
|
76
89
|
}),
|
|
77
90
|
findOne: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery)),
|
|
78
|
-
count: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery),
|
|
91
|
+
count: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), parsedOptions),
|
|
79
92
|
deleteOne: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery)),
|
|
80
93
|
insertOne: () => currentMethod(bson_1.EJSON.deserialize(document)),
|
|
81
94
|
updateOne: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), bson_1.EJSON.deserialize(resolvedUpdate)),
|
|
82
|
-
findOneAndUpdate: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), bson_1.EJSON.deserialize(resolvedUpdate),
|
|
95
|
+
findOneAndUpdate: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), bson_1.EJSON.deserialize(resolvedUpdate), parsedOptions),
|
|
83
96
|
aggregate: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
84
97
|
return (yield currentMethod(bson_1.EJSON.deserialize(pipeline), {}, // TODO -> ADD OPTIONS
|
|
85
98
|
isClient)).toArray();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/mongodb-atlas/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/mongodb-atlas/index.ts"],"names":[],"mappings":"AAcA,OAAO,EAAyC,oBAAoB,EAAE,MAAM,SAAS,CAAA;AA60BrF,QAAA,MAAM,YAAY,EAAE,oBAsBlB,CAAA;AAEF,eAAe,YAAY,CAAA"}
|
|
@@ -12,6 +12,7 @@ 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
|
+
const get_1 = __importDefault(require("lodash/get"));
|
|
15
16
|
const isEqual_1 = __importDefault(require("lodash/isEqual"));
|
|
16
17
|
const machines_1 = require("../../utils/roles/machines");
|
|
17
18
|
const utils_1 = require("../../utils/roles/machines/utils");
|
|
@@ -36,6 +37,29 @@ const logService = (message, payload) => {
|
|
|
36
37
|
return;
|
|
37
38
|
console.log('[service-debug]', message, payload !== null && payload !== void 0 ? payload : '');
|
|
38
39
|
};
|
|
40
|
+
const getUpdatedPaths = (update) => {
|
|
41
|
+
const entries = Object.entries(update !== null && update !== void 0 ? update : {});
|
|
42
|
+
const hasOperators = entries.some(([key]) => key.startsWith('$'));
|
|
43
|
+
if (!hasOperators) {
|
|
44
|
+
return Object.keys(update !== null && update !== void 0 ? update : {});
|
|
45
|
+
}
|
|
46
|
+
const paths = new Set();
|
|
47
|
+
for (const [key, value] of entries) {
|
|
48
|
+
if (!key.startsWith('$'))
|
|
49
|
+
continue;
|
|
50
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
51
|
+
Object.keys(value).forEach((path) => paths.add(path));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return [...paths];
|
|
55
|
+
};
|
|
56
|
+
const areUpdatedFieldsAllowed = (filtered, updated, updatedPaths) => {
|
|
57
|
+
if (!filtered)
|
|
58
|
+
return false;
|
|
59
|
+
if (!updatedPaths.length)
|
|
60
|
+
return (0, isEqual_1.default)(filtered, updated);
|
|
61
|
+
return updatedPaths.every((path) => (0, isEqual_1.default)((0, get_1.default)(filtered, path), (0, get_1.default)(updated, path)));
|
|
62
|
+
};
|
|
39
63
|
const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
40
64
|
var _a, _b;
|
|
41
65
|
const normalizedRules = rules !== null && rules !== void 0 ? rules : {};
|
|
@@ -238,6 +262,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
238
262
|
const winningRole = (0, utils_1.getWinningRole)(result, user, roles);
|
|
239
263
|
// Check if the update data contains MongoDB update operators (e.g., $set, $inc)
|
|
240
264
|
const hasOperators = Object.keys(data).some((key) => key.startsWith('$'));
|
|
265
|
+
const updatedPaths = getUpdatedPaths(data);
|
|
241
266
|
// Flatten the update object to extract the actual fields being modified
|
|
242
267
|
// const docToCheck = hasOperators
|
|
243
268
|
// ? Object.values(data).reduce((acc, operation) => ({ ...acc, ...operation }), {})
|
|
@@ -264,7 +289,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
264
289
|
}, user)
|
|
265
290
|
: fallbackAccess(docToCheck);
|
|
266
291
|
// Ensure no unauthorized changes are made
|
|
267
|
-
const areDocumentsEqual = (
|
|
292
|
+
const areDocumentsEqual = areUpdatedFieldsAllowed(document, docToCheck, updatedPaths);
|
|
268
293
|
if (!status || !areDocumentsEqual) {
|
|
269
294
|
throw new Error('Update not permitted');
|
|
270
295
|
}
|
|
@@ -296,6 +321,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
296
321
|
}
|
|
297
322
|
const winningRole = (0, utils_1.getWinningRole)(result, user, roles);
|
|
298
323
|
const hasOperators = Object.keys(data).some((key) => key.startsWith('$'));
|
|
324
|
+
const updatedPaths = Array.isArray(data) ? [] : getUpdatedPaths(data);
|
|
299
325
|
const pipeline = [
|
|
300
326
|
{
|
|
301
327
|
$match: { $and: safeQuery }
|
|
@@ -316,7 +342,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
316
342
|
expansions: {}
|
|
317
343
|
}, user)
|
|
318
344
|
: fallbackAccess(docToCheck);
|
|
319
|
-
const areDocumentsEqual = (
|
|
345
|
+
const areDocumentsEqual = areUpdatedFieldsAllowed(document, docToCheck, updatedPaths);
|
|
320
346
|
if (!status || !areDocumentsEqual) {
|
|
321
347
|
throw new Error('Update not permitted');
|
|
322
348
|
}
|
|
@@ -574,6 +600,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
574
600
|
}
|
|
575
601
|
// Check if the update data contains MongoDB update operators (e.g., $set, $inc)
|
|
576
602
|
const hasOperators = Object.keys(data).some((key) => key.startsWith('$'));
|
|
603
|
+
const updatedPaths = getUpdatedPaths(data);
|
|
577
604
|
// Flatten the update object to extract the actual fields being modified
|
|
578
605
|
// const docToCheck = hasOperators
|
|
579
606
|
// ? Object.values(data).reduce((acc, operation) => ({ ...acc, ...operation }), {})
|
|
@@ -600,7 +627,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
600
627
|
return status ? document : undefined;
|
|
601
628
|
})));
|
|
602
629
|
// Ensure no unauthorized changes are made
|
|
603
|
-
const areDocumentsEqual = (
|
|
630
|
+
const areDocumentsEqual = docsToCheck.every((doc, index) => areUpdatedFieldsAllowed(filteredItems[index], doc, updatedPaths));
|
|
604
631
|
if (!areDocumentsEqual) {
|
|
605
632
|
console.log('check1 In updateMany --> (!areDocumentsEqual)');
|
|
606
633
|
throw new Error('Update not permitted');
|
|
@@ -36,7 +36,7 @@ exports.hashPassword = hashPassword;
|
|
|
36
36
|
const comparePassword = (plaintext, storedPassword) => __awaiter(void 0, void 0, void 0, function* () {
|
|
37
37
|
const [storedHash, storedSalt] = storedPassword.split('.');
|
|
38
38
|
if (!storedHash || !storedSalt) {
|
|
39
|
-
throw new Error(
|
|
39
|
+
throw new Error('Invalid credentials');
|
|
40
40
|
}
|
|
41
41
|
const storedBuffer = Buffer.from(storedHash, 'hex');
|
|
42
42
|
const buffer = (yield scrypt(plaintext, storedSalt, 64));
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
+
import { Document } from 'mongodb'
|
|
3
4
|
import { EJSON } from 'bson'
|
|
4
5
|
import { GetOperatorsFunction } from '../../services/mongodb-atlas/model'
|
|
5
6
|
import { ExecuteQueryParams, Functions } from './interface'
|
|
@@ -65,11 +66,24 @@ export const executeQuery = async ({
|
|
|
65
66
|
: typeof returnNewDocument === 'boolean'
|
|
66
67
|
? { returnDocument: returnNewDocument ? 'after' : 'before' }
|
|
67
68
|
: undefined
|
|
69
|
+
const parsedOptions = resolvedOptions ? EJSON.deserialize(resolvedOptions) : undefined
|
|
68
70
|
return {
|
|
69
71
|
find: async () =>
|
|
70
|
-
await (
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
await (() => {
|
|
73
|
+
const cursor = (currentMethod as ReturnType<GetOperatorsFunction>['find'])(
|
|
74
|
+
EJSON.deserialize(resolvedQuery)
|
|
75
|
+
)
|
|
76
|
+
if (parsedOptions?.sort) {
|
|
77
|
+
cursor.sort(parsedOptions.sort as Document)
|
|
78
|
+
}
|
|
79
|
+
if (typeof parsedOptions?.skip === 'number') {
|
|
80
|
+
cursor.skip(parsedOptions.skip)
|
|
81
|
+
}
|
|
82
|
+
if (typeof parsedOptions?.limit === 'number') {
|
|
83
|
+
cursor.limit(parsedOptions.limit)
|
|
84
|
+
}
|
|
85
|
+
return cursor.toArray()
|
|
86
|
+
})(),
|
|
73
87
|
findOne: () =>
|
|
74
88
|
(currentMethod as ReturnType<GetOperatorsFunction>['findOne'])(
|
|
75
89
|
EJSON.deserialize(resolvedQuery)
|
|
@@ -77,7 +91,7 @@ export const executeQuery = async ({
|
|
|
77
91
|
count: () =>
|
|
78
92
|
(currentMethod as ReturnType<GetOperatorsFunction>['count'])(
|
|
79
93
|
EJSON.deserialize(resolvedQuery),
|
|
80
|
-
|
|
94
|
+
parsedOptions
|
|
81
95
|
),
|
|
82
96
|
deleteOne: () =>
|
|
83
97
|
(currentMethod as ReturnType<GetOperatorsFunction>['deleteOne'])(
|
|
@@ -92,7 +106,7 @@ export const executeQuery = async ({
|
|
|
92
106
|
(currentMethod as ReturnType<GetOperatorsFunction>['findOneAndUpdate'])(
|
|
93
107
|
EJSON.deserialize(resolvedQuery),
|
|
94
108
|
EJSON.deserialize(resolvedUpdate),
|
|
95
|
-
|
|
109
|
+
parsedOptions
|
|
96
110
|
),
|
|
97
111
|
aggregate: async () =>
|
|
98
112
|
(await (currentMethod as ReturnType<GetOperatorsFunction>['aggregate'])(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import get from 'lodash/get'
|
|
1
2
|
import isEqual from 'lodash/isEqual'
|
|
2
3
|
import {
|
|
3
4
|
Collection,
|
|
@@ -42,6 +43,34 @@ const logService = (message: string, payload?: unknown) => {
|
|
|
42
43
|
console.log('[service-debug]', message, payload ?? '')
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
const getUpdatedPaths = (update: Document) => {
|
|
47
|
+
const entries = Object.entries(update ?? {})
|
|
48
|
+
const hasOperators = entries.some(([key]) => key.startsWith('$'))
|
|
49
|
+
|
|
50
|
+
if (!hasOperators) {
|
|
51
|
+
return Object.keys(update ?? {})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const paths = new Set<string>()
|
|
55
|
+
for (const [key, value] of entries) {
|
|
56
|
+
if (!key.startsWith('$')) continue
|
|
57
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
58
|
+
Object.keys(value as Document).forEach((path) => paths.add(path))
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return [...paths]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const areUpdatedFieldsAllowed = (
|
|
65
|
+
filtered: Document | null | undefined,
|
|
66
|
+
updated: Document,
|
|
67
|
+
updatedPaths: string[]
|
|
68
|
+
) => {
|
|
69
|
+
if (!filtered) return false
|
|
70
|
+
if (!updatedPaths.length) return isEqual(filtered, updated)
|
|
71
|
+
return updatedPaths.every((path) => isEqual(get(filtered, path), get(updated, path)))
|
|
72
|
+
}
|
|
73
|
+
|
|
45
74
|
const getOperators: GetOperatorsFunction = (
|
|
46
75
|
collection,
|
|
47
76
|
{ rules, collName, user, run_as_system }
|
|
@@ -273,6 +302,7 @@ const getOperators: GetOperatorsFunction = (
|
|
|
273
302
|
|
|
274
303
|
// Check if the update data contains MongoDB update operators (e.g., $set, $inc)
|
|
275
304
|
const hasOperators = Object.keys(data).some((key) => key.startsWith('$'))
|
|
305
|
+
const updatedPaths = getUpdatedPaths(data as Document)
|
|
276
306
|
|
|
277
307
|
// Flatten the update object to extract the actual fields being modified
|
|
278
308
|
// const docToCheck = hasOperators
|
|
@@ -304,7 +334,7 @@ const getOperators: GetOperatorsFunction = (
|
|
|
304
334
|
)
|
|
305
335
|
: fallbackAccess(docToCheck)
|
|
306
336
|
// Ensure no unauthorized changes are made
|
|
307
|
-
const areDocumentsEqual =
|
|
337
|
+
const areDocumentsEqual = areUpdatedFieldsAllowed(document, docToCheck, updatedPaths)
|
|
308
338
|
|
|
309
339
|
if (!status || !areDocumentsEqual) {
|
|
310
340
|
throw new Error('Update not permitted')
|
|
@@ -343,6 +373,7 @@ const getOperators: GetOperatorsFunction = (
|
|
|
343
373
|
|
|
344
374
|
const winningRole = getWinningRole(result, user, roles)
|
|
345
375
|
const hasOperators = Object.keys(data).some((key) => key.startsWith('$'))
|
|
376
|
+
const updatedPaths = Array.isArray(data) ? [] : getUpdatedPaths(data as Document)
|
|
346
377
|
const pipeline = [
|
|
347
378
|
{
|
|
348
379
|
$match: { $and: safeQuery }
|
|
@@ -369,7 +400,7 @@ const getOperators: GetOperatorsFunction = (
|
|
|
369
400
|
)
|
|
370
401
|
: fallbackAccess(docToCheck)
|
|
371
402
|
|
|
372
|
-
const areDocumentsEqual =
|
|
403
|
+
const areDocumentsEqual = areUpdatedFieldsAllowed(document, docToCheck, updatedPaths)
|
|
373
404
|
if (!status || !areDocumentsEqual) {
|
|
374
405
|
throw new Error('Update not permitted')
|
|
375
406
|
}
|
|
@@ -704,6 +735,7 @@ const getOperators: GetOperatorsFunction = (
|
|
|
704
735
|
|
|
705
736
|
// Check if the update data contains MongoDB update operators (e.g., $set, $inc)
|
|
706
737
|
const hasOperators = Object.keys(data).some((key) => key.startsWith('$'))
|
|
738
|
+
const updatedPaths = getUpdatedPaths(data as Document)
|
|
707
739
|
|
|
708
740
|
// Flatten the update object to extract the actual fields being modified
|
|
709
741
|
// const docToCheck = hasOperators
|
|
@@ -743,7 +775,9 @@ const getOperators: GetOperatorsFunction = (
|
|
|
743
775
|
)
|
|
744
776
|
|
|
745
777
|
// Ensure no unauthorized changes are made
|
|
746
|
-
const areDocumentsEqual =
|
|
778
|
+
const areDocumentsEqual = docsToCheck.every((doc, index) =>
|
|
779
|
+
areUpdatedFieldsAllowed(filteredItems[index], doc, updatedPaths)
|
|
780
|
+
)
|
|
747
781
|
|
|
748
782
|
if (!areDocumentsEqual) {
|
|
749
783
|
console.log('check1 In updateMany --> (!areDocumentsEqual)')
|
|
@@ -24,7 +24,7 @@ export const comparePassword = async (plaintext: string, storedPassword: string)
|
|
|
24
24
|
const [storedHash, storedSalt] = storedPassword.split('.')
|
|
25
25
|
|
|
26
26
|
if (!storedHash || !storedSalt) {
|
|
27
|
-
throw new Error(
|
|
27
|
+
throw new Error('Invalid credentials');
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
const storedBuffer = Buffer.from(storedHash, 'hex')
|