@bedrock/kms-http 23.2.0 → 23.3.1
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 +5 -2
- package/lib/BedrockKeystoreConfigStorage.js +2 -2
- package/lib/config.js +16 -0
- package/lib/http.js +68 -4
- package/lib/middleware.js +3 -3
- package/package.json +17 -19
- package/schemas/bedrock-kms-http.js +3 -3
package/README.md
CHANGED
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
This software requires and supports maintained recent versions of Node.js.
|
|
15
|
+
Updates may remove support for older unmaintained platform versions. Please use
|
|
16
|
+
dependency version lock files and testing to ensure compatibility with this
|
|
17
|
+
software.
|
|
15
18
|
|
|
16
19
|
### NPM
|
|
17
20
|
|
|
@@ -36,7 +39,7 @@ npm install
|
|
|
36
39
|
In `lib/index.js` (or `main.js`, as appropriate):
|
|
37
40
|
|
|
38
41
|
```js
|
|
39
|
-
import 'bedrock
|
|
42
|
+
import '@bedrock/kms-http';
|
|
40
43
|
```
|
|
41
44
|
|
|
42
45
|
### KMS HTTP API
|
|
@@ -27,7 +27,7 @@ export class BedrockKeystoreConfigStorage extends KeystoreConfigStorage {
|
|
|
27
27
|
throw new BedrockError(
|
|
28
28
|
'Permission denied. Source IP is not allowed.', 'NotAllowedError', {
|
|
29
29
|
httpStatusCode: 403,
|
|
30
|
-
public: true
|
|
30
|
+
public: true
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -51,7 +51,7 @@ export class BedrockKeystoreConfigStorage extends KeystoreConfigStorage {
|
|
|
51
51
|
throw new BedrockError(
|
|
52
52
|
'Permission denied. Source IP is not allowed.', 'NotAllowedError', {
|
|
53
53
|
httpStatusCode: 403,
|
|
54
|
-
public: true
|
|
54
|
+
public: true
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
57
|
}
|
package/lib/config.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import {config} from '@bedrock/core';
|
|
5
5
|
import '@bedrock/app-identity';
|
|
6
|
+
import '@bedrock/express';
|
|
6
7
|
import '@bedrock/meter-usage-reporter';
|
|
7
8
|
import '@bedrock/validation';
|
|
8
9
|
|
|
@@ -64,3 +65,18 @@ cfg.kmsModuleProxy = {
|
|
|
64
65
|
}
|
|
65
66
|
*/
|
|
66
67
|
};
|
|
68
|
+
|
|
69
|
+
// require raw JSON payloads for proxied routes to preserve specific content
|
|
70
|
+
// expression as it is used in signed digests
|
|
71
|
+
const createRawBodyParserOptions = () => ({
|
|
72
|
+
raw: {
|
|
73
|
+
type: ['json', '+json']
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
const bodyParserRoutes = config.express.bodyParser.routes;
|
|
77
|
+
// for generating a new key
|
|
78
|
+
bodyParserRoutes['/kms/keystores/:keystoreId/keys'] =
|
|
79
|
+
createRawBodyParserOptions();
|
|
80
|
+
// for running a key operation on an existing key
|
|
81
|
+
bodyParserRoutes['/kms/keystores/:keystoreId/keys/:keyId'] =
|
|
82
|
+
createRawBodyParserOptions();
|
package/lib/http.js
CHANGED
|
@@ -5,7 +5,7 @@ import * as bedrock from '@bedrock/core';
|
|
|
5
5
|
import * as brZCapStorage from '@bedrock/zcap-storage';
|
|
6
6
|
import * as helpers from './helpers.js';
|
|
7
7
|
import * as middleware from './middleware.js';
|
|
8
|
-
import
|
|
8
|
+
import * as zlib from 'node:zlib';
|
|
9
9
|
import {
|
|
10
10
|
getConfigsQuery,
|
|
11
11
|
postKeystoreBody,
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from '@bedrock/kms';
|
|
19
19
|
import {reportOperationUsage, SERVICE_TYPE} from './metering.js';
|
|
20
20
|
import {asyncHandler} from '@bedrock/express';
|
|
21
|
+
import {createProxyMiddleware} from 'http-proxy-middleware';
|
|
21
22
|
import {generateRandom} from '@digitalbazaar/webkms-switch';
|
|
22
23
|
import {meters} from '@bedrock/meter-usage-reporter';
|
|
23
24
|
import {createValidateMiddleware as validate} from '@bedrock/validation';
|
|
@@ -52,6 +53,8 @@ bedrock.events.on('bedrock-express.configure.routes', app => {
|
|
|
52
53
|
|
|
53
54
|
// create KMS module proxy middleware for use with KMS operations
|
|
54
55
|
const kmsModuleProxyMiddleware = _createKmsModuleProxyMiddleware();
|
|
56
|
+
// create raw => json parser for routes using KMS module proxy middleware
|
|
57
|
+
const rawBodyToJsonMiddleware = asyncHandler(_parseRawBodyAsJson);
|
|
55
58
|
|
|
56
59
|
/* Note: CORS is used on all endpoints. This is safe because authorization
|
|
57
60
|
uses HTTP signatures + capabilities, not cookies; CSRF is not possible. */
|
|
@@ -100,7 +103,7 @@ bedrock.events.on('bedrock-express.configure.routes', app => {
|
|
|
100
103
|
// insufficient remaining storage
|
|
101
104
|
throw new BedrockError('Permission denied.', 'NotAllowedError', {
|
|
102
105
|
httpStatusCode: 403,
|
|
103
|
-
public: true
|
|
106
|
+
public: true
|
|
104
107
|
});
|
|
105
108
|
}
|
|
106
109
|
|
|
@@ -220,6 +223,7 @@ bedrock.events.on('bedrock-express.configure.routes', app => {
|
|
|
220
223
|
cors(),
|
|
221
224
|
getKeystoreConfig,
|
|
222
225
|
kmsModuleProxyMiddleware,
|
|
226
|
+
rawBodyToJsonMiddleware,
|
|
223
227
|
handleOperation);
|
|
224
228
|
|
|
225
229
|
// invoke KMS operation on an existing key
|
|
@@ -229,6 +233,7 @@ bedrock.events.on('bedrock-express.configure.routes', app => {
|
|
|
229
233
|
cors(),
|
|
230
234
|
getKeystoreConfig,
|
|
231
235
|
kmsModuleProxyMiddleware,
|
|
236
|
+
rawBodyToJsonMiddleware,
|
|
232
237
|
handleOperation);
|
|
233
238
|
|
|
234
239
|
// TODO: consider whether this should be exposed w/o authorization
|
|
@@ -240,6 +245,7 @@ bedrock.events.on('bedrock-express.configure.routes', app => {
|
|
|
240
245
|
cors(),
|
|
241
246
|
getKeystoreConfig,
|
|
242
247
|
kmsModuleProxyMiddleware,
|
|
248
|
+
rawBodyToJsonMiddleware,
|
|
243
249
|
asyncHandler(async (req, res) => {
|
|
244
250
|
const {webkms: {keystore}} = req;
|
|
245
251
|
const keyId = `${keystore.id}/keys/${req.params.keyId}`;
|
|
@@ -278,7 +284,7 @@ bedrock.events.on('bedrock-express.configure.routes', app => {
|
|
|
278
284
|
// meter is disabled, do not allow storage
|
|
279
285
|
throw new BedrockError('Permission denied.', 'NotAllowedError', {
|
|
280
286
|
httpStatusCode: 403,
|
|
281
|
-
public: true
|
|
287
|
+
public: true
|
|
282
288
|
});
|
|
283
289
|
}
|
|
284
290
|
|
|
@@ -319,7 +325,7 @@ function _createKmsModuleProxyMiddleware() {
|
|
|
319
325
|
ws: false,
|
|
320
326
|
on: {
|
|
321
327
|
// required to rewrite body since it has been parsed
|
|
322
|
-
proxyReq:
|
|
328
|
+
proxyReq: _reserializeRequestBody
|
|
323
329
|
}
|
|
324
330
|
}));
|
|
325
331
|
}
|
|
@@ -333,3 +339,61 @@ function _createKmsModuleProxyMiddleware() {
|
|
|
333
339
|
mw(req, res, next);
|
|
334
340
|
};
|
|
335
341
|
}
|
|
342
|
+
|
|
343
|
+
// parses raw body to json
|
|
344
|
+
function _parseRawBodyAsJson(req, res, next) {
|
|
345
|
+
try {
|
|
346
|
+
// special case empty body
|
|
347
|
+
req.body = req.body?.length > 0 ? JSON.parse(req.body.toString()) : {};
|
|
348
|
+
next();
|
|
349
|
+
} catch(cause) {
|
|
350
|
+
throw new BedrockError('Failed to parse expected JSON body.', {
|
|
351
|
+
name: 'DataError',
|
|
352
|
+
details: {httpStatusCode: 400, public: true},
|
|
353
|
+
cause
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function _reserializeRequestBody(proxyReq, req) {
|
|
359
|
+
try {
|
|
360
|
+
if(!(req.body?.length > 0)) {
|
|
361
|
+
// nothing to send
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const contentType = proxyReq.getHeader('content-type');
|
|
366
|
+
if(!contentType) {
|
|
367
|
+
// nothing to send
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// reapply content-encoding
|
|
372
|
+
const contentEncoding = String(
|
|
373
|
+
proxyReq.getHeader('content-encoding')).toLowerCase();
|
|
374
|
+
let data = req.body;
|
|
375
|
+
if(contentEncoding === 'gzip') {
|
|
376
|
+
data = zlib.gzipSync(data);
|
|
377
|
+
} else if(contentEncoding === 'deflate') {
|
|
378
|
+
data = zlib.deflateSync(data);
|
|
379
|
+
} else if(contentEncoding === 'zstd') {
|
|
380
|
+
// note: not supported by body-parser, so unreachable
|
|
381
|
+
data = zlib.zstdCompressSync(data);
|
|
382
|
+
} else if(contentEncoding === 'br') {
|
|
383
|
+
// note: not supported by body-parser, so unreachable
|
|
384
|
+
data = zlib.brotliCompressSync(data);
|
|
385
|
+
} else {
|
|
386
|
+
proxyReq.removeHeader('content-encoding');
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const transferEncoding = proxyReq.getHeader('transfer-encoding');
|
|
390
|
+
if(!transferEncoding) {
|
|
391
|
+
proxyReq.setHeader('content-length', Buffer.byteLength(data));
|
|
392
|
+
}
|
|
393
|
+
proxyReq.write(data);
|
|
394
|
+
} catch(e) {
|
|
395
|
+
// pass error through `proxyReq` processing channel
|
|
396
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
397
|
+
proxyReq.destroy(error);
|
|
398
|
+
}
|
|
399
|
+
}
|
package/lib/middleware.js
CHANGED
|
@@ -81,7 +81,7 @@ export function authorizeKeystoreZcapInvocation() {
|
|
|
81
81
|
rootInvocationTarget: req.webkms.keystore.id
|
|
82
82
|
};
|
|
83
83
|
},
|
|
84
|
-
getRootController: getKeystoreController
|
|
84
|
+
getRootController: getKeystoreController
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -210,13 +210,13 @@ function onError({error}) {
|
|
|
210
210
|
error.message,
|
|
211
211
|
error.name || 'NotAllowedError', {
|
|
212
212
|
...details,
|
|
213
|
-
public: true
|
|
213
|
+
public: true
|
|
214
214
|
}, error);
|
|
215
215
|
}
|
|
216
216
|
throw new BedrockError(
|
|
217
217
|
'Authorization error.', 'NotAllowedError', {
|
|
218
218
|
httpStatusCode: 403,
|
|
219
|
-
public: true
|
|
219
|
+
public: true
|
|
220
220
|
}, error);
|
|
221
221
|
}
|
|
222
222
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bedrock/kms-http",
|
|
3
|
-
"version": "23.
|
|
3
|
+
"version": "23.3.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Bedrock KMS HTTP API",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -30,38 +30,36 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@digitalbazaar/ed25519-multikey": "^1.3.1",
|
|
32
32
|
"@digitalbazaar/ed25519-signature-2020": "^5.4.0",
|
|
33
|
-
"@digitalbazaar/ezcap-express": "^7.
|
|
33
|
+
"@digitalbazaar/ezcap-express": "^7.3.0",
|
|
34
34
|
"@digitalbazaar/webkms-switch": "^12.0.0",
|
|
35
|
-
"cidr-regex": "^
|
|
36
|
-
"cors": "^2.8.
|
|
35
|
+
"cidr-regex": "^7.1.2",
|
|
36
|
+
"cors": "^2.8.6",
|
|
37
37
|
"forwarded": "^0.2.0",
|
|
38
|
-
"http-proxy-middleware": "^
|
|
39
|
-
"ipaddr.js": "^2.
|
|
38
|
+
"http-proxy-middleware": "^4.2.0",
|
|
39
|
+
"ipaddr.js": "^2.5.0"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
|
-
"@bedrock/app-identity": "^4.
|
|
42
|
+
"@bedrock/app-identity": "^4.2.0",
|
|
43
43
|
"@bedrock/core": "^6.3.0",
|
|
44
44
|
"@bedrock/did-context": "^6.0.0",
|
|
45
|
-
"@bedrock/did-io": "^10.
|
|
46
|
-
"@bedrock/express": "^8.3
|
|
47
|
-
"@bedrock/jsonld-document-loader": "^5.2.
|
|
48
|
-
"@bedrock/kms": "^16.
|
|
49
|
-
"@bedrock/meter-usage-reporter": "^10.
|
|
45
|
+
"@bedrock/did-io": "^10.5.0",
|
|
46
|
+
"@bedrock/express": "^8.8.3",
|
|
47
|
+
"@bedrock/jsonld-document-loader": "^5.2.1",
|
|
48
|
+
"@bedrock/kms": "^16.2.0",
|
|
49
|
+
"@bedrock/meter-usage-reporter": "^10.1.0",
|
|
50
50
|
"@bedrock/security-context": "^9.0.0",
|
|
51
|
-
"@bedrock/validation": "^7.
|
|
51
|
+
"@bedrock/validation": "^7.3.0",
|
|
52
52
|
"@bedrock/veres-one-context": "^16.0.0",
|
|
53
|
-
"@bedrock/zcap-storage": "^9.
|
|
53
|
+
"@bedrock/zcap-storage": "^9.7.0"
|
|
54
54
|
},
|
|
55
55
|
"directories": {
|
|
56
56
|
"lib": "./lib"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
|
-
"eslint": "^
|
|
60
|
-
"eslint
|
|
61
|
-
"eslint-plugin-jsdoc": "^50.6.3",
|
|
62
|
-
"eslint-plugin-unicorn": "^56.0.1"
|
|
59
|
+
"@digitalbazaar/eslint-config": "^9.0.0",
|
|
60
|
+
"eslint": "^10.13.0"
|
|
63
61
|
},
|
|
64
62
|
"engines": {
|
|
65
|
-
"node": ">=
|
|
63
|
+
"node": ">=22"
|
|
66
64
|
}
|
|
67
65
|
}
|
|
@@ -92,7 +92,7 @@ const delegatedZcap = {
|
|
|
92
92
|
proofValue: {
|
|
93
93
|
title: 'proofValue',
|
|
94
94
|
type: 'string'
|
|
95
|
-
}
|
|
95
|
+
}
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
}
|
|
@@ -109,7 +109,7 @@ const ipAllowList = {
|
|
|
109
109
|
items: {
|
|
110
110
|
type: 'string',
|
|
111
111
|
// leading and trailing slashes in regex must be removed
|
|
112
|
-
pattern: cidrRegex({exact: true}).toString().slice(1, -1)
|
|
112
|
+
pattern: cidrRegex({exact: true}).toString().slice(1, -1)
|
|
113
113
|
}
|
|
114
114
|
};
|
|
115
115
|
|
|
@@ -146,7 +146,7 @@ const updateKeystoreConfigBody = {
|
|
|
146
146
|
required: [
|
|
147
147
|
'controller',
|
|
148
148
|
'id',
|
|
149
|
-
'sequence'
|
|
149
|
+
'sequence'
|
|
150
150
|
],
|
|
151
151
|
properties: {
|
|
152
152
|
controller,
|