@bedrock/kms 10.0.0 → 10.3.0
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/CHANGELOG.md +17 -0
- package/lib/BedrockKmsModuleManager.js +8 -1
- package/lib/keystores.js +21 -6
- package/package.json +1 -1
- package/test/mocha/13-keystores-update-api.js +40 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# bedrock-kms ChangeLog
|
|
2
2
|
|
|
3
|
+
## 10.3.0 - 2022-06-19
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Cache KMS module APIs as imported.
|
|
7
|
+
|
|
8
|
+
## 10.2.0 - 2022-05-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Expose `_disableClearCacheOnUpdate` for testing cache busting only; do not use in
|
|
12
|
+
production.
|
|
13
|
+
|
|
14
|
+
## 10.1.0 - 2022-05-13
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- Add `fresh` option to `keystores.get()` API to allow for retrieving a fresh
|
|
18
|
+
(not previously cached) keystore config record.
|
|
19
|
+
|
|
3
20
|
## 10.0.0 - 2022-04-29
|
|
4
21
|
|
|
5
22
|
### Changed
|
|
@@ -6,11 +6,18 @@ import * as brPackageManager from '@bedrock/package-manager';
|
|
|
6
6
|
// load config defaults
|
|
7
7
|
import './config.js';
|
|
8
8
|
|
|
9
|
+
const importMap = new Map();
|
|
10
|
+
|
|
9
11
|
export class BedrockKmsModuleManager {
|
|
10
12
|
async get({id}) {
|
|
11
13
|
const {packageName} = brPackageManager.get(
|
|
12
14
|
{alias: id, type: 'webkms-module'});
|
|
13
|
-
|
|
15
|
+
let api = await importMap.get(packageName);
|
|
16
|
+
if(!api) {
|
|
17
|
+
const promise = import(packageName);
|
|
18
|
+
importMap.set(packageName, promise);
|
|
19
|
+
api = await promise;
|
|
20
|
+
}
|
|
14
21
|
return api.default || api;
|
|
15
22
|
}
|
|
16
23
|
}
|
package/lib/keystores.js
CHANGED
|
@@ -16,6 +16,10 @@ import './config.js';
|
|
|
16
16
|
|
|
17
17
|
const USAGE_COUNTER_MAX_CONCURRENCY = 100;
|
|
18
18
|
let KEYSTORE_CONFIG_CACHE;
|
|
19
|
+
let DISABLE_CLEAR_CACHE_ON_UPDATE = false;
|
|
20
|
+
|
|
21
|
+
// cache only exported for testing purposes
|
|
22
|
+
export {KEYSTORE_CONFIG_CACHE as _KEYSTORE_CONFIG_CACHE};
|
|
19
23
|
|
|
20
24
|
bedrock.events.on('bedrock.init', async () => {
|
|
21
25
|
const cfg = bedrock.config.kms;
|
|
@@ -100,8 +104,7 @@ export async function insert({config} = {}) {
|
|
|
100
104
|
config
|
|
101
105
|
};
|
|
102
106
|
try {
|
|
103
|
-
const result = await database.collections['kms-keystore'].insertOne(
|
|
104
|
-
record, database.writeOptions);
|
|
107
|
+
const result = await database.collections['kms-keystore'].insertOne(record);
|
|
105
108
|
return result.ops[0];
|
|
106
109
|
} catch(e) {
|
|
107
110
|
if(!database.isDuplicateError(e)) {
|
|
@@ -180,7 +183,7 @@ export async function update({config, explain = false} = {}) {
|
|
|
180
183
|
config,
|
|
181
184
|
'meta.updated': Date.now()
|
|
182
185
|
}
|
|
183
|
-
}
|
|
186
|
+
});
|
|
184
187
|
|
|
185
188
|
if(result.result.n === 0) {
|
|
186
189
|
// no records changed...
|
|
@@ -196,8 +199,10 @@ export async function update({config, explain = false} = {}) {
|
|
|
196
199
|
});
|
|
197
200
|
}
|
|
198
201
|
|
|
199
|
-
|
|
200
|
-
|
|
202
|
+
if(!DISABLE_CLEAR_CACHE_ON_UPDATE) {
|
|
203
|
+
// delete record from cache
|
|
204
|
+
KEYSTORE_CONFIG_CACHE.delete(config.id);
|
|
205
|
+
}
|
|
201
206
|
|
|
202
207
|
return true;
|
|
203
208
|
}
|
|
@@ -207,11 +212,16 @@ export async function update({config, explain = false} = {}) {
|
|
|
207
212
|
*
|
|
208
213
|
* @param {object} options - The options to use.
|
|
209
214
|
* @param {string} options.id - The ID of the keystore.
|
|
215
|
+
* @param {boolean} [options.fresh=false] - False if it is safe to use a
|
|
216
|
+
* potentially cached value, true to always get a fresh value.
|
|
210
217
|
*
|
|
211
218
|
* @returns {Promise<object>} Resolves to `{config, meta}`.
|
|
212
219
|
*/
|
|
213
|
-
export async function get({id} = {}) {
|
|
220
|
+
export async function get({id, fresh = false} = {}) {
|
|
214
221
|
assert.string(id, 'id');
|
|
222
|
+
if(fresh) {
|
|
223
|
+
KEYSTORE_CONFIG_CACHE.delete(id);
|
|
224
|
+
}
|
|
215
225
|
const fn = () => _getUncachedRecord({id});
|
|
216
226
|
return KEYSTORE_CONFIG_CACHE.memoize({key: id, fn});
|
|
217
227
|
}
|
|
@@ -328,3 +338,8 @@ export async function _getUncachedRecord({id, explain = false} = {}) {
|
|
|
328
338
|
}
|
|
329
339
|
return record;
|
|
330
340
|
}
|
|
341
|
+
|
|
342
|
+
// exported for testing purposes
|
|
343
|
+
export function _disableClearCacheOnUpdate(disable) {
|
|
344
|
+
DISABLE_CLEAR_CACHE_ON_UPDATE = disable;
|
|
345
|
+
}
|
package/package.json
CHANGED
|
@@ -24,6 +24,12 @@ describe('keystores APIs', () => {
|
|
|
24
24
|
controller: 'f2da13ee-50d2-46ab-865d-ee23d609edbd',
|
|
25
25
|
sequence: 0,
|
|
26
26
|
};
|
|
27
|
+
const mockConfigDelta = {
|
|
28
|
+
id: 'https://example.com/keystores/f9da6f23-6e13-42e9-88d3-5c03011ecbbf',
|
|
29
|
+
kmsModule: 'ssm-v1',
|
|
30
|
+
controller: '17e77da1-bb7e-4fe2-b4dd-1c4bba933d7c',
|
|
31
|
+
sequence: 0,
|
|
32
|
+
};
|
|
27
33
|
|
|
28
34
|
before(async () => {
|
|
29
35
|
let err;
|
|
@@ -31,6 +37,7 @@ describe('keystores APIs', () => {
|
|
|
31
37
|
await keystores.insert({config: mockConfigAlpha});
|
|
32
38
|
await keystores.insert({config: mockConfigBeta});
|
|
33
39
|
await keystores.insert({config: mockConfigGamma});
|
|
40
|
+
await keystores.insert({config: mockConfigDelta});
|
|
34
41
|
} catch(e) {
|
|
35
42
|
err = e;
|
|
36
43
|
}
|
|
@@ -93,6 +100,39 @@ describe('keystores APIs', () => {
|
|
|
93
100
|
result.should.be.a('boolean');
|
|
94
101
|
result.should.be.true;
|
|
95
102
|
});
|
|
103
|
+
it('successfully updates a keystore and gets a fresh value', async () => {
|
|
104
|
+
const config = klona(mockConfigDelta);
|
|
105
|
+
|
|
106
|
+
// get keystore config to prime cache
|
|
107
|
+
await keystores.get({id: config.id});
|
|
108
|
+
|
|
109
|
+
// now update config
|
|
110
|
+
let err;
|
|
111
|
+
let result;
|
|
112
|
+
config.sequence++;
|
|
113
|
+
config.controller = 'someOtherController';
|
|
114
|
+
const oldDelete = keystores._KEYSTORE_CONFIG_CACHE.delete;
|
|
115
|
+
try {
|
|
116
|
+
// remove cache delete functionality to test fresh API
|
|
117
|
+
keystores._KEYSTORE_CONFIG_CACHE.delete = () => {};
|
|
118
|
+
result = await keystores.update({config});
|
|
119
|
+
} catch(e) {
|
|
120
|
+
err = e;
|
|
121
|
+
} finally {
|
|
122
|
+
keystores._KEYSTORE_CONFIG_CACHE.delete = oldDelete;
|
|
123
|
+
}
|
|
124
|
+
assertNoError(err);
|
|
125
|
+
result.should.be.a('boolean');
|
|
126
|
+
result.should.be.true;
|
|
127
|
+
|
|
128
|
+
// get keystore config, should be stale
|
|
129
|
+
const stale = await keystores.get({id: config.id});
|
|
130
|
+
stale.config.controller.should.not.equal(config.controller);
|
|
131
|
+
|
|
132
|
+
// get fresh config
|
|
133
|
+
const fresh = await keystores.get({id: config.id, fresh: true});
|
|
134
|
+
fresh.config.controller.should.equal(config.controller);
|
|
135
|
+
});
|
|
96
136
|
it('fails to updates a keystore using wrong sequence number', async () => {
|
|
97
137
|
let err;
|
|
98
138
|
let result;
|