@flowerforce/flowerbase 1.7.6-beta.0 → 1.7.6-beta.2
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/README.md +125 -1
- package/dist/auth/providers/custom-function/controller.d.ts.map +1 -1
- package/dist/auth/providers/custom-function/controller.js +3 -8
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +11 -1
- package/dist/features/encryption/interface.d.ts +36 -0
- package/dist/features/encryption/interface.d.ts.map +1 -0
- package/dist/features/encryption/interface.js +2 -0
- package/dist/features/encryption/utils.d.ts +9 -0
- package/dist/features/encryption/utils.d.ts.map +1 -0
- package/dist/features/encryption/utils.js +34 -0
- package/dist/features/functions/controller.d.ts +2 -0
- package/dist/features/functions/controller.d.ts.map +1 -1
- package/dist/features/functions/controller.js +7 -1
- package/dist/features/rules/utils.d.ts.map +1 -1
- package/dist/features/rules/utils.js +1 -11
- package/dist/features/triggers/index.d.ts.map +1 -1
- package/dist/features/triggers/index.js +4 -0
- package/dist/features/triggers/utils.d.ts.map +1 -1
- package/dist/features/triggers/utils.js +30 -38
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/dist/monitoring/plugin.d.ts.map +1 -1
- package/dist/monitoring/plugin.js +31 -0
- package/dist/services/mongodb-atlas/index.d.ts +3 -0
- package/dist/services/mongodb-atlas/index.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/index.js +97 -17
- package/dist/services/mongodb-atlas/model.d.ts +2 -1
- package/dist/services/mongodb-atlas/model.d.ts.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +14 -3
- package/dist/utils/initializer/mongodbCSFLE.d.ts +69 -0
- package/dist/utils/initializer/mongodbCSFLE.d.ts.map +1 -0
- package/dist/utils/initializer/mongodbCSFLE.js +131 -0
- package/dist/utils/initializer/registerPlugins.d.ts +5 -1
- package/dist/utils/initializer/registerPlugins.d.ts.map +1 -1
- package/dist/utils/initializer/registerPlugins.js +27 -5
- package/package.json +4 -2
- package/src/auth/providers/custom-function/controller.ts +4 -10
- package/src/constants.ts +11 -2
- package/src/features/encryption/interface.ts +46 -0
- package/src/features/encryption/utils.ts +22 -0
- package/src/features/functions/__tests__/watch-filter.test.ts +11 -1
- package/src/features/functions/controller.ts +8 -0
- package/src/features/rules/utils.ts +1 -11
- package/src/features/triggers/index.ts +5 -1
- package/src/features/triggers/utils.ts +31 -42
- package/src/index.ts +10 -2
- package/src/monitoring/plugin.ts +33 -0
- package/src/monitoring/ui.collections.js +7 -10
- package/src/monitoring/ui.css +378 -0
- package/src/monitoring/ui.endpoints.js +5 -10
- package/src/monitoring/ui.events.js +2 -4
- package/src/monitoring/ui.functions.js +64 -71
- package/src/monitoring/ui.html +8 -0
- package/src/monitoring/ui.js +189 -0
- package/src/monitoring/ui.shared.js +237 -2
- package/src/monitoring/ui.triggers.js +2 -3
- package/src/monitoring/ui.users.js +5 -9
- package/src/services/mongodb-atlas/__tests__/watch-filter.test.ts +78 -0
- package/src/services/mongodb-atlas/index.ts +102 -19
- package/src/services/mongodb-atlas/model.ts +3 -1
- package/src/types/fastify-raw-body.d.ts +0 -9
- package/src/utils/__tests__/mongodbCSFLE.test.ts +105 -0
- package/src/utils/index.ts +12 -1
- package/src/utils/initializer/mongodbCSFLE.ts +224 -0
- package/src/utils/initializer/registerPlugins.ts +45 -10
|
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
15
|
const websocket_1 = __importDefault(require("@fastify/websocket"));
|
|
16
16
|
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
17
17
|
require("@fastify/websocket");
|
|
18
|
+
const fs_1 = __importDefault(require("fs"));
|
|
18
19
|
const constants_1 = require("../constants");
|
|
19
20
|
const state_1 = require("../state");
|
|
20
21
|
const collections_1 = require("./routes/collections");
|
|
@@ -289,6 +290,36 @@ const createMonitoringPlugin = (0, fastify_plugin_1.default)((app_1, ...args_1)
|
|
|
289
290
|
reply.type('application/javascript').send(js);
|
|
290
291
|
}));
|
|
291
292
|
});
|
|
293
|
+
const resolveCodeMirrorAsset = (internalPath) => {
|
|
294
|
+
try {
|
|
295
|
+
return require.resolve(`codemirror/${internalPath}`);
|
|
296
|
+
}
|
|
297
|
+
catch (_a) {
|
|
298
|
+
return '';
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
const codemirrorAssets = {
|
|
302
|
+
'codemirror.js': resolveCodeMirrorAsset('lib/codemirror.js'),
|
|
303
|
+
'codemirror.css': resolveCodeMirrorAsset('lib/codemirror.css'),
|
|
304
|
+
'javascript.js': resolveCodeMirrorAsset('mode/javascript/javascript.js'),
|
|
305
|
+
'foldcode.js': resolveCodeMirrorAsset('addon/fold/foldcode.js'),
|
|
306
|
+
'foldgutter.js': resolveCodeMirrorAsset('addon/fold/foldgutter.js'),
|
|
307
|
+
'brace-fold.js': resolveCodeMirrorAsset('addon/fold/brace-fold.js'),
|
|
308
|
+
'comment-fold.js': resolveCodeMirrorAsset('addon/fold/comment-fold.js'),
|
|
309
|
+
'foldgutter.css': resolveCodeMirrorAsset('addon/fold/foldgutter.css')
|
|
310
|
+
};
|
|
311
|
+
Object.entries(codemirrorAssets).forEach(([assetName, relativePath]) => {
|
|
312
|
+
app.get(`${prefix}/vendor/codemirror/${assetName}`, (_req, reply) => __awaiter(void 0, void 0, void 0, function* () {
|
|
313
|
+
const assetPath = relativePath || '';
|
|
314
|
+
if (!assetPath || !fs_1.default.existsSync(assetPath)) {
|
|
315
|
+
reply.code(404).send(`${assetName} not found`);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const asset = fs_1.default.readFileSync(assetPath, 'utf8');
|
|
319
|
+
reply.header('Cache-Control', 'no-store');
|
|
320
|
+
reply.type(assetName.endsWith('.css') ? 'text/css' : 'application/javascript').send(asset);
|
|
321
|
+
}));
|
|
322
|
+
});
|
|
292
323
|
app.get(`${prefix}/ws`, { websocket: true }, (connection) => {
|
|
293
324
|
var _a;
|
|
294
325
|
const socket = (_a = connection.socket) !== null && _a !== void 0 ? _a : connection;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { Document } from 'mongodb';
|
|
1
2
|
import { MongodbAtlasFunction } from './model';
|
|
3
|
+
export declare const toWatchMatchFilter: (value: unknown) => unknown;
|
|
4
|
+
export declare const watchPipelineRequestsDelete: (pipeline: Document[]) => boolean;
|
|
2
5
|
declare const MongodbAtlas: MongodbAtlasFunction;
|
|
3
6
|
export default MongodbAtlas;
|
|
4
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -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":"AAKA,OAAO,EAIL,QAAQ,EAQT,MAAM,SAAS,CAAA;AAOhB,OAAO,EAGL,oBAAoB,EAErB,MAAM,SAAS,CAAA;AAgJhB,eAAO,MAAM,kBAAkB,GAAI,OAAO,OAAO,KAAG,OA0BnD,CAAA;AA4BD,eAAO,MAAM,2BAA2B,GAAI,UAAU,QAAQ,EAAE,YAK5D,CAAA;AAgtCJ,QAAA,MAAM,YAAY,EAAE,oBAwBlB,CAAA;AAEF,eAAe,YAAY,CAAA"}
|
|
@@ -23,6 +23,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
23
23
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.watchPipelineRequestsDelete = exports.toWatchMatchFilter = void 0;
|
|
26
27
|
const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
|
|
27
28
|
const get_1 = __importDefault(require("lodash/get"));
|
|
28
29
|
const isEqual_1 = __importDefault(require("lodash/isEqual"));
|
|
@@ -32,6 +33,7 @@ const utils_1 = require("../../monitoring/utils");
|
|
|
32
33
|
const machines_1 = require("../../utils/roles/machines");
|
|
33
34
|
const utils_2 = require("../../utils/roles/machines/utils");
|
|
34
35
|
const monitoring_1 = require("../monitoring");
|
|
36
|
+
const constants_1 = require("../../constants");
|
|
35
37
|
const model_1 = require("./model");
|
|
36
38
|
const utils_3 = require("./utils");
|
|
37
39
|
//TODO aggiungere no-sql inject security
|
|
@@ -71,6 +73,12 @@ const findOptionKeys = new Set([
|
|
|
71
73
|
'projection'
|
|
72
74
|
]);
|
|
73
75
|
const isPlainObject = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
76
|
+
const isTraversablePlainObject = (value) => {
|
|
77
|
+
if (!isPlainObject(value))
|
|
78
|
+
return false;
|
|
79
|
+
const prototype = Object.getPrototypeOf(value);
|
|
80
|
+
return prototype === Object.prototype || prototype === null;
|
|
81
|
+
};
|
|
74
82
|
const looksLikeFindOptions = (value) => {
|
|
75
83
|
if (!isPlainObject(value))
|
|
76
84
|
return false;
|
|
@@ -113,21 +121,81 @@ const normalizeFindOneAndUpdateOptions = (options) => {
|
|
|
113
121
|
return Object.assign(Object.assign({}, rest), { returnDocument: returnNewDocument ? 'after' : 'before' });
|
|
114
122
|
};
|
|
115
123
|
const buildAndQuery = (clauses) => clauses.length ? { $and: clauses } : {};
|
|
124
|
+
const watchChangeEventRootKeys = new Set([
|
|
125
|
+
'_id',
|
|
126
|
+
'operationType',
|
|
127
|
+
'clusterTime',
|
|
128
|
+
'txnNumber',
|
|
129
|
+
'lsid',
|
|
130
|
+
'ns',
|
|
131
|
+
'documentKey',
|
|
132
|
+
'fullDocument',
|
|
133
|
+
'updateDescription'
|
|
134
|
+
]);
|
|
135
|
+
const isWatchChangeEventPath = (key) => {
|
|
136
|
+
if (watchChangeEventRootKeys.has(key))
|
|
137
|
+
return true;
|
|
138
|
+
return (key.startsWith('ns.') ||
|
|
139
|
+
key.startsWith('documentKey.') ||
|
|
140
|
+
key.startsWith('fullDocument.') ||
|
|
141
|
+
key.startsWith('updateDescription.'));
|
|
142
|
+
};
|
|
143
|
+
const isWatchOpaqueChangeEventObjectKey = (key) => key === 'ns' || key === 'documentKey' || key === 'fullDocument' || key === 'updateDescription';
|
|
116
144
|
const toWatchMatchFilter = (value) => {
|
|
117
145
|
if (Array.isArray(value)) {
|
|
118
|
-
return value.map((item) => toWatchMatchFilter(item));
|
|
146
|
+
return value.map((item) => (0, exports.toWatchMatchFilter)(item));
|
|
119
147
|
}
|
|
120
|
-
if (!
|
|
148
|
+
if (!isTraversablePlainObject(value))
|
|
121
149
|
return value;
|
|
122
150
|
return Object.entries(value).reduce((acc, [key, current]) => {
|
|
123
151
|
if (key.startsWith('$')) {
|
|
124
|
-
acc[key] = toWatchMatchFilter(current);
|
|
152
|
+
acc[key] = (0, exports.toWatchMatchFilter)(current);
|
|
125
153
|
return acc;
|
|
126
154
|
}
|
|
127
|
-
|
|
155
|
+
if (isWatchOpaqueChangeEventObjectKey(key)) {
|
|
156
|
+
acc[key] = current;
|
|
157
|
+
return acc;
|
|
158
|
+
}
|
|
159
|
+
if (isWatchChangeEventPath(key)) {
|
|
160
|
+
acc[key] = (0, exports.toWatchMatchFilter)(current);
|
|
161
|
+
return acc;
|
|
162
|
+
}
|
|
163
|
+
acc[`fullDocument.${key}`] = (0, exports.toWatchMatchFilter)(current);
|
|
128
164
|
return acc;
|
|
129
165
|
}, {});
|
|
130
166
|
};
|
|
167
|
+
exports.toWatchMatchFilter = toWatchMatchFilter;
|
|
168
|
+
const isDeleteOperationValue = (value) => {
|
|
169
|
+
if (typeof value === 'string')
|
|
170
|
+
return value.toLowerCase() === 'delete';
|
|
171
|
+
if (isPlainObject(value) && Array.isArray(value.$in)) {
|
|
172
|
+
return value.$in.some((entry) => isDeleteOperationValue(entry));
|
|
173
|
+
}
|
|
174
|
+
if (Array.isArray(value)) {
|
|
175
|
+
return value.some((entry) => isDeleteOperationValue(entry));
|
|
176
|
+
}
|
|
177
|
+
return false;
|
|
178
|
+
};
|
|
179
|
+
const hasDeleteOperationType = (value) => {
|
|
180
|
+
if (Array.isArray(value)) {
|
|
181
|
+
return value.some((entry) => hasDeleteOperationType(entry));
|
|
182
|
+
}
|
|
183
|
+
if (!isTraversablePlainObject(value))
|
|
184
|
+
return false;
|
|
185
|
+
return Object.entries(value).some(([key, current]) => {
|
|
186
|
+
if (key === 'operationType') {
|
|
187
|
+
return isDeleteOperationValue(current);
|
|
188
|
+
}
|
|
189
|
+
return hasDeleteOperationType(current);
|
|
190
|
+
});
|
|
191
|
+
};
|
|
192
|
+
const watchPipelineRequestsDelete = (pipeline) => pipeline.some((stage) => {
|
|
193
|
+
if (!isTraversablePlainObject(stage))
|
|
194
|
+
return false;
|
|
195
|
+
const match = stage.$match;
|
|
196
|
+
return hasDeleteOperationType(match);
|
|
197
|
+
});
|
|
198
|
+
exports.watchPipelineRequestsDelete = watchPipelineRequestsDelete;
|
|
131
199
|
const resolveWatchArgs = (pipelineOrOptions, options) => {
|
|
132
200
|
var _a;
|
|
133
201
|
const inputPipeline = Array.isArray(pipelineOrOptions) ? pipelineOrOptions : [];
|
|
@@ -142,7 +210,7 @@ const resolveWatchArgs = (pipelineOrOptions, options) => {
|
|
|
142
210
|
const _b = rawOptions, { filter: watchFilter, ids } = _b, watchOptions = __rest(_b, ["filter", "ids"]);
|
|
143
211
|
const extraMatches = [];
|
|
144
212
|
if (typeof watchFilter !== 'undefined') {
|
|
145
|
-
extraMatches.push({ $match: toWatchMatchFilter(watchFilter) });
|
|
213
|
+
extraMatches.push({ $match: (0, exports.toWatchMatchFilter)(watchFilter) });
|
|
146
214
|
}
|
|
147
215
|
if (Array.isArray(ids)) {
|
|
148
216
|
extraMatches.push({
|
|
@@ -333,8 +401,9 @@ const areUpdatedFieldsAllowed = (filtered, updated, updatedPaths) => {
|
|
|
333
401
|
return (0, isEqual_1.default)(filtered, updated);
|
|
334
402
|
return updatedPaths.every((path) => (0, isEqual_1.default)((0, get_1.default)(filtered, path), (0, get_1.default)(updated, path)));
|
|
335
403
|
};
|
|
336
|
-
const getOperators = (
|
|
404
|
+
const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, monitoringOrigin }) => {
|
|
337
405
|
var _a, _b;
|
|
406
|
+
const collection = mongo.client.db(dbName).collection(collName);
|
|
338
407
|
const normalizedRules = rules !== null && rules !== void 0 ? rules : {};
|
|
339
408
|
const collectionRules = normalizedRules[collName];
|
|
340
409
|
const filters = (_a = collectionRules === null || collectionRules === void 0 ? void 0 : collectionRules.filters) !== null && _a !== void 0 ? _a : [];
|
|
@@ -852,22 +921,34 @@ const getOperators = (collection, { rules, collName, user, run_as_system, monito
|
|
|
852
921
|
* This allows fine-grained control over what change events a user can observe, based on roles and filters.
|
|
853
922
|
*/
|
|
854
923
|
watch: (pipelineOrOptions = [], options) => {
|
|
924
|
+
const changestreamCollection = mongo[constants_1.CHANGESTREAM].client.db(dbName).collection(collName);
|
|
855
925
|
try {
|
|
856
926
|
const { pipeline, options: watchOptions, extraMatches } = resolveWatchArgs(pipelineOrOptions, options);
|
|
857
927
|
if (!run_as_system) {
|
|
858
928
|
(0, utils_3.checkDenyOperation)(normalizedRules, collection.collectionName, model_1.CRUD_OPERATIONS.READ);
|
|
859
929
|
// Apply access filters to initial change stream pipeline
|
|
860
930
|
const formattedQuery = (0, utils_3.getFormattedQuery)(filters, {}, user);
|
|
861
|
-
const watchFormattedQuery = formattedQuery.map((condition) => toWatchMatchFilter(condition));
|
|
931
|
+
const watchFormattedQuery = formattedQuery.map((condition) => (0, exports.toWatchMatchFilter)(condition));
|
|
932
|
+
const requestedPipeline = [...extraMatches, ...pipeline];
|
|
933
|
+
const allowDeleteBypass = (0, exports.watchPipelineRequestsDelete)(requestedPipeline);
|
|
862
934
|
const firstStep = watchFormattedQuery.length
|
|
863
935
|
? {
|
|
864
|
-
$match:
|
|
865
|
-
|
|
866
|
-
|
|
936
|
+
$match: allowDeleteBypass
|
|
937
|
+
? {
|
|
938
|
+
$or: [
|
|
939
|
+
{
|
|
940
|
+
$and: watchFormattedQuery
|
|
941
|
+
},
|
|
942
|
+
{ operationType: 'delete' }
|
|
943
|
+
]
|
|
944
|
+
}
|
|
945
|
+
: {
|
|
946
|
+
$and: watchFormattedQuery
|
|
947
|
+
}
|
|
867
948
|
}
|
|
868
949
|
: undefined;
|
|
869
|
-
const formattedPipeline = [firstStep, ...
|
|
870
|
-
const result =
|
|
950
|
+
const formattedPipeline = [firstStep, ...requestedPipeline].filter(Boolean);
|
|
951
|
+
const result = changestreamCollection.watch(formattedPipeline, watchOptions);
|
|
871
952
|
const originalOn = result.on.bind(result);
|
|
872
953
|
/**
|
|
873
954
|
* Validates a change event against the user's roles.
|
|
@@ -917,7 +998,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system, monito
|
|
|
917
998
|
return result;
|
|
918
999
|
}
|
|
919
1000
|
// System mode: no filtering applied
|
|
920
|
-
const result =
|
|
1001
|
+
const result = changestreamCollection.watch([...extraMatches, ...pipeline], watchOptions);
|
|
921
1002
|
emitMongoEvent('watch');
|
|
922
1003
|
return result;
|
|
923
1004
|
}
|
|
@@ -1138,11 +1219,10 @@ const MongodbAtlas = (app, { rules, user, run_as_system, monitoring } = {}) => (
|
|
|
1138
1219
|
db: (dbName) => {
|
|
1139
1220
|
return {
|
|
1140
1221
|
collection: (collName) => {
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
return getOperators(collection, {
|
|
1144
|
-
rules,
|
|
1222
|
+
return getOperators(app.mongo, {
|
|
1223
|
+
dbName,
|
|
1145
1224
|
collName,
|
|
1225
|
+
rules,
|
|
1146
1226
|
user,
|
|
1147
1227
|
run_as_system,
|
|
1148
1228
|
monitoringOrigin: monitoring === null || monitoring === void 0 ? void 0 : monitoring.invokedFrom
|
|
@@ -22,10 +22,11 @@ export type GetValidRuleParams<T extends Role | Filter> = {
|
|
|
22
22
|
record?: WithId<Document> | Document | null;
|
|
23
23
|
};
|
|
24
24
|
type Method<T extends keyof Collection<Document>> = Collection<Document>[T];
|
|
25
|
-
export type GetOperatorsFunction = (
|
|
25
|
+
export type GetOperatorsFunction = (mongoInstance: FastifyInstance["mongo"], { rules, dbName, collName, user, run_as_system, monitoringOrigin }: {
|
|
26
26
|
user?: User;
|
|
27
27
|
rules?: Rules;
|
|
28
28
|
run_as_system?: boolean;
|
|
29
|
+
dbName: string;
|
|
29
30
|
collName: string;
|
|
30
31
|
monitoringOrigin?: string;
|
|
31
32
|
}) => {
|
|
@@ -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,aAAa,EACb,oBAAoB,EACpB,UAAU,EACV,QAAQ,EACR,UAAU,EACV,uBAAuB,EACvB,cAAc,EACd,WAAW,EACX,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,EACb,UAAU,EACX,EAAE;IACD,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,KAAK,CAAC,EAAE,KAAK,CAAA;IACb,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,UAAU,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CACtC,KACE;IACH,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK;QACtB,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,UAAU,CAAC,oBAAoB,CAAC,CAAA;KACnE,CAAA;IACD,YAAY,EAAE,CAAC,OAAO,CAAC,EAAE,oBAAoB,KAAK,aAAa,CAAA;CAChE,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,
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/services/mongodb-atlas/model.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,WAAuC,CAAA;AACvF,eAAO,MAAM,eAAe,GAAI,UAAU,MAAM,KACL,OAAO,CAAA;AAElD,eAAO,MAAM,uBAAuB,GAAI,KAAK,MAAM,KAAG,MAAM,EAQ3D,CAAA"}
|
package/dist/utils/index.js
CHANGED
|
@@ -3,9 +3,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.readJsonContent = exports.readFileContent = void 0;
|
|
7
|
-
const
|
|
8
|
-
const
|
|
6
|
+
exports.recursivelyCollectFiles = exports.readJsonContent = exports.readFileContent = void 0;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const readFileContent = (filePath) => node_fs_1.default.readFileSync(filePath, 'utf-8');
|
|
9
10
|
exports.readFileContent = readFileContent;
|
|
10
11
|
const readJsonContent = (filePath) => JSON.parse((0, exports.readFileContent)(filePath));
|
|
11
12
|
exports.readJsonContent = readJsonContent;
|
|
13
|
+
const recursivelyCollectFiles = (dir) => {
|
|
14
|
+
return node_fs_1.default.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
15
|
+
const fullPath = node_path_1.default.join(dir, entry.name);
|
|
16
|
+
if (entry.isDirectory()) {
|
|
17
|
+
return (0, exports.recursivelyCollectFiles)(fullPath);
|
|
18
|
+
}
|
|
19
|
+
return entry.isFile() ? [fullPath] : [];
|
|
20
|
+
});
|
|
21
|
+
};
|
|
22
|
+
exports.recursivelyCollectFiles = recursivelyCollectFiles;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { UUID, type Document, type AWSEncryptionKeyOptions, type AWSKMSProviderConfiguration, type AzureEncryptionKeyOptions, type AzureKMSProviderConfiguration, type GCPKMSProviderConfiguration, type GCPEncryptionKeyOptions, type KMIPKMSProviderConfiguration, type KMIPEncryptionKeyOptions, type LocalKMSProviderConfiguration, type AutoEncryptionExtraOptions, type AutoEncryptionOptions } from "mongodb";
|
|
2
|
+
import { type EncryptionSchemas } from "../../features/encryption/interface";
|
|
3
|
+
type KMSProviderConfig = {
|
|
4
|
+
/**
|
|
5
|
+
* The alias of the key. It must be referenced in the schema map
|
|
6
|
+
* to select which key to use for encryption.
|
|
7
|
+
*/
|
|
8
|
+
keyAlias: string;
|
|
9
|
+
/**
|
|
10
|
+
* KMS Provider name.
|
|
11
|
+
*/
|
|
12
|
+
provider: "aws";
|
|
13
|
+
/**
|
|
14
|
+
* KMS Provider specific authorization configuration.
|
|
15
|
+
*/
|
|
16
|
+
config: AWSKMSProviderConfiguration;
|
|
17
|
+
/**
|
|
18
|
+
* Configuration of the master key.
|
|
19
|
+
*/
|
|
20
|
+
masterKey: AWSEncryptionKeyOptions;
|
|
21
|
+
} | {
|
|
22
|
+
keyAlias: string;
|
|
23
|
+
provider: "azure";
|
|
24
|
+
config: AzureKMSProviderConfiguration;
|
|
25
|
+
masterKey: AzureEncryptionKeyOptions;
|
|
26
|
+
} | {
|
|
27
|
+
keyAlias: string;
|
|
28
|
+
provider: "gcp";
|
|
29
|
+
config: GCPKMSProviderConfiguration;
|
|
30
|
+
masterKey: GCPEncryptionKeyOptions;
|
|
31
|
+
} | {
|
|
32
|
+
keyAlias: string;
|
|
33
|
+
provider: "kmip";
|
|
34
|
+
config: KMIPKMSProviderConfiguration;
|
|
35
|
+
masterKey: KMIPEncryptionKeyOptions;
|
|
36
|
+
} | {
|
|
37
|
+
keyAlias: string;
|
|
38
|
+
provider: "local";
|
|
39
|
+
config: LocalKMSProviderConfiguration;
|
|
40
|
+
};
|
|
41
|
+
export type MongoDbEncryptionConfig = {
|
|
42
|
+
kmsProviders: KMSProviderConfig[];
|
|
43
|
+
/**
|
|
44
|
+
* The Key Vault database name
|
|
45
|
+
* @default encryption
|
|
46
|
+
*/
|
|
47
|
+
keyVaultDb?: string;
|
|
48
|
+
/**
|
|
49
|
+
* The Key Vault database collection
|
|
50
|
+
* @default __keyVault
|
|
51
|
+
*/
|
|
52
|
+
keyVaultCollection?: string;
|
|
53
|
+
extraOptions?: AutoEncryptionExtraOptions;
|
|
54
|
+
};
|
|
55
|
+
type DataKey = {
|
|
56
|
+
dataKeyId: UUID;
|
|
57
|
+
dataKeyAlias: string;
|
|
58
|
+
};
|
|
59
|
+
export declare const buildSchemaMap: (schemas: EncryptionSchemas, dataKeys: DataKey[]) => Record<string, Document>;
|
|
60
|
+
/**
|
|
61
|
+
* Setup MongoDB Client-Side Field Level Encryption (CSFLE).
|
|
62
|
+
* @see https://www.mongodb.com/docs/manual/core/csfle
|
|
63
|
+
*/
|
|
64
|
+
export declare const setupMongoDbCSFLE: (config: MongoDbEncryptionConfig & {
|
|
65
|
+
mongodbUrl: string;
|
|
66
|
+
schemas?: EncryptionSchemas;
|
|
67
|
+
}) => Promise<AutoEncryptionOptions>;
|
|
68
|
+
export {};
|
|
69
|
+
//# sourceMappingURL=mongodbCSFLE.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mongodbCSFLE.d.ts","sourceRoot":"","sources":["../../../src/utils/initializer/mongodbCSFLE.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,IAAI,EAEJ,KAAK,QAAQ,EACb,KAAK,uBAAuB,EAC5B,KAAK,2BAA2B,EAChC,KAAK,yBAAyB,EAC9B,KAAK,6BAA6B,EAClC,KAAK,2BAA2B,EAChC,KAAK,uBAAuB,EAC5B,KAAK,4BAA4B,EACjC,KAAK,wBAAwB,EAC7B,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAE/B,KAAK,qBAAqB,EAC3B,MAAM,SAAS,CAAC;AACjB,OAAO,EAAoF,KAAK,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAG/J,KAAK,iBAAiB,GAClB;IACA;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAA;IACf;;OAEG;IACH,MAAM,EAAE,2BAA2B,CAAA;IACnC;;OAEG;IACH,SAAS,EAAE,uBAAuB,CAAA;CACnC,GAAG;IACF,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,6BAA6B,CAAC;IACtC,SAAS,EAAE,yBAAyB,CAAA;CACrC,GAAG;IACF,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,KAAK,CAAA;IACf,MAAM,EAAE,2BAA2B,CAAC;IACpC,SAAS,EAAE,uBAAuB,CAAA;CACnC,GAAG;IACF,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,4BAA4B,CAAC;IACrC,SAAS,EAAE,wBAAwB,CAAA;CACpC,GAAG;IACF,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,EAAE,6BAA6B,CAAC;CACvC,CAAA;AAEH,MAAM,MAAM,uBAAuB,GAAG;IACpC,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAClC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;KAGC;IACD,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,YAAY,CAAC,EAAE,0BAA0B,CAAA;CAC1C,CAAA;AAOD,KAAK,OAAO,GAAG;IAAE,SAAS,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAA;AAkFxD,eAAO,MAAM,cAAc,GAAI,SAAS,iBAAiB,EAAE,UAAU,OAAO,EAAE,6BAK7E,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,uBAAuB,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,iBAAiB,CAAA;CAAE,KACpF,OAAO,CAAC,qBAAqB,CA8C/B,CAAA"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.setupMongoDbCSFLE = exports.buildSchemaMap = void 0;
|
|
13
|
+
const mongodb_1 = require("mongodb");
|
|
14
|
+
const constants_1 = require("../../constants");
|
|
15
|
+
function ensureUniqueKeyAltNameIndex(db, config) {
|
|
16
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
yield db.collection(config.keyVaultCollection).createIndex({ keyAltNames: 1 }, {
|
|
18
|
+
unique: true,
|
|
19
|
+
partialFilterExpression: { keyAltNames: { $exists: true } },
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Ensure provided KMS Providers DEK keys exist in the key vault. If not, they are created.
|
|
25
|
+
*/
|
|
26
|
+
function ensureDataEncryptionKeys(clientEncryption, keyVaultDb, config) {
|
|
27
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
28
|
+
const keys = [];
|
|
29
|
+
for (const kmsProvider of config.kmsProviders) {
|
|
30
|
+
const existingKey = yield keyVaultDb.collection(config.keyVaultCollection).findOne({
|
|
31
|
+
keyAltNames: kmsProvider.keyAlias,
|
|
32
|
+
});
|
|
33
|
+
if ((existingKey === null || existingKey === void 0 ? void 0 : existingKey._id) instanceof mongodb_1.Binary) {
|
|
34
|
+
keys.push({ dataKeyId: existingKey._id, dataKeyAlias: kmsProvider.keyAlias });
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const dataKeyId = yield clientEncryption.createDataKey(kmsProvider.provider, {
|
|
38
|
+
masterKey: "masterKey" in kmsProvider ? kmsProvider.masterKey : undefined,
|
|
39
|
+
keyAltNames: [kmsProvider.keyAlias],
|
|
40
|
+
});
|
|
41
|
+
console.log(`[MongoDB Encryption] Created new key with alias ${kmsProvider.keyAlias}`);
|
|
42
|
+
keys.push({ dataKeyId, dataKeyAlias: kmsProvider.keyAlias });
|
|
43
|
+
}
|
|
44
|
+
return keys;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Recursively resolve key aliases in an encryption schema to their corresponding key IDs.
|
|
49
|
+
*/
|
|
50
|
+
const resolveKeyAliases = (schema, dataKeys) => {
|
|
51
|
+
var _a, _b;
|
|
52
|
+
if ("encrypt" in schema) {
|
|
53
|
+
if (!schema.encrypt.keyAlias) {
|
|
54
|
+
return schema;
|
|
55
|
+
}
|
|
56
|
+
const keyId = (_a = dataKeys.find(k => k.dataKeyAlias === schema.encrypt.keyAlias)) === null || _a === void 0 ? void 0 : _a.dataKeyId;
|
|
57
|
+
if (!keyId) {
|
|
58
|
+
throw new Error(`Key with alias ${schema.encrypt.keyAlias} could not be found in the Key Vault.`);
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
encrypt: {
|
|
62
|
+
bsonType: schema.encrypt.bsonType,
|
|
63
|
+
algorithm: schema.encrypt.algorithm,
|
|
64
|
+
keyId: [keyId]
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const mappedSchema = {
|
|
69
|
+
bsonType: "object",
|
|
70
|
+
properties: Object.entries(schema.properties).reduce((acc, [property, config]) => {
|
|
71
|
+
acc[property] = resolveKeyAliases(config, dataKeys);
|
|
72
|
+
return acc;
|
|
73
|
+
}, {})
|
|
74
|
+
};
|
|
75
|
+
if (schema.encryptMetadata) {
|
|
76
|
+
const keyId = (_b = dataKeys.find(k => k.dataKeyAlias === schema.encryptMetadata.keyAlias)) === null || _b === void 0 ? void 0 : _b.dataKeyId;
|
|
77
|
+
if (!keyId) {
|
|
78
|
+
throw new Error(`Key with alias ${schema.encryptMetadata.keyAlias} could not be found in the Key Vault.`);
|
|
79
|
+
}
|
|
80
|
+
mappedSchema.encryptMetadata = { keyId: [keyId] };
|
|
81
|
+
}
|
|
82
|
+
return mappedSchema;
|
|
83
|
+
};
|
|
84
|
+
const buildSchemaMap = (schemas, dataKeys) => {
|
|
85
|
+
return Object.entries(schemas).reduce((acc, [key, schema]) => {
|
|
86
|
+
acc[key] = resolveKeyAliases(schema, dataKeys);
|
|
87
|
+
return acc;
|
|
88
|
+
}, {});
|
|
89
|
+
};
|
|
90
|
+
exports.buildSchemaMap = buildSchemaMap;
|
|
91
|
+
/**
|
|
92
|
+
* Setup MongoDB Client-Side Field Level Encryption (CSFLE).
|
|
93
|
+
* @see https://www.mongodb.com/docs/manual/core/csfle
|
|
94
|
+
*/
|
|
95
|
+
const setupMongoDbCSFLE = (config) => __awaiter(void 0, void 0, void 0, function* () {
|
|
96
|
+
var _a, _b;
|
|
97
|
+
if (config.kmsProviders.length === 0) {
|
|
98
|
+
throw new Error('At least one KMS Provider is required when using MongoDB encryption');
|
|
99
|
+
}
|
|
100
|
+
const requiredConfig = {
|
|
101
|
+
kmsProviders: config.kmsProviders,
|
|
102
|
+
keyVaultDb: (_a = config.keyVaultDb) !== null && _a !== void 0 ? _a : constants_1.DEFAULT_CONFIG.MONGODB_ENCRYPTION_CONFIG.keyVaultDb,
|
|
103
|
+
keyVaultCollection: (_b = config.keyVaultDb) !== null && _b !== void 0 ? _b : constants_1.DEFAULT_CONFIG.MONGODB_ENCRYPTION_CONFIG.keyVaultCollection,
|
|
104
|
+
};
|
|
105
|
+
const kmsProviders = requiredConfig.kmsProviders.reduce((acc, { provider, config }) => (Object.assign(Object.assign({}, acc), { [provider]: config })), {});
|
|
106
|
+
const keyVaultNamespace = `${requiredConfig.keyVaultDb}.${requiredConfig.keyVaultCollection}`;
|
|
107
|
+
const keyVaultClient = new mongodb_1.MongoClient(config.mongodbUrl, {
|
|
108
|
+
maxPoolSize: 1,
|
|
109
|
+
autoEncryption: {
|
|
110
|
+
keyVaultNamespace,
|
|
111
|
+
kmsProviders,
|
|
112
|
+
extraOptions: config.extraOptions
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
yield keyVaultClient.connect();
|
|
116
|
+
const keyVaultDb = keyVaultClient.db(requiredConfig.keyVaultDb);
|
|
117
|
+
yield ensureUniqueKeyAltNameIndex(keyVaultDb, requiredConfig);
|
|
118
|
+
const clientEncryption = new mongodb_1.ClientEncryption(keyVaultClient, {
|
|
119
|
+
keyVaultNamespace,
|
|
120
|
+
kmsProviders,
|
|
121
|
+
});
|
|
122
|
+
const dataKeys = yield ensureDataEncryptionKeys(clientEncryption, keyVaultDb, requiredConfig);
|
|
123
|
+
yield keyVaultClient.close();
|
|
124
|
+
return {
|
|
125
|
+
keyVaultNamespace,
|
|
126
|
+
kmsProviders,
|
|
127
|
+
schemaMap: config.schemas ? (0, exports.buildSchemaMap)(config.schemas, dataKeys) : undefined,
|
|
128
|
+
extraOptions: config.extraOptions
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
exports.setupMongoDbCSFLE = setupMongoDbCSFLE;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { FastifyInstance } from 'fastify';
|
|
2
2
|
import { CorsConfig } from '../../';
|
|
3
3
|
import { Functions } from '../../features/functions/interface';
|
|
4
|
+
import { EncryptionSchemas } from '../../features/encryption/interface';
|
|
5
|
+
import { MongoDbEncryptionConfig } from './mongodbCSFLE';
|
|
4
6
|
type RegisterFunction = FastifyInstance['register'];
|
|
5
7
|
type RegisterPluginsParams = {
|
|
6
8
|
register: RegisterFunction;
|
|
@@ -8,6 +10,8 @@ type RegisterPluginsParams = {
|
|
|
8
10
|
jwtSecret: string;
|
|
9
11
|
functionsList: Functions;
|
|
10
12
|
corsConfig?: CorsConfig;
|
|
13
|
+
encryptionSchemas?: EncryptionSchemas;
|
|
14
|
+
mongodbEncryptionConfig?: MongoDbEncryptionConfig;
|
|
11
15
|
};
|
|
12
16
|
/**
|
|
13
17
|
* > Used to register all plugins
|
|
@@ -16,6 +20,6 @@ type RegisterPluginsParams = {
|
|
|
16
20
|
* @param jwtSecret -> connection jwt
|
|
17
21
|
* @tested
|
|
18
22
|
*/
|
|
19
|
-
export declare const registerPlugins: ({ register, mongodbUrl, jwtSecret, functionsList, corsConfig }: RegisterPluginsParams) => Promise<void>;
|
|
23
|
+
export declare const registerPlugins: ({ register, mongodbUrl, jwtSecret, functionsList, corsConfig, mongodbEncryptionConfig, encryptionSchemas }: RegisterPluginsParams) => Promise<void>;
|
|
20
24
|
export {};
|
|
21
25
|
//# sourceMappingURL=registerPlugins.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registerPlugins.d.ts","sourceRoot":"","sources":["../../../src/utils/initializer/registerPlugins.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAOnC,OAAO,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAA;
|
|
1
|
+
{"version":3,"file":"registerPlugins.d.ts","sourceRoot":"","sources":["../../../src/utils/initializer/registerPlugins.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAOnC,OAAO,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAA;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAA;AAEvE,OAAO,EAAqB,uBAAuB,EAAE,MAAM,gBAAgB,CAAA;AAE3E,KAAK,gBAAgB,GAAG,eAAe,CAAC,UAAU,CAAC,CAAA;AAGnD,KAAK,qBAAqB,GAAG;IAC3B,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,SAAS,CAAA;IACxB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,uBAAuB,CAAC,EAAE,uBAAuB,CAAA;CAClD,CAAA;AAQD;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAU,4GAQnC,qBAAqB,kBAwBvB,CAAA"}
|
|
@@ -23,6 +23,7 @@ const controller_3 = require("../../auth/providers/custom-function/controller");
|
|
|
23
23
|
const controller_4 = require("../../auth/providers/local-userpass/controller");
|
|
24
24
|
const constants_1 = require("../../constants");
|
|
25
25
|
const plugin_1 = __importDefault(require("../../monitoring/plugin"));
|
|
26
|
+
const mongodbCSFLE_1 = require("./mongodbCSFLE");
|
|
26
27
|
/**
|
|
27
28
|
* > Used to register all plugins
|
|
28
29
|
* @param register -> the fastify register method
|
|
@@ -30,13 +31,15 @@ const plugin_1 = __importDefault(require("../../monitoring/plugin"));
|
|
|
30
31
|
* @param jwtSecret -> connection jwt
|
|
31
32
|
* @tested
|
|
32
33
|
*/
|
|
33
|
-
const registerPlugins = (_a) => __awaiter(void 0, [_a], void 0, function* ({ register, mongodbUrl, jwtSecret, functionsList, corsConfig }) {
|
|
34
|
+
const registerPlugins = (_a) => __awaiter(void 0, [_a], void 0, function* ({ register, mongodbUrl, jwtSecret, functionsList, corsConfig, mongodbEncryptionConfig, encryptionSchemas }) {
|
|
34
35
|
try {
|
|
35
36
|
const registersConfig = yield getRegisterConfig({
|
|
36
37
|
mongodbUrl,
|
|
37
38
|
jwtSecret,
|
|
38
39
|
corsConfig,
|
|
39
|
-
functionsList
|
|
40
|
+
functionsList,
|
|
41
|
+
mongodbEncryptionConfig,
|
|
42
|
+
encryptionSchemas
|
|
40
43
|
});
|
|
41
44
|
registersConfig.forEach(({ plugin, options, pluginName }) => {
|
|
42
45
|
try {
|
|
@@ -61,11 +64,14 @@ exports.registerPlugins = registerPlugins;
|
|
|
61
64
|
* @param jwtSecret -> connection jwt
|
|
62
65
|
* @testable
|
|
63
66
|
*/
|
|
64
|
-
const getRegisterConfig = (_a) => __awaiter(void 0, [_a], void 0, function* ({ mongodbUrl, jwtSecret, corsConfig }) {
|
|
67
|
+
const getRegisterConfig = (_a) => __awaiter(void 0, [_a], void 0, function* ({ mongodbUrl, jwtSecret, corsConfig, encryptionSchemas, mongodbEncryptionConfig, }) {
|
|
65
68
|
const corsOptions = corsConfig !== null && corsConfig !== void 0 ? corsConfig : {
|
|
66
69
|
origin: '*',
|
|
67
70
|
methods: ['POST', 'GET']
|
|
68
71
|
};
|
|
72
|
+
const autoEncryption = mongodbEncryptionConfig
|
|
73
|
+
? yield (0, mongodbCSFLE_1.setupMongoDbCSFLE)(Object.assign({ mongodbUrl, schemas: encryptionSchemas }, mongodbEncryptionConfig))
|
|
74
|
+
: undefined;
|
|
69
75
|
const baseConfig = [
|
|
70
76
|
{
|
|
71
77
|
pluginName: 'cors',
|
|
@@ -76,8 +82,24 @@ const getRegisterConfig = (_a) => __awaiter(void 0, [_a], void 0, function* ({ m
|
|
|
76
82
|
pluginName: 'fastifyMongodb',
|
|
77
83
|
plugin: mongodb_1.default,
|
|
78
84
|
options: {
|
|
85
|
+
url: mongodbUrl,
|
|
79
86
|
forceClose: true,
|
|
80
|
-
|
|
87
|
+
autoEncryption
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
/**
|
|
91
|
+
* When auto-encryption is active, add another MongoDB client with bypass for change streams.
|
|
92
|
+
* The $changeStream operator does not support automatic encryption, only decryption.
|
|
93
|
+
* @see https://www.mongodb.com/docs/manual/core/csfle/reference/supported-operations
|
|
94
|
+
*/
|
|
95
|
+
autoEncryption && {
|
|
96
|
+
pluginName: 'fastifyMongodb',
|
|
97
|
+
plugin: mongodb_1.default,
|
|
98
|
+
options: {
|
|
99
|
+
name: "changestream",
|
|
100
|
+
url: mongodbUrl,
|
|
101
|
+
forceClose: true,
|
|
102
|
+
autoEncryption: Object.assign(Object.assign({}, autoEncryption), { bypassAutoEncryption: true })
|
|
81
103
|
}
|
|
82
104
|
},
|
|
83
105
|
{
|
|
@@ -133,5 +155,5 @@ const getRegisterConfig = (_a) => __awaiter(void 0, [_a], void 0, function* ({ m
|
|
|
133
155
|
options: { basePath: '/monit' }
|
|
134
156
|
});
|
|
135
157
|
}
|
|
136
|
-
return baseConfig;
|
|
158
|
+
return baseConfig.filter(Boolean);
|
|
137
159
|
});
|