Skip to content

Commit c529f07

Browse files
durranaddaleaxbaileympearson
authored
feat(NODE-6952): support configuring DEK cache expiration (#4538)
Co-authored-by: Anna Henningsen <[email protected]> Co-authored-by: Bailey Pearson <[email protected]>
1 parent 6fe6ccc commit c529f07

File tree

11 files changed

+657
-7
lines changed

11 files changed

+657
-7
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@
9696
"js-yaml": "^4.1.0",
9797
"mocha": "^10.8.2",
9898
"mocha-sinon": "^2.1.2",
99-
"mongodb-client-encryption": "^6.3.0",
99+
"mongodb-client-encryption": "^6.4.0",
100100
"mongodb-legacy": "^6.1.3",
101101
"nyc": "^15.1.0",
102102
"prettier": "^3.5.3",

src/client-side-encryption/auto_encrypter.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ export interface AutoEncryptionOptions {
5252
bypassAutoEncryption?: boolean;
5353
/** Allows users to bypass query analysis */
5454
bypassQueryAnalysis?: boolean;
55+
/**
56+
* Sets the expiration time for the DEK in the cache in milliseconds. Defaults to 60000. 0 means no timeout.
57+
*/
58+
keyExpirationMS?: number;
5559
options?: {
5660
/** An optional hook to catch logging messages from the underlying encryption engine */
5761
logger?: (level: AutoEncryptionLoggerLevel, message: string) => void;
@@ -285,6 +289,10 @@ export class AutoEncrypter {
285289
mongoCryptOptions.bypassQueryAnalysis = options.bypassQueryAnalysis;
286290
}
287291

292+
if (options.keyExpirationMS != null) {
293+
mongoCryptOptions.keyExpirationMS = options.keyExpirationMS;
294+
}
295+
288296
this._bypassMongocryptdAndCryptShared = this._bypassEncryption || !!options.bypassQueryAnalysis;
289297

290298
if (options.extraOptions && options.extraOptions.cryptSharedLibSearchPaths) {

src/client-side-encryption/client_encryption.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,11 @@ export interface ClientEncryptionOptions {
885885
*/
886886
tlsOptions?: CSFLEKMSTlsOptions;
887887

888+
/**
889+
* Sets the expiration time for the DEK in the cache in milliseconds. Defaults to 60000. 0 means no timeout.
890+
*/
891+
keyExpirationMS?: number;
892+
888893
/**
889894
* @experimental
890895
*

test/integration/client-side-encryption/client_side_encryption.spec.test.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ const skippedAuthTests = [
1515
'Insert a document with auto encryption using the AWS provider with temporary credentials',
1616
'Insert a document with auto encryption using Azure KMS provider',
1717
'$rename works if target value has same encryption options',
18-
'Insert with deterministic encryption, then find it',
19-
'Insert with randomized encryption, then find it',
2018
'Bulk write with encryption',
2119
'Insert with bypassAutoEncryption',
2220
'Insert with bypassAutoEncryption for local schema',
@@ -109,6 +107,13 @@ describe('Client Side Encryption (Legacy)', function () {
109107
if (typeof result === 'string') return result;
110108
}
111109

110+
if (['Insert with deterministic encryption, then find it'].includes(description)) {
111+
const result = configuration.filters.ClientSideEncryptionFilter.filter({
112+
metadata: { requires: { clientSideEncryption: '>=6.4.0' } }
113+
});
114+
115+
if (typeof result === 'string') return result;
116+
}
112117
return true;
113118
});
114119
});
@@ -142,12 +147,19 @@ describe('Client Side Encryption (Unified)', function () {
142147
'rewrap from aws:name1 to aws:name2',
143148
'can explicitly encrypt with a named KMS provider'
144149
];
150+
const dekExpirationTests = ['decrypt, wait, and decrypt again'];
145151
if (delegatedKMIPTests.includes(description)) {
146152
const shouldSkip = configuration.filters.ClientSideEncryptionFilter.filter({
147153
metadata: { requires: { clientSideEncryption: '>=6.0.1' } }
148154
});
149155
if (typeof shouldSkip === 'string') return shouldSkip;
150156
}
157+
if (dekExpirationTests.includes(description)) {
158+
const shouldSkip = configuration.filters.ClientSideEncryptionFilter.filter({
159+
metadata: { requires: { clientSideEncryption: '>=6.4.0' } }
160+
});
161+
if (typeof shouldSkip === 'string') return shouldSkip;
162+
}
151163

152164
return isServerless ? 'Unified CSFLE tests to not run on serverless' : false;
153165
}
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
{
2+
"runOn": [
3+
{
4+
"minServerVersion": "4.1.10"
5+
}
6+
],
7+
"database_name": "default",
8+
"collection_name": "default",
9+
"data": [],
10+
"json_schema": {
11+
"properties": {
12+
"encrypted_w_altname": {
13+
"encrypt": {
14+
"keyId": "/altname",
15+
"bsonType": "string",
16+
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
17+
}
18+
},
19+
"encrypted_string": {
20+
"encrypt": {
21+
"keyId": [
22+
{
23+
"$binary": {
24+
"base64": "AAAAAAAAAAAAAAAAAAAAAA==",
25+
"subType": "04"
26+
}
27+
}
28+
],
29+
"bsonType": "string",
30+
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
31+
}
32+
},
33+
"random": {
34+
"encrypt": {
35+
"keyId": [
36+
{
37+
"$binary": {
38+
"base64": "AAAAAAAAAAAAAAAAAAAAAA==",
39+
"subType": "04"
40+
}
41+
}
42+
],
43+
"bsonType": "string",
44+
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random"
45+
}
46+
},
47+
"encrypted_string_equivalent": {
48+
"encrypt": {
49+
"keyId": [
50+
{
51+
"$binary": {
52+
"base64": "AAAAAAAAAAAAAAAAAAAAAA==",
53+
"subType": "04"
54+
}
55+
}
56+
],
57+
"bsonType": "string",
58+
"algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic"
59+
}
60+
}
61+
},
62+
"bsonType": "object"
63+
},
64+
"key_vault_data": [
65+
{
66+
"status": 1,
67+
"_id": {
68+
"$binary": {
69+
"base64": "AAAAAAAAAAAAAAAAAAAAAA==",
70+
"subType": "04"
71+
}
72+
},
73+
"masterKey": {
74+
"provider": "aws",
75+
"key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0",
76+
"region": "us-east-1"
77+
},
78+
"updateDate": {
79+
"$date": {
80+
"$numberLong": "1552949630483"
81+
}
82+
},
83+
"keyMaterial": {
84+
"$binary": {
85+
"base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO",
86+
"subType": "00"
87+
}
88+
},
89+
"creationDate": {
90+
"$date": {
91+
"$numberLong": "1552949630483"
92+
}
93+
},
94+
"keyAltNames": [
95+
"altname",
96+
"another_altname"
97+
]
98+
}
99+
],
100+
"tests": [
101+
{
102+
"description": "Insert with deterministic encryption, then find it",
103+
"clientOptions": {
104+
"autoEncryptOpts": {
105+
"kmsProviders": {
106+
"aws": {}
107+
},
108+
"keyExpirationMS": 1
109+
}
110+
},
111+
"operations": [
112+
{
113+
"name": "insertOne",
114+
"arguments": {
115+
"document": {
116+
"_id": 1,
117+
"encrypted_string": "string0"
118+
}
119+
}
120+
},
121+
{
122+
"name": "wait",
123+
"object": "testRunner",
124+
"arguments": {
125+
"ms": 50
126+
}
127+
},
128+
{
129+
"name": "find",
130+
"arguments": {
131+
"filter": {
132+
"_id": 1
133+
}
134+
},
135+
"result": [
136+
{
137+
"_id": 1,
138+
"encrypted_string": "string0"
139+
}
140+
]
141+
}
142+
],
143+
"expectations": [
144+
{
145+
"command_started_event": {
146+
"command": {
147+
"listCollections": 1,
148+
"filter": {
149+
"name": "default"
150+
}
151+
},
152+
"command_name": "listCollections"
153+
}
154+
},
155+
{
156+
"command_started_event": {
157+
"command": {
158+
"find": "datakeys",
159+
"filter": {
160+
"$or": [
161+
{
162+
"_id": {
163+
"$in": [
164+
{
165+
"$binary": {
166+
"base64": "AAAAAAAAAAAAAAAAAAAAAA==",
167+
"subType": "04"
168+
}
169+
}
170+
]
171+
}
172+
},
173+
{
174+
"keyAltNames": {
175+
"$in": []
176+
}
177+
}
178+
]
179+
},
180+
"$db": "keyvault",
181+
"readConcern": {
182+
"level": "majority"
183+
}
184+
},
185+
"command_name": "find"
186+
}
187+
},
188+
{
189+
"command_started_event": {
190+
"command": {
191+
"insert": "default",
192+
"documents": [
193+
{
194+
"_id": 1,
195+
"encrypted_string": {
196+
"$binary": {
197+
"base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==",
198+
"subType": "06"
199+
}
200+
}
201+
}
202+
],
203+
"ordered": true
204+
},
205+
"command_name": "insert"
206+
}
207+
},
208+
{
209+
"command_started_event": {
210+
"command": {
211+
"find": "default",
212+
"filter": {
213+
"_id": 1
214+
}
215+
},
216+
"command_name": "find"
217+
}
218+
},
219+
{
220+
"command_started_event": {
221+
"command": {
222+
"find": "datakeys",
223+
"filter": {
224+
"$or": [
225+
{
226+
"_id": {
227+
"$in": [
228+
{
229+
"$binary": {
230+
"base64": "AAAAAAAAAAAAAAAAAAAAAA==",
231+
"subType": "04"
232+
}
233+
}
234+
]
235+
}
236+
},
237+
{
238+
"keyAltNames": {
239+
"$in": []
240+
}
241+
}
242+
]
243+
},
244+
"$db": "keyvault",
245+
"readConcern": {
246+
"level": "majority"
247+
}
248+
},
249+
"command_name": "find"
250+
}
251+
}
252+
],
253+
"outcome": {
254+
"collection": {
255+
"data": [
256+
{
257+
"_id": 1,
258+
"encrypted_string": {
259+
"$binary": {
260+
"base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==",
261+
"subType": "06"
262+
}
263+
}
264+
}
265+
]
266+
}
267+
}
268+
}
269+
]
270+
}

0 commit comments

Comments
 (0)