@flowerforce/flowerbase 1.4.2-beta.3 → 1.4.2-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/auth/controller.d.ts.map +1 -1
- package/dist/auth/controller.js +8 -0
- package/dist/auth/providers/anon-user/controller.d.ts.map +1 -1
- package/dist/auth/providers/anon-user/controller.js +9 -15
- package/dist/auth/utils.js +8 -8
- package/dist/features/functions/controller.d.ts.map +1 -1
- package/dist/features/functions/controller.js +2 -1
- package/dist/features/functions/dtos.d.ts +1 -0
- package/dist/features/functions/dtos.d.ts.map +1 -1
- package/dist/features/functions/interface.d.ts +1 -0
- package/dist/features/functions/interface.d.ts.map +1 -1
- package/dist/features/functions/utils.d.ts +2 -2
- package/dist/features/functions/utils.d.ts.map +1 -1
- package/dist/features/functions/utils.js +13 -5
- package/dist/services/mongodb-atlas/index.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/index.js +25 -13
- package/dist/services/mongodb-atlas/model.d.ts +4 -4
- package/dist/services/mongodb-atlas/model.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/auth/__tests__/controller.test.ts +51 -0
- package/src/auth/controller.ts +11 -0
- package/src/auth/providers/anon-user/__tests__/controller.test.ts +82 -0
- package/src/auth/providers/anon-user/controller.ts +9 -19
- package/src/auth/utils.ts +8 -8
- package/src/features/functions/controller.ts +2 -0
- package/src/features/functions/dtos.ts +1 -0
- package/src/features/functions/interface.ts +1 -0
- package/src/features/functions/utils.ts +21 -4
- package/src/services/mongodb-atlas/index.ts +35 -13
- package/src/services/mongodb-atlas/model.ts +13 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/auth/controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAQzC;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,eAAe,
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/auth/controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAQzC;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,eAAe,iBA0JxD"}
|
package/dist/auth/controller.js
CHANGED
|
@@ -31,6 +31,14 @@ function authController(app) {
|
|
|
31
31
|
catch (error) {
|
|
32
32
|
console.error('Failed to ensure refresh token TTL index', error);
|
|
33
33
|
}
|
|
34
|
+
try {
|
|
35
|
+
yield db.collection(authCollection).createIndex({ email: 1 }, {
|
|
36
|
+
unique: true
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
console.error('Failed to ensure auth email unique index', error);
|
|
41
|
+
}
|
|
34
42
|
app.addHook(HANDLER_TYPE, app.jwtAuthentication);
|
|
35
43
|
/**
|
|
36
44
|
* Endpoint to retrieve the authenticated user's profile.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/anon-user/controller.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/anon-user/controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAOzC;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,eAAe,iBAmE5D"}
|
|
@@ -10,9 +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.anonUserController = anonUserController;
|
|
13
|
+
const bson_1 = require("bson");
|
|
13
14
|
const constants_1 = require("../../../constants");
|
|
14
|
-
const crypto_1 = require("../../../utils/crypto");
|
|
15
15
|
const handleUserRegistration_model_1 = require("../../../shared/models/handleUserRegistration.model");
|
|
16
|
+
const crypto_1 = require("../../../utils/crypto");
|
|
16
17
|
const utils_1 = require("../../utils");
|
|
17
18
|
/**
|
|
18
19
|
* Controller for handling anonymous user login.
|
|
@@ -41,30 +42,23 @@ function anonUserController(app) {
|
|
|
41
42
|
throw new Error('Anonymous authentication disabled');
|
|
42
43
|
}
|
|
43
44
|
const now = new Date();
|
|
44
|
-
const
|
|
45
|
+
const userId = new bson_1.ObjectId();
|
|
46
|
+
const anonEmail = `anon-${userId.toString()}@users.invalid`;
|
|
47
|
+
yield db.collection(authCollection).insertOne({
|
|
48
|
+
_id: userId,
|
|
49
|
+
email: anonEmail,
|
|
45
50
|
status: 'confirmed',
|
|
46
51
|
createdAt: now,
|
|
47
52
|
custom_data: {},
|
|
48
53
|
identities: [
|
|
49
54
|
{
|
|
55
|
+
id: userId.toString(),
|
|
56
|
+
provider_id: userId.toString(),
|
|
50
57
|
provider_type: handleUserRegistration_model_1.PROVIDER.ANON_USER,
|
|
51
58
|
provider_data: {}
|
|
52
59
|
}
|
|
53
60
|
]
|
|
54
61
|
});
|
|
55
|
-
const userId = insertResult.insertedId;
|
|
56
|
-
yield db.collection(authCollection).updateOne({ _id: userId }, {
|
|
57
|
-
$set: {
|
|
58
|
-
identities: [
|
|
59
|
-
{
|
|
60
|
-
id: userId.toString(),
|
|
61
|
-
provider_id: userId.toString(),
|
|
62
|
-
provider_type: handleUserRegistration_model_1.PROVIDER.ANON_USER,
|
|
63
|
-
provider_data: {}
|
|
64
|
-
}
|
|
65
|
-
]
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
62
|
const currentUserData = {
|
|
69
63
|
_id: userId,
|
|
70
64
|
user_data: {}
|
package/dist/auth/utils.js
CHANGED
|
@@ -15,10 +15,10 @@ exports.LOGIN_SCHEMA = {
|
|
|
15
15
|
username: {
|
|
16
16
|
type: 'string',
|
|
17
17
|
pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
|
|
18
|
-
minLength:
|
|
18
|
+
minLength: 5,
|
|
19
19
|
maxLength: 254
|
|
20
20
|
},
|
|
21
|
-
password: { type: 'string', minLength:
|
|
21
|
+
password: { type: 'string', minLength: 6, maxLength: 128 }
|
|
22
22
|
},
|
|
23
23
|
required: ['username', 'password']
|
|
24
24
|
}
|
|
@@ -30,7 +30,7 @@ exports.RESET_SEND_SCHEMA = {
|
|
|
30
30
|
email: {
|
|
31
31
|
type: 'string',
|
|
32
32
|
pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
|
|
33
|
-
minLength:
|
|
33
|
+
minLength: 5,
|
|
34
34
|
maxLength: 254
|
|
35
35
|
}
|
|
36
36
|
},
|
|
@@ -44,10 +44,10 @@ exports.RESET_CALL_SCHEMA = {
|
|
|
44
44
|
email: {
|
|
45
45
|
type: 'string',
|
|
46
46
|
pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
|
|
47
|
-
minLength:
|
|
47
|
+
minLength: 5,
|
|
48
48
|
maxLength: 254
|
|
49
49
|
},
|
|
50
|
-
password: { type: 'string', minLength:
|
|
50
|
+
password: { type: 'string', minLength: 6, maxLength: 128 },
|
|
51
51
|
arguments: { type: 'array' }
|
|
52
52
|
},
|
|
53
53
|
required: ['email', 'password']
|
|
@@ -57,7 +57,7 @@ exports.CONFIRM_RESET_SCHEMA = {
|
|
|
57
57
|
body: {
|
|
58
58
|
type: 'object',
|
|
59
59
|
properties: {
|
|
60
|
-
password: { type: 'string', minLength:
|
|
60
|
+
password: { type: 'string', minLength: 6, maxLength: 128 },
|
|
61
61
|
token: { type: 'string' },
|
|
62
62
|
tokenId: { type: 'string' }
|
|
63
63
|
},
|
|
@@ -82,10 +82,10 @@ exports.REGISTRATION_SCHEMA = {
|
|
|
82
82
|
email: {
|
|
83
83
|
type: 'string',
|
|
84
84
|
pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
|
|
85
|
-
minLength:
|
|
85
|
+
minLength: 5,
|
|
86
86
|
maxLength: 254
|
|
87
87
|
},
|
|
88
|
-
password: { type: 'string', minLength:
|
|
88
|
+
password: { type: 'string', minLength: 6, maxLength: 128 }
|
|
89
89
|
},
|
|
90
90
|
required: ['email', 'password']
|
|
91
91
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../src/features/functions/controller.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AA2ChD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,EAAE,
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../src/features/functions/controller.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AA2ChD;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,EAAE,kBA0JjC,CAAA"}
|
|
@@ -67,7 +67,7 @@ const functionsController = (app_1, _a) => __awaiter(void 0, [app_1, _a], void 0
|
|
|
67
67
|
if (!serviceFn) {
|
|
68
68
|
throw new Error(`Service "${req.body.service}" does not exist`);
|
|
69
69
|
}
|
|
70
|
-
const [{ database, collection, query, filter, update, options, returnNewDocument, document, documents, pipeline = [] }] = args;
|
|
70
|
+
const [{ database, collection, query, filter, update, projection, options, returnNewDocument, document, documents, pipeline = [] }] = args;
|
|
71
71
|
const currentMethod = serviceFn(app, { rules, user })
|
|
72
72
|
.db(database)
|
|
73
73
|
.collection(collection)[method];
|
|
@@ -77,6 +77,7 @@ const functionsController = (app_1, _a) => __awaiter(void 0, [app_1, _a], void 0
|
|
|
77
77
|
query,
|
|
78
78
|
filter,
|
|
79
79
|
update,
|
|
80
|
+
projection,
|
|
80
81
|
options,
|
|
81
82
|
returnNewDocument,
|
|
82
83
|
document,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dtos.d.ts","sourceRoot":"","sources":["../../../src/features/functions/dtos.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AAEzE,KAAK,UAAU,GAAG,OAAO,CAAC,MAAM,UAAU,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC,CAAA;AAE1E,MAAM,MAAM,eAAe,GACvB;IACA,IAAI,EAAE,UAAU,CAAA;IAChB,SAAS,EAAE,aAAa,CAAA;CACzB,GACC;IACA,SAAS,EAAE,aAAa,CAAA;IACxB,IAAI,EAAE,UAAU,CAAA;IAChB,OAAO,EAAE,eAAe,CAAA;CACzB,CAAA;AAEH,MAAM,MAAM,qBAAqB,GAAG;IAClC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,KAAK,aAAa,GAAG,SAAS,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,QAAQ,CAAA;IACjB,KAAK,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAA;IACvC,MAAM,EAAE,QAAQ,CAAA;IAChB,OAAO,CAAC,EAAE,QAAQ,CAAA;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,EAAE,QAAQ,CAAA;IAClB,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;CACtB,CAAC,CAAA;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,KAAK,QAAQ,GAAG;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA"}
|
|
1
|
+
{"version":3,"file":"dtos.d.ts","sourceRoot":"","sources":["../../../src/features/functions/dtos.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AAEzE,KAAK,UAAU,GAAG,OAAO,CAAC,MAAM,UAAU,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC,CAAA;AAE1E,MAAM,MAAM,eAAe,GACvB;IACA,IAAI,EAAE,UAAU,CAAA;IAChB,SAAS,EAAE,aAAa,CAAA;CACzB,GACC;IACA,SAAS,EAAE,aAAa,CAAA;IACxB,IAAI,EAAE,UAAU,CAAA;IAChB,OAAO,EAAE,eAAe,CAAA;CACzB,CAAA;AAEH,MAAM,MAAM,qBAAqB,GAAG;IAClC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,KAAK,aAAa,GAAG,SAAS,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,QAAQ,CAAA;IACjB,KAAK,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAA;IACvC,MAAM,EAAE,QAAQ,CAAA;IAChB,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,CAAC,EAAE,QAAQ,EAAE,CAAA;CACtB,CAAC,CAAA;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,KAAK,QAAQ,GAAG;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA"}
|
|
@@ -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;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtE,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,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"}
|
|
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;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEtE,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"}
|
|
@@ -11,11 +11,11 @@ export declare const loadFunctions: (rootDir?: string) => Promise<Functions>;
|
|
|
11
11
|
* @param query -> the query data
|
|
12
12
|
* @param update -> the update Document that should be deserialized
|
|
13
13
|
*/
|
|
14
|
-
export declare const executeQuery: ({ currentMethod, query, update, filter, options, returnNewDocument, document, documents, pipeline, isClient }: ExecuteQueryParams) => Promise<{
|
|
14
|
+
export declare const executeQuery: ({ currentMethod, query, update, filter, projection, options, returnNewDocument, document, documents, pipeline, isClient }: ExecuteQueryParams) => Promise<{
|
|
15
15
|
find: () => Promise<any[]>;
|
|
16
16
|
findOne: () => Promise<unknown>;
|
|
17
17
|
count: () => Promise<number>;
|
|
18
|
-
deleteOne: () => Promise<
|
|
18
|
+
deleteOne: () => Promise<import("mongodb").DeleteResult>;
|
|
19
19
|
insertOne: () => Promise<import("mongodb").InsertOneResult<Document>>;
|
|
20
20
|
updateOne: () => Promise<unknown> | import("mongodb").FindCursor<any> | import("mongodb").ChangeStream<Document, Document> | import("mongodb").AggregationCursor<Document>;
|
|
21
21
|
findOneAndUpdate: () => Promise<Document | null>;
|
|
@@ -1 +1 @@
|
|
|
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
|
|
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,2HAYhC,kBAAkB;;;;;;;;;;;;EA4FpB,CAAA"}
|
|
@@ -58,7 +58,7 @@ exports.loadFunctions = loadFunctions;
|
|
|
58
58
|
* @param query -> the query data
|
|
59
59
|
* @param update -> the update Document that should be deserialized
|
|
60
60
|
*/
|
|
61
|
-
const executeQuery = (_a) => __awaiter(void 0, [_a], void 0, function* ({ currentMethod, query, update, filter, options, returnNewDocument, document, documents, pipeline, isClient = false }) {
|
|
61
|
+
const executeQuery = (_a) => __awaiter(void 0, [_a], void 0, function* ({ currentMethod, query, update, filter, projection, options, returnNewDocument, document, documents, pipeline, isClient = false }) {
|
|
62
62
|
const resolvedQuery = typeof query !== 'undefined'
|
|
63
63
|
? query
|
|
64
64
|
: typeof filter !== 'undefined'
|
|
@@ -71,10 +71,18 @@ const executeQuery = (_a) => __awaiter(void 0, [_a], void 0, function* ({ curren
|
|
|
71
71
|
? { returnDocument: returnNewDocument ? 'after' : 'before' }
|
|
72
72
|
: undefined;
|
|
73
73
|
const parsedOptions = resolvedOptions ? bson_1.EJSON.deserialize(resolvedOptions) : undefined;
|
|
74
|
+
const parsedProjection = typeof projection !== 'undefined' ? bson_1.EJSON.deserialize(projection) : undefined;
|
|
75
|
+
const resolvedProjection = typeof projection !== 'undefined'
|
|
76
|
+
? parsedProjection
|
|
77
|
+
: parsedOptions &&
|
|
78
|
+
typeof parsedOptions === 'object' &&
|
|
79
|
+
'projection' in parsedOptions
|
|
80
|
+
? parsedOptions.projection
|
|
81
|
+
: undefined;
|
|
74
82
|
return {
|
|
75
83
|
find: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
76
84
|
return yield (() => {
|
|
77
|
-
const cursor = currentMethod(bson_1.EJSON.deserialize(resolvedQuery));
|
|
85
|
+
const cursor = currentMethod(bson_1.EJSON.deserialize(resolvedQuery), resolvedProjection, parsedOptions);
|
|
78
86
|
if (parsedOptions === null || parsedOptions === void 0 ? void 0 : parsedOptions.sort) {
|
|
79
87
|
cursor.sort(parsedOptions.sort);
|
|
80
88
|
}
|
|
@@ -87,9 +95,9 @@ const executeQuery = (_a) => __awaiter(void 0, [_a], void 0, function* ({ curren
|
|
|
87
95
|
return cursor.toArray();
|
|
88
96
|
})();
|
|
89
97
|
}),
|
|
90
|
-
findOne: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery)),
|
|
98
|
+
findOne: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), resolvedProjection, parsedOptions),
|
|
91
99
|
count: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), parsedOptions),
|
|
92
|
-
deleteOne: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery)),
|
|
100
|
+
deleteOne: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), parsedOptions),
|
|
93
101
|
insertOne: () => currentMethod(bson_1.EJSON.deserialize(document)),
|
|
94
102
|
updateOne: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), bson_1.EJSON.deserialize(resolvedUpdate)),
|
|
95
103
|
findOneAndUpdate: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), bson_1.EJSON.deserialize(resolvedUpdate), parsedOptions),
|
|
@@ -99,7 +107,7 @@ const executeQuery = (_a) => __awaiter(void 0, [_a], void 0, function* ({ curren
|
|
|
99
107
|
}),
|
|
100
108
|
insertMany: () => currentMethod(bson_1.EJSON.deserialize(documents)),
|
|
101
109
|
updateMany: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), bson_1.EJSON.deserialize(resolvedUpdate)),
|
|
102
|
-
deleteMany: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery))
|
|
110
|
+
deleteMany: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), parsedOptions)
|
|
103
111
|
};
|
|
104
112
|
});
|
|
105
113
|
exports.executeQuery = executeQuery;
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AAm2BrF,QAAA,MAAM,YAAY,EAAE,oBAsBlB,CAAA;AAEF,eAAe,YAAY,CAAA"}
|
|
@@ -75,6 +75,8 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
75
75
|
* Finds a single document in a MongoDB collection with optional role-based filtering and validation.
|
|
76
76
|
*
|
|
77
77
|
* @param {Filter<Document>} query - The MongoDB query used to match the document.
|
|
78
|
+
* @param {Document} [projection] - Optional projection to select returned fields.
|
|
79
|
+
* @param {FindOneOptions} [options] - Optional settings for the findOne operation.
|
|
78
80
|
* @returns {Promise<Document | {} | null>} A promise resolving to the document if found and permitted, an empty object if access is denied, or `null` if not found.
|
|
79
81
|
*
|
|
80
82
|
* @description
|
|
@@ -86,12 +88,15 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
86
88
|
* - Validates the result using `checkValidation` to ensure read permission.
|
|
87
89
|
* - If validation fails, returns an empty object; otherwise returns the validated document.
|
|
88
90
|
*/
|
|
89
|
-
findOne: (
|
|
91
|
+
findOne: (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (query = {}, projection, options) {
|
|
90
92
|
var _a;
|
|
93
|
+
const resolvedOptions = projection || options
|
|
94
|
+
? Object.assign(Object.assign({}, (options !== null && options !== void 0 ? options : {})), (projection ? { projection } : {})) : undefined;
|
|
95
|
+
const resolvedQuery = query !== null && query !== void 0 ? query : {};
|
|
91
96
|
if (!run_as_system) {
|
|
92
97
|
(0, utils_2.checkDenyOperation)(normalizedRules, collection.collectionName, model_1.CRUD_OPERATIONS.READ);
|
|
93
98
|
// Apply access control filters to the query
|
|
94
|
-
const formattedQuery = (0, utils_2.getFormattedQuery)(filters,
|
|
99
|
+
const formattedQuery = (0, utils_2.getFormattedQuery)(filters, resolvedQuery, user);
|
|
95
100
|
logDebug('update formattedQuery', {
|
|
96
101
|
collection: collName,
|
|
97
102
|
query,
|
|
@@ -106,7 +111,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
106
111
|
logService('findOne query', { collName, formattedQuery });
|
|
107
112
|
const safeQuery = (0, utils_2.normalizeQuery)(formattedQuery);
|
|
108
113
|
logService('findOne normalizedQuery', { collName, safeQuery });
|
|
109
|
-
const result = yield collection.findOne({ $and: safeQuery });
|
|
114
|
+
const result = yield collection.findOne({ $and: safeQuery }, resolvedOptions);
|
|
110
115
|
logDebug('findOne result', {
|
|
111
116
|
collection: collName,
|
|
112
117
|
result
|
|
@@ -130,12 +135,13 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
130
135
|
return Promise.resolve(status ? document : {});
|
|
131
136
|
}
|
|
132
137
|
// System mode: no validation applied
|
|
133
|
-
return collection.findOne(
|
|
138
|
+
return collection.findOne(resolvedQuery, resolvedOptions);
|
|
134
139
|
}),
|
|
135
140
|
/**
|
|
136
141
|
* Deletes a single document from a MongoDB collection with optional role-based validation.
|
|
137
142
|
*
|
|
138
143
|
* @param {Filter<Document>} [query={}] - The MongoDB query used to match the document to delete.
|
|
144
|
+
* @param {DeleteOptions} [options] - Optional settings for the delete operation.
|
|
139
145
|
* @returns {Promise<DeleteResult>} A promise resolving to the result of the delete operation.
|
|
140
146
|
*
|
|
141
147
|
* @throws {Error} If the user is not authorized to delete the document.
|
|
@@ -149,7 +155,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
149
155
|
* - If validation fails, throws an error.
|
|
150
156
|
* - If validation passes, deletes the document using the filtered query.
|
|
151
157
|
*/
|
|
152
|
-
deleteOne: (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (query = {}) {
|
|
158
|
+
deleteOne: (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (query = {}, options) {
|
|
153
159
|
var _a;
|
|
154
160
|
if (!run_as_system) {
|
|
155
161
|
(0, utils_2.checkDenyOperation)(normalizedRules, collection.collectionName, model_1.CRUD_OPERATIONS.DELETE);
|
|
@@ -174,10 +180,10 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
174
180
|
if (!status) {
|
|
175
181
|
throw new Error('Delete not permitted');
|
|
176
182
|
}
|
|
177
|
-
return collection.deleteOne({ $and: formattedQuery });
|
|
183
|
+
return collection.deleteOne({ $and: formattedQuery }, options);
|
|
178
184
|
}
|
|
179
185
|
// System mode: bypass access control
|
|
180
|
-
return collection.deleteOne(query);
|
|
186
|
+
return collection.deleteOne(query, options);
|
|
181
187
|
}),
|
|
182
188
|
/**
|
|
183
189
|
* Inserts a single document into a MongoDB collection with optional role-based validation.
|
|
@@ -372,6 +378,9 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
372
378
|
* Finds documents in a MongoDB collection with optional role-based access control and post-query validation.
|
|
373
379
|
*
|
|
374
380
|
* @param {Filter<Document>} query - The MongoDB query to filter documents.
|
|
381
|
+
* @param {Document} [projection] - Optional projection to select returned fields.
|
|
382
|
+
* @param {FindOptions} [options] - Optional settings for the find operation.
|
|
383
|
+
* @param {FindOptions} [options] - Optional settings for the find operation.
|
|
375
384
|
* @returns {FindCursor} A customized `FindCursor` that includes additional access control logic in its `toArray()` method.
|
|
376
385
|
*
|
|
377
386
|
* @description
|
|
@@ -385,14 +394,16 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
385
394
|
*
|
|
386
395
|
* This ensures that both pre-query filtering and post-query validation are applied consistently.
|
|
387
396
|
*/
|
|
388
|
-
find: (query) => {
|
|
397
|
+
find: (query = {}, projection, options) => {
|
|
398
|
+
const resolvedOptions = projection || options
|
|
399
|
+
? Object.assign(Object.assign({}, (options !== null && options !== void 0 ? options : {})), (projection ? { projection } : {})) : undefined;
|
|
389
400
|
if (!run_as_system) {
|
|
390
401
|
(0, utils_2.checkDenyOperation)(normalizedRules, collection.collectionName, model_1.CRUD_OPERATIONS.READ);
|
|
391
402
|
// Pre-query filtering based on access control rules
|
|
392
403
|
const formattedQuery = (0, utils_2.getFormattedQuery)(filters, query, user);
|
|
393
404
|
const currentQuery = formattedQuery.length ? { $and: formattedQuery } : {};
|
|
394
405
|
// aggiunto filter per evitare questo errore: $and argument's entries must be objects
|
|
395
|
-
const cursor = collection.find(currentQuery);
|
|
406
|
+
const cursor = collection.find(currentQuery, resolvedOptions);
|
|
396
407
|
const originalToArray = cursor.toArray.bind(cursor);
|
|
397
408
|
/**
|
|
398
409
|
* Overridden `toArray` method that validates each document for read access.
|
|
@@ -425,7 +436,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
425
436
|
return cursor;
|
|
426
437
|
}
|
|
427
438
|
// System mode: return original unfiltered cursor
|
|
428
|
-
return collection.find(query);
|
|
439
|
+
return collection.find(query, resolvedOptions);
|
|
429
440
|
},
|
|
430
441
|
count: (query, options) => {
|
|
431
442
|
if (!run_as_system) {
|
|
@@ -640,6 +651,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
640
651
|
* Deletes multiple documents from a MongoDB collection with role-based access control and validation.
|
|
641
652
|
*
|
|
642
653
|
* @param query - The initial MongoDB query to filter documents to be deleted.
|
|
654
|
+
* @param {DeleteOptions} [options] - Optional settings for the delete operation.
|
|
643
655
|
* @returns {Promise<{ acknowledged: boolean, deletedCount: number }>} A promise resolving to the deletion result.
|
|
644
656
|
*
|
|
645
657
|
* @description
|
|
@@ -650,7 +662,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
650
662
|
* - Validates each document against user roles.
|
|
651
663
|
* - Deletes only the documents that the current user has permission to delete.
|
|
652
664
|
*/
|
|
653
|
-
deleteMany: (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (query = {}) {
|
|
665
|
+
deleteMany: (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (query = {}, options) {
|
|
654
666
|
if (!run_as_system) {
|
|
655
667
|
(0, utils_2.checkDenyOperation)(normalizedRules, collection.collectionName, model_1.CRUD_OPERATIONS.DELETE);
|
|
656
668
|
// Apply access control filters
|
|
@@ -682,10 +694,10 @@ const getOperators = (collection, { rules, collName, user, run_as_system }) => {
|
|
|
682
694
|
const deleteQuery = {
|
|
683
695
|
$and: [...formattedQuery, { _id: { $in: elementsToDelete } }]
|
|
684
696
|
};
|
|
685
|
-
return collection.deleteMany(deleteQuery);
|
|
697
|
+
return collection.deleteMany(deleteQuery, options);
|
|
686
698
|
}
|
|
687
699
|
// If running as system, bypass access control and delete directly
|
|
688
|
-
return collection.deleteMany(query);
|
|
700
|
+
return collection.deleteMany(query, options);
|
|
689
701
|
})
|
|
690
702
|
};
|
|
691
703
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FastifyInstance } from 'fastify';
|
|
2
|
-
import { Collection, Document, FindCursor, FindOneAndUpdateOptions, Filter as MongoFilter, UpdateFilter, WithId } from 'mongodb';
|
|
2
|
+
import { Collection, Document, FindCursor, FindOneAndUpdateOptions, FindOneOptions, FindOptions, Filter as MongoFilter, UpdateFilter, WithId } from 'mongodb';
|
|
3
3
|
import { User } from '../../auth/dtos';
|
|
4
4
|
import { Filter, Rules } from '../../features/rules/interface';
|
|
5
5
|
import { Role } from '../../utils/roles/interface';
|
|
@@ -24,12 +24,12 @@ export type GetOperatorsFunction = (collection: Collection<Document>, { rules, c
|
|
|
24
24
|
run_as_system?: boolean;
|
|
25
25
|
collName: string;
|
|
26
26
|
}) => {
|
|
27
|
-
findOne: (
|
|
28
|
-
deleteOne: (...params: Parameters<Method<'
|
|
27
|
+
findOne: (filter?: MongoFilter<Document>, projection?: Document, options?: FindOneOptions) => ReturnType<Method<'findOne'>>;
|
|
28
|
+
deleteOne: (...params: Parameters<Method<'deleteOne'>>) => ReturnType<Method<'deleteOne'>>;
|
|
29
29
|
insertOne: (...params: Parameters<Method<'insertOne'>>) => ReturnType<Method<'insertOne'>>;
|
|
30
30
|
updateOne: (...params: Parameters<Method<'updateOne'>>) => ReturnType<Method<'updateOne'>>;
|
|
31
31
|
findOneAndUpdate: (filter: MongoFilter<Document>, update: UpdateFilter<Document> | Document[], options?: FindOneAndUpdateOptions) => Promise<Document | null>;
|
|
32
|
-
find: (
|
|
32
|
+
find: (filter?: MongoFilter<Document>, projection?: Document, options?: FindOptions) => FindCursor;
|
|
33
33
|
count: (...params: Parameters<Method<'countDocuments'>>) => ReturnType<Method<'countDocuments'>>;
|
|
34
34
|
watch: (...params: Parameters<Method<'watch'>>) => ReturnType<Method<'watch'>>;
|
|
35
35
|
aggregate: (...params: [...Parameters<Method<'aggregate'>>, isClient: boolean]) => ReturnType<Method<'aggregate'>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/services/mongodb-atlas/model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EACL,UAAU,EACV,QAAQ,EACR,UAAU,EACV,uBAAuB,EACvB,MAAM,IAAI,WAAW,EACrB,YAAY,EACZ,MAAM,EACP,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AACtC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAA;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,6BAA6B,CAAA;AAElD,MAAM,MAAM,oBAAoB,GAAG,CACjC,GAAG,EAAE,eAAe,EACpB,EACE,KAAK,EACL,IAAI,EACJ,aAAa,EACd,EAAE;IACD,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,KAAK,CAAC,EAAE,KAAK,CAAA;IACb,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB,KACE;IACH,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK;QACtB,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,UAAU,CAAC,oBAAoB,CAAC,CAAA;KACnE,CAAA;CACF,CAAA;AAED,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,IAAI,GAAG,MAAM,IAAI;IACxD,OAAO,EAAE,CAAC,EAAE,CAAA;IACZ,IAAI,EAAE,IAAI,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,QAAQ,GAAG,IAAI,CAAA;CAC5C,CAAA;AACD,KAAK,MAAM,CAAC,CAAC,SAAS,MAAM,UAAU,CAAC,QAAQ,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAA;AAE3E,MAAM,MAAM,oBAAoB,GAAG,CACjC,UAAU,EAAE,UAAU,CAAC,QAAQ,CAAC,EAChC,EACE,KAAK,EACL,QAAQ,EACR,IAAI,EACJ,aAAa,EACd,EAAE;IACD,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,KAAK,CAAC,EAAE,KAAK,CAAA;IACb,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAA;CACjB,KACE;IACH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/services/mongodb-atlas/model.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { authController } from '../controller'
|
|
2
|
+
|
|
3
|
+
jest.mock('../../constants', () => ({
|
|
4
|
+
AUTH_CONFIG: {
|
|
5
|
+
authCollection: 'auth_users',
|
|
6
|
+
refreshTokensCollection: 'refresh_tokens',
|
|
7
|
+
userCollection: 'users',
|
|
8
|
+
user_id_field: 'id'
|
|
9
|
+
},
|
|
10
|
+
DB_NAME: 'test-db',
|
|
11
|
+
DEFAULT_CONFIG: {
|
|
12
|
+
REFRESH_TOKEN_TTL_DAYS: 1
|
|
13
|
+
}
|
|
14
|
+
}))
|
|
15
|
+
|
|
16
|
+
describe('authController', () => {
|
|
17
|
+
it('creates a unique email index on the auth collection', async () => {
|
|
18
|
+
const authCollection = {
|
|
19
|
+
createIndex: jest.fn().mockResolvedValue('ok')
|
|
20
|
+
}
|
|
21
|
+
const refreshCollection = {
|
|
22
|
+
createIndex: jest.fn().mockResolvedValue('ok')
|
|
23
|
+
}
|
|
24
|
+
const db = {
|
|
25
|
+
collection: jest.fn((name: string) => {
|
|
26
|
+
if (name === 'auth_users') {
|
|
27
|
+
return authCollection
|
|
28
|
+
}
|
|
29
|
+
if (name === 'refresh_tokens') {
|
|
30
|
+
return refreshCollection
|
|
31
|
+
}
|
|
32
|
+
return { createIndex: jest.fn().mockResolvedValue('ok') }
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
const app = {
|
|
36
|
+
mongo: { client: { db: jest.fn().mockReturnValue(db) } },
|
|
37
|
+
addHook: jest.fn(),
|
|
38
|
+
get: jest.fn(),
|
|
39
|
+
post: jest.fn(),
|
|
40
|
+
delete: jest.fn(),
|
|
41
|
+
jwtAuthentication: jest.fn()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
await authController(app as unknown as never)
|
|
45
|
+
|
|
46
|
+
expect(authCollection.createIndex).toHaveBeenCalledWith(
|
|
47
|
+
{ email: 1 },
|
|
48
|
+
{ unique: true }
|
|
49
|
+
)
|
|
50
|
+
})
|
|
51
|
+
})
|
package/src/auth/controller.ts
CHANGED
|
@@ -27,6 +27,17 @@ export async function authController(app: FastifyInstance) {
|
|
|
27
27
|
console.error('Failed to ensure refresh token TTL index', error)
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
try {
|
|
31
|
+
await db.collection(authCollection).createIndex(
|
|
32
|
+
{ email: 1 },
|
|
33
|
+
{
|
|
34
|
+
unique: true
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
} catch (error) {
|
|
38
|
+
console.error('Failed to ensure auth email unique index', error)
|
|
39
|
+
}
|
|
40
|
+
|
|
30
41
|
app.addHook(HANDLER_TYPE, app.jwtAuthentication)
|
|
31
42
|
|
|
32
43
|
/**
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { anonUserController } from '../controller'
|
|
2
|
+
import { PROVIDER } from '../../../../shared/models/handleUserRegistration.model'
|
|
3
|
+
|
|
4
|
+
jest.mock('../../../../constants', () => ({
|
|
5
|
+
AUTH_CONFIG: {
|
|
6
|
+
authCollection: 'auth_users',
|
|
7
|
+
refreshTokensCollection: 'refresh_tokens',
|
|
8
|
+
providers: {
|
|
9
|
+
'anon-user': { disabled: false }
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
DB_NAME: 'test-db',
|
|
13
|
+
DEFAULT_CONFIG: {
|
|
14
|
+
REFRESH_TOKEN_TTL_DAYS: 1,
|
|
15
|
+
ANON_USER_TTL_SECONDS: 3600
|
|
16
|
+
}
|
|
17
|
+
}))
|
|
18
|
+
|
|
19
|
+
describe('anonUserController', () => {
|
|
20
|
+
it('inserts anon users with a generated email', async () => {
|
|
21
|
+
let insertedDoc: Record<string, unknown> | undefined
|
|
22
|
+
const authCollection = {
|
|
23
|
+
createIndex: jest.fn().mockResolvedValue('ok'),
|
|
24
|
+
insertOne: jest.fn(async (doc: Record<string, unknown>) => {
|
|
25
|
+
insertedDoc = doc
|
|
26
|
+
return { insertedId: doc._id }
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
const refreshCollection = {
|
|
30
|
+
insertOne: jest.fn().mockResolvedValue({})
|
|
31
|
+
}
|
|
32
|
+
const db = {
|
|
33
|
+
collection: jest.fn((name: string) => {
|
|
34
|
+
if (name === 'auth_users') {
|
|
35
|
+
return authCollection
|
|
36
|
+
}
|
|
37
|
+
if (name === 'refresh_tokens') {
|
|
38
|
+
return refreshCollection
|
|
39
|
+
}
|
|
40
|
+
return authCollection
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let loginHandler: ((...args: unknown[]) => unknown) | undefined
|
|
45
|
+
const app = {
|
|
46
|
+
mongo: { client: { db: jest.fn().mockReturnValue(db) } },
|
|
47
|
+
post: jest.fn((path: string, handler: (...args: unknown[]) => unknown) => {
|
|
48
|
+
loginHandler = handler
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
await anonUserController(app as unknown as never)
|
|
53
|
+
|
|
54
|
+
const context = {
|
|
55
|
+
createRefreshToken: jest.fn(() => 'refresh'),
|
|
56
|
+
createAccessToken: jest.fn(() => 'access')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
await (loginHandler as (...args: unknown[]) => Promise<unknown>).call(context, {})
|
|
60
|
+
|
|
61
|
+
const doc = insertedDoc as {
|
|
62
|
+
_id: { toString: () => string }
|
|
63
|
+
email: string
|
|
64
|
+
identities: Array<{
|
|
65
|
+
id?: string
|
|
66
|
+
provider_id?: string
|
|
67
|
+
provider_type?: string
|
|
68
|
+
provider_data?: Record<string, unknown>
|
|
69
|
+
}>
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
expect(doc.email).toBe(`anon-${doc._id.toString()}@users.invalid`)
|
|
73
|
+
expect(doc.identities[0]).toEqual(
|
|
74
|
+
expect.objectContaining({
|
|
75
|
+
id: doc._id.toString(),
|
|
76
|
+
provider_id: doc._id.toString(),
|
|
77
|
+
provider_type: PROVIDER.ANON_USER,
|
|
78
|
+
provider_data: {}
|
|
79
|
+
})
|
|
80
|
+
)
|
|
81
|
+
})
|
|
82
|
+
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { ObjectId } from 'bson'
|
|
1
2
|
import { FastifyInstance } from 'fastify'
|
|
2
3
|
import { AUTH_CONFIG, DB_NAME, DEFAULT_CONFIG } from '../../../constants'
|
|
3
|
-
import { hashToken } from '../../../utils/crypto'
|
|
4
4
|
import { PROVIDER } from '../../../shared/models/handleUserRegistration.model'
|
|
5
|
+
import { hashToken } from '../../../utils/crypto'
|
|
5
6
|
import { AUTH_ENDPOINTS } from '../../utils'
|
|
6
7
|
import { LoginDto } from './dtos'
|
|
7
8
|
|
|
@@ -37,35 +38,24 @@ export async function anonUserController(app: FastifyInstance) {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
const now = new Date()
|
|
40
|
-
const
|
|
41
|
+
const userId = new ObjectId()
|
|
42
|
+
const anonEmail = `anon-${userId.toString()}@users.invalid`
|
|
43
|
+
await db.collection(authCollection!).insertOne({
|
|
44
|
+
_id: userId,
|
|
45
|
+
email: anonEmail,
|
|
41
46
|
status: 'confirmed',
|
|
42
47
|
createdAt: now,
|
|
43
48
|
custom_data: {},
|
|
44
49
|
identities: [
|
|
45
50
|
{
|
|
51
|
+
id: userId.toString(),
|
|
52
|
+
provider_id: userId.toString(),
|
|
46
53
|
provider_type: PROVIDER.ANON_USER,
|
|
47
54
|
provider_data: {}
|
|
48
55
|
}
|
|
49
56
|
]
|
|
50
57
|
})
|
|
51
58
|
|
|
52
|
-
const userId = insertResult.insertedId
|
|
53
|
-
await db.collection(authCollection!).updateOne(
|
|
54
|
-
{ _id: userId },
|
|
55
|
-
{
|
|
56
|
-
$set: {
|
|
57
|
-
identities: [
|
|
58
|
-
{
|
|
59
|
-
id: userId.toString(),
|
|
60
|
-
provider_id: userId.toString(),
|
|
61
|
-
provider_type: PROVIDER.ANON_USER,
|
|
62
|
-
provider_data: {}
|
|
63
|
-
}
|
|
64
|
-
]
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
)
|
|
68
|
-
|
|
69
59
|
const currentUserData = {
|
|
70
60
|
_id: userId,
|
|
71
61
|
user_data: {}
|
package/src/auth/utils.ts
CHANGED
|
@@ -11,10 +11,10 @@ export const LOGIN_SCHEMA = {
|
|
|
11
11
|
username: {
|
|
12
12
|
type: 'string',
|
|
13
13
|
pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
|
|
14
|
-
minLength:
|
|
14
|
+
minLength: 5,
|
|
15
15
|
maxLength: 254
|
|
16
16
|
},
|
|
17
|
-
password: { type: 'string', minLength:
|
|
17
|
+
password: { type: 'string', minLength: 6, maxLength: 128 }
|
|
18
18
|
},
|
|
19
19
|
required: ['username', 'password']
|
|
20
20
|
}
|
|
@@ -27,7 +27,7 @@ export const RESET_SEND_SCHEMA = {
|
|
|
27
27
|
email: {
|
|
28
28
|
type: 'string',
|
|
29
29
|
pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
|
|
30
|
-
minLength:
|
|
30
|
+
minLength: 5,
|
|
31
31
|
maxLength: 254
|
|
32
32
|
}
|
|
33
33
|
},
|
|
@@ -42,10 +42,10 @@ export const RESET_CALL_SCHEMA = {
|
|
|
42
42
|
email: {
|
|
43
43
|
type: 'string',
|
|
44
44
|
pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
|
|
45
|
-
minLength:
|
|
45
|
+
minLength: 5,
|
|
46
46
|
maxLength: 254
|
|
47
47
|
},
|
|
48
|
-
password: { type: 'string', minLength:
|
|
48
|
+
password: { type: 'string', minLength: 6, maxLength: 128 },
|
|
49
49
|
arguments: { type: 'array' }
|
|
50
50
|
},
|
|
51
51
|
required: ['email', 'password']
|
|
@@ -56,7 +56,7 @@ export const CONFIRM_RESET_SCHEMA = {
|
|
|
56
56
|
body: {
|
|
57
57
|
type: 'object',
|
|
58
58
|
properties: {
|
|
59
|
-
password: { type: 'string', minLength:
|
|
59
|
+
password: { type: 'string', minLength: 6, maxLength: 128 },
|
|
60
60
|
token: { type: 'string' },
|
|
61
61
|
tokenId: { type: 'string' }
|
|
62
62
|
},
|
|
@@ -84,10 +84,10 @@ export const REGISTRATION_SCHEMA = {
|
|
|
84
84
|
email: {
|
|
85
85
|
type: 'string',
|
|
86
86
|
pattern: '^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$',
|
|
87
|
-
minLength:
|
|
87
|
+
minLength: 5,
|
|
88
88
|
maxLength: 254
|
|
89
89
|
},
|
|
90
|
-
password: { type: 'string', minLength:
|
|
90
|
+
password: { type: 'string', minLength: 6, maxLength: 128 }
|
|
91
91
|
},
|
|
92
92
|
required: ['email', 'password']
|
|
93
93
|
}
|
|
@@ -81,6 +81,7 @@ export const functionsController: FunctionController = async (
|
|
|
81
81
|
query,
|
|
82
82
|
filter,
|
|
83
83
|
update,
|
|
84
|
+
projection,
|
|
84
85
|
options,
|
|
85
86
|
returnNewDocument,
|
|
86
87
|
document,
|
|
@@ -98,6 +99,7 @@ export const functionsController: FunctionController = async (
|
|
|
98
99
|
query,
|
|
99
100
|
filter,
|
|
100
101
|
update,
|
|
102
|
+
projection,
|
|
101
103
|
options,
|
|
102
104
|
returnNewDocument,
|
|
103
105
|
document,
|
|
@@ -46,6 +46,7 @@ export const executeQuery = async ({
|
|
|
46
46
|
query,
|
|
47
47
|
update,
|
|
48
48
|
filter,
|
|
49
|
+
projection,
|
|
49
50
|
options,
|
|
50
51
|
returnNewDocument,
|
|
51
52
|
document,
|
|
@@ -67,11 +68,23 @@ export const executeQuery = async ({
|
|
|
67
68
|
? { returnDocument: returnNewDocument ? 'after' : 'before' }
|
|
68
69
|
: undefined
|
|
69
70
|
const parsedOptions = resolvedOptions ? EJSON.deserialize(resolvedOptions) : undefined
|
|
71
|
+
const parsedProjection =
|
|
72
|
+
typeof projection !== 'undefined' ? EJSON.deserialize(projection) : undefined
|
|
73
|
+
const resolvedProjection =
|
|
74
|
+
typeof projection !== 'undefined'
|
|
75
|
+
? parsedProjection
|
|
76
|
+
: parsedOptions &&
|
|
77
|
+
typeof parsedOptions === 'object' &&
|
|
78
|
+
'projection' in parsedOptions
|
|
79
|
+
? (parsedOptions as Document).projection
|
|
80
|
+
: undefined
|
|
70
81
|
return {
|
|
71
82
|
find: async () =>
|
|
72
83
|
await (() => {
|
|
73
84
|
const cursor = (currentMethod as ReturnType<GetOperatorsFunction>['find'])(
|
|
74
|
-
EJSON.deserialize(resolvedQuery)
|
|
85
|
+
EJSON.deserialize(resolvedQuery),
|
|
86
|
+
resolvedProjection,
|
|
87
|
+
parsedOptions
|
|
75
88
|
)
|
|
76
89
|
if (parsedOptions?.sort) {
|
|
77
90
|
cursor.sort(parsedOptions.sort as Document)
|
|
@@ -86,7 +99,9 @@ export const executeQuery = async ({
|
|
|
86
99
|
})(),
|
|
87
100
|
findOne: () =>
|
|
88
101
|
(currentMethod as ReturnType<GetOperatorsFunction>['findOne'])(
|
|
89
|
-
EJSON.deserialize(resolvedQuery)
|
|
102
|
+
EJSON.deserialize(resolvedQuery),
|
|
103
|
+
resolvedProjection,
|
|
104
|
+
parsedOptions
|
|
90
105
|
),
|
|
91
106
|
count: () =>
|
|
92
107
|
(currentMethod as ReturnType<GetOperatorsFunction>['count'])(
|
|
@@ -95,7 +110,8 @@ export const executeQuery = async ({
|
|
|
95
110
|
),
|
|
96
111
|
deleteOne: () =>
|
|
97
112
|
(currentMethod as ReturnType<GetOperatorsFunction>['deleteOne'])(
|
|
98
|
-
EJSON.deserialize(resolvedQuery)
|
|
113
|
+
EJSON.deserialize(resolvedQuery),
|
|
114
|
+
parsedOptions
|
|
99
115
|
),
|
|
100
116
|
insertOne: () =>
|
|
101
117
|
(currentMethod as ReturnType<GetOperatorsFunction>['insertOne'])(
|
|
@@ -125,7 +141,8 @@ export const executeQuery = async ({
|
|
|
125
141
|
),
|
|
126
142
|
deleteMany: () =>
|
|
127
143
|
(currentMethod as ReturnType<GetOperatorsFunction>['deleteMany'])(
|
|
128
|
-
EJSON.deserialize(resolvedQuery)
|
|
144
|
+
EJSON.deserialize(resolvedQuery),
|
|
145
|
+
parsedOptions
|
|
129
146
|
)
|
|
130
147
|
}
|
|
131
148
|
}
|
|
@@ -89,6 +89,8 @@ const getOperators: GetOperatorsFunction = (
|
|
|
89
89
|
* Finds a single document in a MongoDB collection with optional role-based filtering and validation.
|
|
90
90
|
*
|
|
91
91
|
* @param {Filter<Document>} query - The MongoDB query used to match the document.
|
|
92
|
+
* @param {Document} [projection] - Optional projection to select returned fields.
|
|
93
|
+
* @param {FindOneOptions} [options] - Optional settings for the findOne operation.
|
|
92
94
|
* @returns {Promise<Document | {} | null>} A promise resolving to the document if found and permitted, an empty object if access is denied, or `null` if not found.
|
|
93
95
|
*
|
|
94
96
|
* @description
|
|
@@ -100,11 +102,19 @@ const getOperators: GetOperatorsFunction = (
|
|
|
100
102
|
* - Validates the result using `checkValidation` to ensure read permission.
|
|
101
103
|
* - If validation fails, returns an empty object; otherwise returns the validated document.
|
|
102
104
|
*/
|
|
103
|
-
findOne: async (query) => {
|
|
105
|
+
findOne: async (query = {}, projection, options) => {
|
|
106
|
+
const resolvedOptions =
|
|
107
|
+
projection || options
|
|
108
|
+
? {
|
|
109
|
+
...(options ?? {}),
|
|
110
|
+
...(projection ? { projection } : {})
|
|
111
|
+
}
|
|
112
|
+
: undefined
|
|
113
|
+
const resolvedQuery = query ?? {}
|
|
104
114
|
if (!run_as_system) {
|
|
105
115
|
checkDenyOperation(normalizedRules, collection.collectionName, CRUD_OPERATIONS.READ)
|
|
106
116
|
// Apply access control filters to the query
|
|
107
|
-
const formattedQuery = getFormattedQuery(filters,
|
|
117
|
+
const formattedQuery = getFormattedQuery(filters, resolvedQuery, user)
|
|
108
118
|
logDebug('update formattedQuery', {
|
|
109
119
|
collection: collName,
|
|
110
120
|
query,
|
|
@@ -120,7 +130,7 @@ const getOperators: GetOperatorsFunction = (
|
|
|
120
130
|
logService('findOne query', { collName, formattedQuery })
|
|
121
131
|
const safeQuery = normalizeQuery(formattedQuery)
|
|
122
132
|
logService('findOne normalizedQuery', { collName, safeQuery })
|
|
123
|
-
const result = await collection.findOne({ $and: safeQuery })
|
|
133
|
+
const result = await collection.findOne({ $and: safeQuery }, resolvedOptions)
|
|
124
134
|
logDebug('findOne result', {
|
|
125
135
|
collection: collName,
|
|
126
136
|
result
|
|
@@ -151,12 +161,13 @@ const getOperators: GetOperatorsFunction = (
|
|
|
151
161
|
return Promise.resolve(status ? document : {})
|
|
152
162
|
}
|
|
153
163
|
// System mode: no validation applied
|
|
154
|
-
return collection.findOne(
|
|
164
|
+
return collection.findOne(resolvedQuery, resolvedOptions)
|
|
155
165
|
},
|
|
156
166
|
/**
|
|
157
167
|
* Deletes a single document from a MongoDB collection with optional role-based validation.
|
|
158
168
|
*
|
|
159
169
|
* @param {Filter<Document>} [query={}] - The MongoDB query used to match the document to delete.
|
|
170
|
+
* @param {DeleteOptions} [options] - Optional settings for the delete operation.
|
|
160
171
|
* @returns {Promise<DeleteResult>} A promise resolving to the result of the delete operation.
|
|
161
172
|
*
|
|
162
173
|
* @throws {Error} If the user is not authorized to delete the document.
|
|
@@ -170,7 +181,7 @@ const getOperators: GetOperatorsFunction = (
|
|
|
170
181
|
* - If validation fails, throws an error.
|
|
171
182
|
* - If validation passes, deletes the document using the filtered query.
|
|
172
183
|
*/
|
|
173
|
-
deleteOne: async (query = {}) => {
|
|
184
|
+
deleteOne: async (query = {}, options) => {
|
|
174
185
|
if (!run_as_system) {
|
|
175
186
|
checkDenyOperation(normalizedRules, collection.collectionName, CRUD_OPERATIONS.DELETE)
|
|
176
187
|
// Apply access control filters
|
|
@@ -202,10 +213,10 @@ const getOperators: GetOperatorsFunction = (
|
|
|
202
213
|
throw new Error('Delete not permitted')
|
|
203
214
|
}
|
|
204
215
|
|
|
205
|
-
return collection.deleteOne({ $and: formattedQuery })
|
|
216
|
+
return collection.deleteOne({ $and: formattedQuery }, options)
|
|
206
217
|
}
|
|
207
218
|
// System mode: bypass access control
|
|
208
|
-
return collection.deleteOne(query)
|
|
219
|
+
return collection.deleteOne(query, options)
|
|
209
220
|
},
|
|
210
221
|
/**
|
|
211
222
|
* Inserts a single document into a MongoDB collection with optional role-based validation.
|
|
@@ -438,6 +449,9 @@ const getOperators: GetOperatorsFunction = (
|
|
|
438
449
|
* Finds documents in a MongoDB collection with optional role-based access control and post-query validation.
|
|
439
450
|
*
|
|
440
451
|
* @param {Filter<Document>} query - The MongoDB query to filter documents.
|
|
452
|
+
* @param {Document} [projection] - Optional projection to select returned fields.
|
|
453
|
+
* @param {FindOptions} [options] - Optional settings for the find operation.
|
|
454
|
+
* @param {FindOptions} [options] - Optional settings for the find operation.
|
|
441
455
|
* @returns {FindCursor} A customized `FindCursor` that includes additional access control logic in its `toArray()` method.
|
|
442
456
|
*
|
|
443
457
|
* @description
|
|
@@ -451,14 +465,21 @@ const getOperators: GetOperatorsFunction = (
|
|
|
451
465
|
*
|
|
452
466
|
* This ensures that both pre-query filtering and post-query validation are applied consistently.
|
|
453
467
|
*/
|
|
454
|
-
find: (query) => {
|
|
468
|
+
find: (query = {}, projection, options) => {
|
|
469
|
+
const resolvedOptions =
|
|
470
|
+
projection || options
|
|
471
|
+
? {
|
|
472
|
+
...(options ?? {}),
|
|
473
|
+
...(projection ? { projection } : {})
|
|
474
|
+
}
|
|
475
|
+
: undefined
|
|
455
476
|
if (!run_as_system) {
|
|
456
477
|
checkDenyOperation(normalizedRules, collection.collectionName, CRUD_OPERATIONS.READ)
|
|
457
478
|
// Pre-query filtering based on access control rules
|
|
458
479
|
const formattedQuery = getFormattedQuery(filters, query, user)
|
|
459
480
|
const currentQuery = formattedQuery.length ? { $and: formattedQuery } : {}
|
|
460
481
|
// aggiunto filter per evitare questo errore: $and argument's entries must be objects
|
|
461
|
-
const cursor = collection.find(currentQuery)
|
|
482
|
+
const cursor = collection.find(currentQuery, resolvedOptions)
|
|
462
483
|
const originalToArray = cursor.toArray.bind(cursor)
|
|
463
484
|
|
|
464
485
|
/**
|
|
@@ -502,7 +523,7 @@ const getOperators: GetOperatorsFunction = (
|
|
|
502
523
|
return cursor
|
|
503
524
|
}
|
|
504
525
|
// System mode: return original unfiltered cursor
|
|
505
|
-
return collection.find(query)
|
|
526
|
+
return collection.find(query, resolvedOptions)
|
|
506
527
|
},
|
|
507
528
|
count: (query, options) => {
|
|
508
529
|
if (!run_as_system) {
|
|
@@ -793,6 +814,7 @@ const getOperators: GetOperatorsFunction = (
|
|
|
793
814
|
* Deletes multiple documents from a MongoDB collection with role-based access control and validation.
|
|
794
815
|
*
|
|
795
816
|
* @param query - The initial MongoDB query to filter documents to be deleted.
|
|
817
|
+
* @param {DeleteOptions} [options] - Optional settings for the delete operation.
|
|
796
818
|
* @returns {Promise<{ acknowledged: boolean, deletedCount: number }>} A promise resolving to the deletion result.
|
|
797
819
|
*
|
|
798
820
|
* @description
|
|
@@ -803,7 +825,7 @@ const getOperators: GetOperatorsFunction = (
|
|
|
803
825
|
* - Validates each document against user roles.
|
|
804
826
|
* - Deletes only the documents that the current user has permission to delete.
|
|
805
827
|
*/
|
|
806
|
-
deleteMany: async (query = {}) => {
|
|
828
|
+
deleteMany: async (query = {}, options) => {
|
|
807
829
|
if (!run_as_system) {
|
|
808
830
|
checkDenyOperation(normalizedRules, collection.collectionName, CRUD_OPERATIONS.DELETE)
|
|
809
831
|
// Apply access control filters
|
|
@@ -849,10 +871,10 @@ const getOperators: GetOperatorsFunction = (
|
|
|
849
871
|
const deleteQuery = {
|
|
850
872
|
$and: [...formattedQuery, { _id: { $in: elementsToDelete } }]
|
|
851
873
|
}
|
|
852
|
-
return collection.deleteMany(deleteQuery)
|
|
874
|
+
return collection.deleteMany(deleteQuery, options)
|
|
853
875
|
}
|
|
854
876
|
// If running as system, bypass access control and delete directly
|
|
855
|
-
return collection.deleteMany(query)
|
|
877
|
+
return collection.deleteMany(query, options)
|
|
856
878
|
}
|
|
857
879
|
}
|
|
858
880
|
}
|
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
Document,
|
|
5
5
|
FindCursor,
|
|
6
6
|
FindOneAndUpdateOptions,
|
|
7
|
+
FindOneOptions,
|
|
8
|
+
FindOptions,
|
|
7
9
|
Filter as MongoFilter,
|
|
8
10
|
UpdateFilter,
|
|
9
11
|
WithId
|
|
@@ -50,8 +52,12 @@ export type GetOperatorsFunction = (
|
|
|
50
52
|
collName: string
|
|
51
53
|
}
|
|
52
54
|
) => {
|
|
53
|
-
findOne: (
|
|
54
|
-
|
|
55
|
+
findOne: (
|
|
56
|
+
filter?: MongoFilter<Document>,
|
|
57
|
+
projection?: Document,
|
|
58
|
+
options?: FindOneOptions
|
|
59
|
+
) => ReturnType<Method<'findOne'>>
|
|
60
|
+
deleteOne: (...params: Parameters<Method<'deleteOne'>>) => ReturnType<Method<'deleteOne'>>
|
|
55
61
|
insertOne: (
|
|
56
62
|
...params: Parameters<Method<'insertOne'>>
|
|
57
63
|
) => ReturnType<Method<'insertOne'>>
|
|
@@ -63,7 +69,11 @@ export type GetOperatorsFunction = (
|
|
|
63
69
|
update: UpdateFilter<Document> | Document[],
|
|
64
70
|
options?: FindOneAndUpdateOptions
|
|
65
71
|
) => Promise<Document | null>
|
|
66
|
-
find: (
|
|
72
|
+
find: (
|
|
73
|
+
filter?: MongoFilter<Document>,
|
|
74
|
+
projection?: Document,
|
|
75
|
+
options?: FindOptions
|
|
76
|
+
) => FindCursor
|
|
67
77
|
count: (
|
|
68
78
|
...params: Parameters<Method<'countDocuments'>>
|
|
69
79
|
) => ReturnType<Method<'countDocuments'>>
|