@bedrock/kms-http 14.3.0 → 14.5.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 CHANGED
@@ -1,5 +1,24 @@
1
1
  # bedrock-kms-http ChangeLog
2
2
 
3
+ ## 14.5.0 - 2022-06-19
4
+
5
+ ### Changed
6
+ - Use `@bedrock/kms@10.3`.
7
+ - Use `@digitalbazaar/webkms-switch@10.3`.
8
+
9
+ ## 14.4.0 - 2022-05-19
10
+
11
+ ### Added
12
+ - Add max age header for CORS, defaulting to the max acceptable time for
13
+ modern browsers of 86400 seconds (24 hours).
14
+
15
+ ## 14.3.1 - 2022-05-16
16
+
17
+ ### Fixed
18
+ - Use `@digitalbazaar/webkms-switch@10.2` to ensure that pre-cached `webkms`
19
+ request meta data is not overwritten and that a custom `getRootController`
20
+ function is used (to enable cache busting on KMS operations).
21
+
3
22
  ## 14.3.0 - 2022-05-16
4
23
 
5
24
  ### Added
package/lib/http.js CHANGED
@@ -6,7 +6,6 @@ import * as brZCapStorage from '@bedrock/zcap-storage';
6
6
  import * as helpers from './helpers.js';
7
7
  import * as middleware from './middleware.js';
8
8
  import {asyncHandler} from '@bedrock/express';
9
- import cors from 'cors';
10
9
  import {createRequire} from 'node:module';
11
10
  import {createValidateMiddleware as validate} from '@bedrock/validation';
12
11
  import {
@@ -24,6 +23,7 @@ const require = createRequire(import.meta.url);
24
23
  const {generateRandom} = require('@digitalbazaar/webkms-switch');
25
24
 
26
25
  const {config, util: {BedrockError}} = bedrock;
26
+ const {cors} = middleware;
27
27
 
28
28
  bedrock.events.on('bedrock-express.configure.routes', app => {
29
29
  const cfg = config['kms-http'];
@@ -191,6 +191,7 @@ bedrock.events.on('bedrock-express.configure.routes', app => {
191
191
  app.post(
192
192
  routes.keys,
193
193
  cors(),
194
+ getKeystoreConfig,
194
195
  handleOperation);
195
196
 
196
197
  // invoke KMS operation on an existing key
@@ -198,6 +199,7 @@ bedrock.events.on('bedrock-express.configure.routes', app => {
198
199
  app.post(
199
200
  routes.key,
200
201
  cors(),
202
+ getKeystoreConfig,
201
203
  handleOperation);
202
204
 
203
205
  // TODO: consider whether this should be exposed w/o authorization
package/lib/middleware.js CHANGED
@@ -7,6 +7,7 @@ import * as helpers from './helpers.js';
7
7
  import {asyncHandler} from '@bedrock/express';
8
8
  import {BedrockKeystoreConfigStorage} from './BedrockKeystoreConfigStorage.js';
9
9
  import {createRequire} from 'node:module';
10
+ import {default as _cors} from 'cors';
10
11
  import {
11
12
  defaultDocumentLoader as documentLoader,
12
13
  defaultModuleManager as moduleManager
@@ -46,7 +47,7 @@ export function createGetKeystoreConfig({routes}) {
46
47
  id: keystoreId, req, returnRecord: true
47
48
  });
48
49
  const {config: keystore} = configRecord;
49
- req.webkms = {keystore, configRecord};
50
+ req.webkms = {...req.webkms, keystore, configRecord};
50
51
  }
51
52
  next();
52
53
  });
@@ -58,7 +59,10 @@ export function createKmsOperationMiddleware() {
58
59
  return createOperationMiddleware({
59
60
  ...kmsOperationOptions,
60
61
  storage, moduleManager, documentLoader,
61
- expectedHost: config.server.host, getVerifier,
62
+ expectedHost: config.server.host,
63
+ // override `getRootController` to provide cache busting capability
64
+ getRootController: getKeystoreController,
65
+ getVerifier,
62
66
  inspectCapabilityChain,
63
67
  onError, onSuccess: reportOperationUsage, suiteFactory
64
68
  });
@@ -104,6 +108,11 @@ export function authorizeZcapRevocation() {
104
108
  });
105
109
  }
106
110
 
111
+ export function cors() {
112
+ // `86400` is the max acceptable cache age for modern browsers
113
+ return _cors({maxAge: 86400});
114
+ }
115
+
107
116
  async function getKeystoreController({req}) {
108
117
  /* The following code is to prevent false-negative authz errors that are the
109
118
  result of stale cached keystore configs.
@@ -185,7 +194,7 @@ async function getKeystoreController({req}) {
185
194
  id: req.webkms.keystore.id, req, returnRecord: true, fresh: true
186
195
  });
187
196
  const {config: keystore} = configRecord;
188
- req.webkms = {keystore, configRecord};
197
+ req.webkms = {...req.webkms, keystore, configRecord};
189
198
  logger.debug('Forced fresh keystore config retrieval during zcap check.');
190
199
  }
191
200
 
@@ -243,10 +252,7 @@ function onError({error}) {
243
252
  if(!(error instanceof BedrockError)) {
244
253
  // always expose cause message and name; expose cause details as
245
254
  // BedrockError if error is marked public
246
- let details = {};
247
- if(error.details && error.details.public) {
248
- details = error.details;
249
- }
255
+ const details = error.details?.public ? error.details : {};
250
256
  error = new BedrockError(
251
257
  error.message,
252
258
  error.name || 'NotAllowedError', {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrock/kms-http",
3
- "version": "14.3.0",
3
+ "version": "14.5.0",
4
4
  "type": "module",
5
5
  "description": "Bedrock KMS HTTP API",
6
6
  "main": "./lib/index.js",
@@ -27,7 +27,7 @@
27
27
  "@digitalbazaar/ed25519-signature-2020": "^3.0.0",
28
28
  "@digitalbazaar/ed25519-verification-key-2020": "^3.2.0",
29
29
  "@digitalbazaar/ezcap-express": "^6.2.0",
30
- "@digitalbazaar/webkms-switch": "^10.0.0",
30
+ "@digitalbazaar/webkms-switch": "^10.3.0",
31
31
  "cidr-regex": "^3.1.1",
32
32
  "cors": "^2.8.5",
33
33
  "crypto-ld": "^6.0.0",
@@ -38,7 +38,7 @@
38
38
  "@bedrock/app-identity": "^3.0.0",
39
39
  "@bedrock/core": "^6.0.0",
40
40
  "@bedrock/express": "^8.0.0",
41
- "@bedrock/kms": "^10.2.0",
41
+ "@bedrock/kms": "^10.3.0",
42
42
  "@bedrock/meter-usage-reporter": "^7.0.0",
43
43
  "@bedrock/validation": "^7.0.0",
44
44
  "@bedrock/zcap-storage": "^7.0.0"
@@ -67,7 +67,7 @@ describe('keystore API interactions using webkms-client', () => {
67
67
  bobKeystoreAgent.keystoreId);
68
68
  });
69
69
 
70
- it('updates config controller twice proving cache busting', async () => {
70
+ it('updates config and uses key proving cache busting', async () => {
71
71
  const keystoreId = aliceKeystoreConfig.id;
72
72
  const kmsClient = new KmsClient({httpsAgent});
73
73
 
@@ -88,6 +88,9 @@ describe('keystore API interactions using webkms-client', () => {
88
88
  const agent3KeystoreAgent = new KeystoreAgent(
89
89
  {capabilityAgent: agent3, keystoreId, kmsClient});
90
90
 
91
+ // generate a key for use
92
+ const hmacKey = await aliceKeystoreAgent.generateKey({type: 'hmac'});
93
+
91
94
  // delegate zcap for updating config from agent2 to agent3 to test
92
95
  // cache busting with delegated zcap
93
96
  const rootCapability = ZCAP_ROOT_PREFIX + encodeURIComponent(keystoreId);
@@ -120,7 +123,7 @@ describe('keystore API interactions using webkms-client', () => {
120
123
  // disable cache delete functionality on update
121
124
  keystores._disableClearCacheOnUpdate(true);
122
125
 
123
- // update controller alice => agent1 (no cache busting required
126
+ // update controller alice => agent1 (no cache busting required)
124
127
  {
125
128
  config.sequence++;
126
129
  config.controller = agent1.id;
@@ -164,6 +167,72 @@ describe('keystore API interactions using webkms-client', () => {
164
167
  result.config.controller.should.eql(agent1.id);
165
168
  ({config} = result);
166
169
  }
170
+
171
+ // update controller back to alice to test key op cache busting
172
+ {
173
+ config.sequence++;
174
+ config.controller = aliceCapabilityAgent.id;
175
+ const result = await agent1KeystoreAgent.updateConfig({config});
176
+ result.config.id.should.eql(config.id);
177
+ result.config.controller.should.eql(aliceCapabilityAgent.id);
178
+ ({config} = result);
179
+ }
180
+
181
+ // use hmac key as alice (requires cache busting by inspecting root
182
+ // zcap)
183
+ await hmacKey.sign({data: new Uint8Array([0])});
184
+
185
+ // update controller alice => agent1 (no cache busting required)
186
+ {
187
+ config.sequence++;
188
+ config.controller = agent1.id;
189
+ const result = await aliceKeystoreAgent.updateConfig({config});
190
+ result.config.id.should.eql(config.id);
191
+ result.config.controller.should.eql(agent1.id);
192
+ ({config} = result);
193
+ }
194
+
195
+ // use hmac key as agent1 (requires cache busting by
196
+ // inspecting the root zcap)
197
+ hmacKey.invocationSigner = agent1.getSigner();
198
+ // use different data each time to avoid hmac caching
199
+ await hmacKey.sign({data: new Uint8Array([1])});
200
+
201
+ // update controller agent1 => agent2 (requires cache busting by
202
+ // inspecting the root zcap)
203
+ {
204
+ config.sequence++;
205
+ config.controller = agent2.id;
206
+ const result = await agent1KeystoreAgent.updateConfig({config});
207
+ result.config.id.should.eql(config.id);
208
+ result.config.controller.should.eql(agent2.id);
209
+ ({config} = result);
210
+ }
211
+
212
+ // use hmac key as agent3 (requires cache busting by
213
+ // inspecting a delegated zcap)
214
+ hmacKey.capability = agent3Zcap;
215
+ hmacKey.invocationSigner = agent3.getSigner();
216
+ // use different data each time to avoid hmac caching
217
+ await hmacKey.sign({data: new Uint8Array([1])});
218
+
219
+ // update controller agent2 => agent3 (cache should already be fresh)
220
+ {
221
+ config.sequence++;
222
+ config.controller = agent3.id;
223
+ const result = await agent3KeystoreAgent.updateConfig(
224
+ {capability: agent3Zcap, config});
225
+ result.config.id.should.eql(config.id);
226
+ result.config.controller.should.eql(agent3.id);
227
+ ({config} = result);
228
+ }
229
+
230
+ // use hmac key as agent1 (requires cache busting by
231
+ // inspecting a deep delegated zcap)
232
+ hmacKey.capability = agent1Zcap;
233
+ hmacKey.invocationSigner = agent1.getSigner();
234
+ // use different data each time to avoid hmac caching
235
+ await hmacKey.sign({data: new Uint8Array([1])});
167
236
  } catch(e) {
168
237
  assertNoError(e);
169
238
  } finally {