@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
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { UUID } from 'mongodb'
|
|
2
|
+
import type { EncryptionSchemas } from '../../features/encryption/interface'
|
|
3
|
+
import { buildSchemaMap } from '../initializer/mongodbCSFLE'
|
|
4
|
+
|
|
5
|
+
describe('buildSchemaMap', () => {
|
|
6
|
+
const genericSchemas: EncryptionSchemas = {
|
|
7
|
+
'appDb.records': {
|
|
8
|
+
bsonType: 'object',
|
|
9
|
+
encryptMetadata: {
|
|
10
|
+
keyAlias: 'root-key'
|
|
11
|
+
},
|
|
12
|
+
properties: {
|
|
13
|
+
publicText: {
|
|
14
|
+
encrypt: {
|
|
15
|
+
bsonType: 'string',
|
|
16
|
+
algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Random'
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
protectedText: {
|
|
20
|
+
encrypt: {
|
|
21
|
+
bsonType: 'string',
|
|
22
|
+
algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic',
|
|
23
|
+
keyAlias: 'root-key'
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
nestedObject: {
|
|
27
|
+
bsonType: 'object',
|
|
28
|
+
encryptMetadata: { keyAlias: 'nested-key' },
|
|
29
|
+
properties: {
|
|
30
|
+
deepObject: {
|
|
31
|
+
bsonType: 'object',
|
|
32
|
+
properties: {
|
|
33
|
+
deepSecret: {
|
|
34
|
+
encrypt: {
|
|
35
|
+
bsonType: 'string',
|
|
36
|
+
algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Random',
|
|
37
|
+
keyAlias: 'deep-key'
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
it('resolves keyAlias to keyId for root and nested schemas', () => {
|
|
49
|
+
const rootKeyId = new UUID()
|
|
50
|
+
const nestedKeyId = new UUID()
|
|
51
|
+
const deepKeyId = new UUID()
|
|
52
|
+
|
|
53
|
+
const schemaMap = buildSchemaMap(genericSchemas, [
|
|
54
|
+
{ dataKeyAlias: 'root-key', dataKeyId: rootKeyId },
|
|
55
|
+
{ dataKeyAlias: 'nested-key', dataKeyId: nestedKeyId },
|
|
56
|
+
{ dataKeyAlias: 'deep-key', dataKeyId: deepKeyId }
|
|
57
|
+
])
|
|
58
|
+
|
|
59
|
+
expect(schemaMap['appDb.records']).toEqual({
|
|
60
|
+
bsonType: 'object',
|
|
61
|
+
encryptMetadata: { keyId: [rootKeyId] },
|
|
62
|
+
properties: {
|
|
63
|
+
publicText: {
|
|
64
|
+
encrypt: {
|
|
65
|
+
bsonType: 'string',
|
|
66
|
+
algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Random'
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
protectedText: {
|
|
70
|
+
encrypt: {
|
|
71
|
+
bsonType: 'string',
|
|
72
|
+
algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic',
|
|
73
|
+
keyId: [rootKeyId]
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
nestedObject: {
|
|
77
|
+
bsonType: 'object',
|
|
78
|
+
encryptMetadata: { keyId: [nestedKeyId] },
|
|
79
|
+
properties: {
|
|
80
|
+
deepObject: {
|
|
81
|
+
bsonType: 'object',
|
|
82
|
+
properties: {
|
|
83
|
+
deepSecret: {
|
|
84
|
+
encrypt: {
|
|
85
|
+
bsonType: 'string',
|
|
86
|
+
algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Random',
|
|
87
|
+
keyId: [deepKeyId]
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('throws when nested keyAlias cannot be resolved', () => {
|
|
99
|
+
const rootKeyId = new UUID()
|
|
100
|
+
|
|
101
|
+
expect(() =>
|
|
102
|
+
buildSchemaMap(genericSchemas, [{ dataKeyAlias: 'root-key', dataKeyId: rootKeyId }])
|
|
103
|
+
).toThrow('Key with alias deep-key could not be found in the Key Vault.')
|
|
104
|
+
})
|
|
105
|
+
})
|
package/src/utils/index.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
|
-
import fs from 'fs'
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
2
3
|
|
|
3
4
|
export const readFileContent = (filePath: string) => fs.readFileSync(filePath, 'utf-8')
|
|
4
5
|
export const readJsonContent = (filePath: string) =>
|
|
5
6
|
JSON.parse(readFileContent(filePath)) as unknown
|
|
7
|
+
|
|
8
|
+
export const recursivelyCollectFiles = (dir: string): string[] => {
|
|
9
|
+
return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
|
10
|
+
const fullPath = path.join(dir, entry.name)
|
|
11
|
+
if (entry.isDirectory()) {
|
|
12
|
+
return recursivelyCollectFiles(fullPath)
|
|
13
|
+
}
|
|
14
|
+
return entry.isFile() ? [fullPath] : []
|
|
15
|
+
})
|
|
16
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ClientEncryption,
|
|
3
|
+
MongoClient,
|
|
4
|
+
UUID,
|
|
5
|
+
Binary,
|
|
6
|
+
type Document,
|
|
7
|
+
type AWSEncryptionKeyOptions,
|
|
8
|
+
type AWSKMSProviderConfiguration,
|
|
9
|
+
type AzureEncryptionKeyOptions,
|
|
10
|
+
type AzureKMSProviderConfiguration,
|
|
11
|
+
type GCPKMSProviderConfiguration,
|
|
12
|
+
type GCPEncryptionKeyOptions,
|
|
13
|
+
type KMIPKMSProviderConfiguration,
|
|
14
|
+
type KMIPEncryptionKeyOptions,
|
|
15
|
+
type LocalKMSProviderConfiguration,
|
|
16
|
+
type AutoEncryptionExtraOptions,
|
|
17
|
+
type KMSProviders,
|
|
18
|
+
type AutoEncryptionOptions,
|
|
19
|
+
} from "mongodb";
|
|
20
|
+
import { EncryptionSchemaProperty, MappedEncryptionSchema, MappedEncryptionSchemaProperty, type EncryptionSchemas } from "../../features/encryption/interface";
|
|
21
|
+
import { DEFAULT_CONFIG } from "../../constants";
|
|
22
|
+
|
|
23
|
+
type KMSProviderConfig =
|
|
24
|
+
| {
|
|
25
|
+
/**
|
|
26
|
+
* The alias of the key. It must be referenced in the schema map
|
|
27
|
+
* to select which key to use for encryption.
|
|
28
|
+
*/
|
|
29
|
+
keyAlias: string
|
|
30
|
+
/**
|
|
31
|
+
* KMS Provider name.
|
|
32
|
+
*/
|
|
33
|
+
provider: "aws"
|
|
34
|
+
/**
|
|
35
|
+
* KMS Provider specific authorization configuration.
|
|
36
|
+
*/
|
|
37
|
+
config: AWSKMSProviderConfiguration
|
|
38
|
+
/**
|
|
39
|
+
* Configuration of the master key.
|
|
40
|
+
*/
|
|
41
|
+
masterKey: AWSEncryptionKeyOptions
|
|
42
|
+
} | {
|
|
43
|
+
keyAlias: string
|
|
44
|
+
provider: "azure"
|
|
45
|
+
config: AzureKMSProviderConfiguration,
|
|
46
|
+
masterKey: AzureEncryptionKeyOptions
|
|
47
|
+
} | {
|
|
48
|
+
keyAlias: string
|
|
49
|
+
provider: "gcp"
|
|
50
|
+
config: GCPKMSProviderConfiguration,
|
|
51
|
+
masterKey: GCPEncryptionKeyOptions
|
|
52
|
+
} | {
|
|
53
|
+
keyAlias: string
|
|
54
|
+
provider: "kmip"
|
|
55
|
+
config: KMIPKMSProviderConfiguration,
|
|
56
|
+
masterKey: KMIPEncryptionKeyOptions
|
|
57
|
+
} | {
|
|
58
|
+
keyAlias: string
|
|
59
|
+
provider: "local"
|
|
60
|
+
config: LocalKMSProviderConfiguration,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type MongoDbEncryptionConfig = {
|
|
64
|
+
kmsProviders: KMSProviderConfig[],
|
|
65
|
+
/**
|
|
66
|
+
* The Key Vault database name
|
|
67
|
+
* @default encryption
|
|
68
|
+
*/
|
|
69
|
+
keyVaultDb?: string
|
|
70
|
+
/**
|
|
71
|
+
* The Key Vault database collection
|
|
72
|
+
* @default __keyVault
|
|
73
|
+
*/
|
|
74
|
+
keyVaultCollection?: string
|
|
75
|
+
extraOptions?: AutoEncryptionExtraOptions
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @internal
|
|
80
|
+
*/
|
|
81
|
+
type RequiredConfig = Required<Omit<MongoDbEncryptionConfig, "extraOptions">>
|
|
82
|
+
|
|
83
|
+
type DataKey = { dataKeyId: UUID, dataKeyAlias: string }
|
|
84
|
+
|
|
85
|
+
async function ensureUniqueKeyAltNameIndex(db: ReturnType<MongoClient['db']>, config: RequiredConfig): Promise<void> {
|
|
86
|
+
await db.collection(config.keyVaultCollection).createIndex(
|
|
87
|
+
{ keyAltNames: 1 },
|
|
88
|
+
{
|
|
89
|
+
unique: true,
|
|
90
|
+
partialFilterExpression: { keyAltNames: { $exists: true } },
|
|
91
|
+
}
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Ensure provided KMS Providers DEK keys exist in the key vault. If not, they are created.
|
|
97
|
+
*/
|
|
98
|
+
async function ensureDataEncryptionKeys(
|
|
99
|
+
clientEncryption: ClientEncryption,
|
|
100
|
+
keyVaultDb: ReturnType<MongoClient['db']>,
|
|
101
|
+
config: RequiredConfig
|
|
102
|
+
): Promise<DataKey[]> {
|
|
103
|
+
const keys: DataKey[] = []
|
|
104
|
+
|
|
105
|
+
for (const kmsProvider of config.kmsProviders) {
|
|
106
|
+
const existingKey = await keyVaultDb.collection(config.keyVaultCollection).findOne({
|
|
107
|
+
keyAltNames: kmsProvider.keyAlias,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
if (existingKey?._id instanceof Binary) {
|
|
111
|
+
keys.push({ dataKeyId: existingKey._id, dataKeyAlias: kmsProvider.keyAlias })
|
|
112
|
+
continue
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const dataKeyId = await clientEncryption.createDataKey(kmsProvider.provider, {
|
|
116
|
+
masterKey: "masterKey" in kmsProvider ? kmsProvider.masterKey : undefined,
|
|
117
|
+
keyAltNames: [kmsProvider.keyAlias],
|
|
118
|
+
});
|
|
119
|
+
console.log(`[MongoDB Encryption] Created new key with alias ${kmsProvider.keyAlias}`)
|
|
120
|
+
keys.push({ dataKeyId, dataKeyAlias: kmsProvider.keyAlias })
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return keys
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Recursively resolve key aliases in an encryption schema to their corresponding key IDs.
|
|
128
|
+
*/
|
|
129
|
+
const resolveKeyAliases = (schema: EncryptionSchemaProperty, dataKeys: DataKey[]): MappedEncryptionSchemaProperty => {
|
|
130
|
+
if ("encrypt" in schema) {
|
|
131
|
+
if (!schema.encrypt.keyAlias) {
|
|
132
|
+
return schema
|
|
133
|
+
}
|
|
134
|
+
const keyId = dataKeys.find(k => k.dataKeyAlias === schema.encrypt.keyAlias)?.dataKeyId
|
|
135
|
+
if (!keyId) {
|
|
136
|
+
throw new Error(`Key with alias ${schema.encrypt.keyAlias} could not be found in the Key Vault.`)
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
encrypt: {
|
|
140
|
+
bsonType: schema.encrypt.bsonType,
|
|
141
|
+
algorithm: schema.encrypt.algorithm,
|
|
142
|
+
keyId: [keyId]
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
const mappedSchema: MappedEncryptionSchema = {
|
|
147
|
+
bsonType: "object",
|
|
148
|
+
properties: Object.entries(schema.properties).reduce((acc, [property, config]) => {
|
|
149
|
+
acc[property] = resolveKeyAliases(config, dataKeys)
|
|
150
|
+
return acc
|
|
151
|
+
}, {} as Record<string, MappedEncryptionSchemaProperty>)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (schema.encryptMetadata) {
|
|
155
|
+
const keyId = dataKeys.find(k => k.dataKeyAlias === schema.encryptMetadata!.keyAlias)?.dataKeyId
|
|
156
|
+
if (!keyId) {
|
|
157
|
+
throw new Error(`Key with alias ${schema.encryptMetadata.keyAlias} could not be found in the Key Vault.`)
|
|
158
|
+
}
|
|
159
|
+
mappedSchema.encryptMetadata = { keyId: [keyId] }
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return mappedSchema
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export const buildSchemaMap = (schemas: EncryptionSchemas, dataKeys: DataKey[]) => {
|
|
166
|
+
return Object.entries(schemas).reduce((acc, [key, schema]) => {
|
|
167
|
+
acc[key] = resolveKeyAliases(schema, dataKeys)
|
|
168
|
+
return acc
|
|
169
|
+
}, {} as Record<string, Document>)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Setup MongoDB Client-Side Field Level Encryption (CSFLE).
|
|
174
|
+
* @see https://www.mongodb.com/docs/manual/core/csfle
|
|
175
|
+
*/
|
|
176
|
+
export const setupMongoDbCSFLE = async (
|
|
177
|
+
config: MongoDbEncryptionConfig & { mongodbUrl: string; schemas?: EncryptionSchemas }
|
|
178
|
+
): Promise<AutoEncryptionOptions> => {
|
|
179
|
+
if (config.kmsProviders.length === 0) {
|
|
180
|
+
throw new Error('At least one KMS Provider is required when using MongoDB encryption')
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const requiredConfig: RequiredConfig = {
|
|
184
|
+
kmsProviders: config.kmsProviders,
|
|
185
|
+
keyVaultDb: config.keyVaultDb ?? DEFAULT_CONFIG.MONGODB_ENCRYPTION_CONFIG.keyVaultDb,
|
|
186
|
+
keyVaultCollection: config.keyVaultDb ?? DEFAULT_CONFIG.MONGODB_ENCRYPTION_CONFIG.keyVaultCollection,
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const kmsProviders = requiredConfig.kmsProviders.reduce(
|
|
190
|
+
(acc, { provider, config }) => ({ ...acc, [provider]: config }),
|
|
191
|
+
{} as KMSProviders
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
const keyVaultNamespace = `${requiredConfig.keyVaultDb}.${requiredConfig.keyVaultCollection}`
|
|
195
|
+
const keyVaultClient = new MongoClient(config.mongodbUrl, {
|
|
196
|
+
maxPoolSize: 1,
|
|
197
|
+
autoEncryption: {
|
|
198
|
+
keyVaultNamespace,
|
|
199
|
+
kmsProviders,
|
|
200
|
+
extraOptions: config.extraOptions
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
await keyVaultClient.connect();
|
|
205
|
+
|
|
206
|
+
const keyVaultDb = keyVaultClient.db(requiredConfig.keyVaultDb);
|
|
207
|
+
await ensureUniqueKeyAltNameIndex(keyVaultDb, requiredConfig)
|
|
208
|
+
|
|
209
|
+
const clientEncryption = new ClientEncryption(keyVaultClient, {
|
|
210
|
+
keyVaultNamespace,
|
|
211
|
+
kmsProviders,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const dataKeys = await ensureDataEncryptionKeys(clientEncryption, keyVaultDb, requiredConfig)
|
|
215
|
+
|
|
216
|
+
await keyVaultClient.close()
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
keyVaultNamespace,
|
|
220
|
+
kmsProviders,
|
|
221
|
+
schemaMap: config.schemas ? buildSchemaMap(config.schemas, dataKeys) : undefined,
|
|
222
|
+
extraOptions: config.extraOptions
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import cors from '@fastify/cors'
|
|
2
|
-
import fastifyMongodb from '@fastify/mongodb'
|
|
2
|
+
import fastifyMongodb, { FastifyMongodbOptions } from '@fastify/mongodb'
|
|
3
3
|
import { FastifyInstance } from 'fastify'
|
|
4
4
|
import fastifyRawBody from 'fastify-raw-body'
|
|
5
5
|
import { CorsConfig } from '../../'
|
|
@@ -10,7 +10,9 @@ import { customFunctionController } from '../../auth/providers/custom-function/c
|
|
|
10
10
|
import { localUserPassController } from '../../auth/providers/local-userpass/controller'
|
|
11
11
|
import { API_VERSION, DEFAULT_CONFIG } from '../../constants'
|
|
12
12
|
import { Functions } from '../../features/functions/interface'
|
|
13
|
+
import { EncryptionSchemas } from '../../features/encryption/interface'
|
|
13
14
|
import monitoringPlugin from '../../monitoring/plugin'
|
|
15
|
+
import { setupMongoDbCSFLE, MongoDbEncryptionConfig } from './mongodbCSFLE'
|
|
14
16
|
|
|
15
17
|
type RegisterFunction = FastifyInstance['register']
|
|
16
18
|
type RegisterParameters = Parameters<RegisterFunction>
|
|
@@ -21,6 +23,8 @@ type RegisterPluginsParams = {
|
|
|
21
23
|
jwtSecret: string
|
|
22
24
|
functionsList: Functions
|
|
23
25
|
corsConfig?: CorsConfig
|
|
26
|
+
encryptionSchemas?: EncryptionSchemas
|
|
27
|
+
mongodbEncryptionConfig?: MongoDbEncryptionConfig
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
type RegisterConfig = {
|
|
@@ -41,14 +45,18 @@ export const registerPlugins = async ({
|
|
|
41
45
|
mongodbUrl,
|
|
42
46
|
jwtSecret,
|
|
43
47
|
functionsList,
|
|
44
|
-
corsConfig
|
|
48
|
+
corsConfig,
|
|
49
|
+
mongodbEncryptionConfig,
|
|
50
|
+
encryptionSchemas
|
|
45
51
|
}: RegisterPluginsParams) => {
|
|
46
52
|
try {
|
|
47
53
|
const registersConfig = await getRegisterConfig({
|
|
48
54
|
mongodbUrl,
|
|
49
55
|
jwtSecret,
|
|
50
56
|
corsConfig,
|
|
51
|
-
functionsList
|
|
57
|
+
functionsList,
|
|
58
|
+
mongodbEncryptionConfig,
|
|
59
|
+
encryptionSchemas
|
|
52
60
|
})
|
|
53
61
|
|
|
54
62
|
registersConfig.forEach(({ plugin, options, pluginName }) => {
|
|
@@ -75,15 +83,23 @@ export const registerPlugins = async ({
|
|
|
75
83
|
const getRegisterConfig = async ({
|
|
76
84
|
mongodbUrl,
|
|
77
85
|
jwtSecret,
|
|
78
|
-
corsConfig
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
> => {
|
|
86
|
+
corsConfig,
|
|
87
|
+
encryptionSchemas,
|
|
88
|
+
mongodbEncryptionConfig,
|
|
89
|
+
}: Omit<RegisterPluginsParams, "register">): Promise<RegisterConfig[]> => {
|
|
82
90
|
const corsOptions = corsConfig ?? {
|
|
83
91
|
origin: '*',
|
|
84
92
|
methods: ['POST', 'GET']
|
|
85
93
|
}
|
|
86
94
|
|
|
95
|
+
const autoEncryption = mongodbEncryptionConfig
|
|
96
|
+
? await setupMongoDbCSFLE({
|
|
97
|
+
mongodbUrl,
|
|
98
|
+
schemas: encryptionSchemas,
|
|
99
|
+
...mongodbEncryptionConfig
|
|
100
|
+
})
|
|
101
|
+
: undefined
|
|
102
|
+
|
|
87
103
|
const baseConfig = [
|
|
88
104
|
{
|
|
89
105
|
pluginName: 'cors',
|
|
@@ -94,9 +110,28 @@ const getRegisterConfig = async ({
|
|
|
94
110
|
pluginName: 'fastifyMongodb',
|
|
95
111
|
plugin: fastifyMongodb,
|
|
96
112
|
options: {
|
|
113
|
+
url: mongodbUrl,
|
|
97
114
|
forceClose: true,
|
|
98
|
-
|
|
99
|
-
}
|
|
115
|
+
autoEncryption
|
|
116
|
+
} satisfies FastifyMongodbOptions
|
|
117
|
+
},
|
|
118
|
+
/**
|
|
119
|
+
* When auto-encryption is active, add another MongoDB client with bypass for change streams.
|
|
120
|
+
* The $changeStream operator does not support automatic encryption, only decryption.
|
|
121
|
+
* @see https://www.mongodb.com/docs/manual/core/csfle/reference/supported-operations
|
|
122
|
+
*/
|
|
123
|
+
autoEncryption && {
|
|
124
|
+
pluginName: 'fastifyMongodb',
|
|
125
|
+
plugin: fastifyMongodb,
|
|
126
|
+
options: {
|
|
127
|
+
name: "changestream",
|
|
128
|
+
url: mongodbUrl,
|
|
129
|
+
forceClose: true,
|
|
130
|
+
autoEncryption: {
|
|
131
|
+
...autoEncryption,
|
|
132
|
+
bypassAutoEncryption: true
|
|
133
|
+
}
|
|
134
|
+
} satisfies FastifyMongodbOptions
|
|
100
135
|
},
|
|
101
136
|
{
|
|
102
137
|
pluginName: 'jwtAuthPlugin',
|
|
@@ -153,5 +188,5 @@ const getRegisterConfig = async ({
|
|
|
153
188
|
} as RegisterConfig)
|
|
154
189
|
}
|
|
155
190
|
|
|
156
|
-
return baseConfig
|
|
191
|
+
return baseConfig.filter(Boolean)
|
|
157
192
|
}
|