From b46050a449a0453351c49be21554e8d11a5299ef Mon Sep 17 00:00:00 2001 From: James Easter Date: Wed, 19 Nov 2025 20:05:42 -0700 Subject: [PATCH 1/7] Add route for fetching all keystores with root controller. --- lib/http.js | 36 ++++++++++++++++++++++++++++++++---- schemas/bedrock-kms-http.js | 11 +++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/lib/http.js b/lib/http.js index deafeed..f7ebbd9 100644 --- a/lib/http.js +++ b/lib/http.js @@ -6,14 +6,15 @@ import * as brZCapStorage from '@bedrock/zcap-storage'; import * as helpers from './helpers.js'; import * as middleware from './middleware.js'; import { - keystores, - defaultModuleManager as moduleManager -} from '@bedrock/kms'; -import { + getConfigsQuery, postKeystoreBody, postRevocationBody, updateKeystoreConfigBody } from '../schemas/bedrock-kms-http.js'; +import { + keystores, + defaultModuleManager as moduleManager +} from '@bedrock/kms'; import {reportOperationUsage, SERVICE_TYPE} from './metering.js'; import {asyncHandler} from '@bedrock/express'; import {generateRandom} from '@digitalbazaar/webkms-switch'; @@ -41,6 +42,8 @@ bedrock.events.on('bedrock-express.configure.routes', app => { routes.key = `${routes.keys}/:keyId`; routes.revocations = `${routes.keystore}/zcaps/revocations/:revocationId`; + const {baseUri} = bedrock.config.server; + // create middleware for handling KMS operations const handleOperation = middleware.createKmsOperationMiddleware(); @@ -51,6 +54,31 @@ bedrock.events.on('bedrock-express.configure.routes', app => { /* Note: CORS is used on all endpoints. This is safe because authorization uses HTTP signatures + capabilities, not cookies; CSRF is not possible. */ + // get all keystores with root controller + app.options(routes.keystores, cors()); + app.get( + routes.keystores, + cors(), + validate({querySchema: getConfigsQuery}), + middleware.authorizeZcapInvocation({ + async getExpectedValues() { + return { + host: bedrock.config.server.host, + rootInvocationTarget: baseUri + routes.keystores + }; + }, + async getRootController({req}) { + return req.query.controller; + } + }), + asyncHandler(async (req, res) => { + const controller = req.query.controller; + const results = await keystores.find({controller}); + res.json({ + results: results.map(r => r.config) + }); + })); + // create a new keystore app.options(routes.keystores, cors()); app.post( diff --git a/schemas/bedrock-kms-http.js b/schemas/bedrock-kms-http.js index 95b2848..3e1cb86 100644 --- a/schemas/bedrock-kms-http.js +++ b/schemas/bedrock-kms-http.js @@ -162,7 +162,18 @@ const postRevocationBody = { ...delegatedZcap }; +const getConfigsQuery = { + title: 'Service Object Configuration Query', + type: 'object', + required: ['controller'], + additionalProperties: false, + properties: { + controller + } +}; + export { + getConfigsQuery, postKeystoreBody, postRevocationBody, updateKeystoreConfigBody From 61834355f52f8ea284eb829a61163c8ff56a3dc5 Mon Sep 17 00:00:00 2001 From: James Easter Date: Thu, 20 Nov 2025 16:01:53 -0700 Subject: [PATCH 2/7] Add get all BedrockKeystoreConfigStorage method. --- lib/BedrockKeystoreConfigStorage.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/BedrockKeystoreConfigStorage.js b/lib/BedrockKeystoreConfigStorage.js index a72272d..0a9f04e 100644 --- a/lib/BedrockKeystoreConfigStorage.js +++ b/lib/BedrockKeystoreConfigStorage.js @@ -33,4 +33,27 @@ export class BedrockKeystoreConfigStorage extends KeystoreConfigStorage { return returnRecord ? record : keystoreConfig; } + + async getAll({req, returnRecord = false} = {}) { + const controller = req.query.controller; + const records = await keystores.find({controller}); + const keystoreConfigs = records.map(r => r.config); + // skip request checks if specifically requested + if(req === false) { + return returnRecord ? records : keystoreConfigs; + } + // verify that request is from an IP that is allowed to access the config + for(let i = 0; i < keystoreConfigs.length; i++) { + const keystoreConfig = keystoreConfigs[i]; + const {verified} = helpers.verifyRequestIp({keystoreConfig, req}); + if(!verified) { + throw new BedrockError( + 'Permission denied. Source IP is not allowed.', 'NotAllowedError', { + httpStatusCode: 403, + public: true, + }); + } + } + return returnRecord ? records : keystoreConfigs; + } } From f3e652f02b4b52f35811c1ed9c795d27ed081130 Mon Sep 17 00:00:00 2001 From: James Easter Date: Thu, 20 Nov 2025 16:02:17 -0700 Subject: [PATCH 3/7] Add middleware for fetching all keystore configs. --- lib/middleware.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/middleware.js b/lib/middleware.js index 5a62747..afc7d78 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -43,6 +43,16 @@ export function createGetKeystoreConfig({routes}) { }); } +// creates middleware to get all the keystore configs for root controller +export function createGetAllKeystoreConfigs() { + return asyncHandler(async (req, _, next) => { + const controller = req.query.controller; + const results = await storage.getAll({req, controller}); + req.keystores = results.map(r => r.config); + next(); + }); +} + // creates middleware for handling KMS operations export function createKmsOperationMiddleware() { const {kmsOperationOptions} = config['kms-http']; From 46d22f79a837a593b7e022eb067aa6d67ab4704b Mon Sep 17 00:00:00 2001 From: James Easter Date: Thu, 20 Nov 2025 16:02:56 -0700 Subject: [PATCH 4/7] Use getAllKeystoreConfigs middleware in keystores GET route. --- lib/http.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/http.js b/lib/http.js index f7ebbd9..9a6af16 100644 --- a/lib/http.js +++ b/lib/http.js @@ -47,6 +47,9 @@ bedrock.events.on('bedrock-express.configure.routes', app => { // create middleware for handling KMS operations const handleOperation = middleware.createKmsOperationMiddleware(); + // create middleware for returning all keystores from root controller + const getAllKeystoreConfigs = middleware.createGetAllKeystoreConfigs(); + // create middleware for getting consistent view of the keystore config // associated with a request const getKeystoreConfig = middleware.createGetKeystoreConfig({routes}); @@ -71,12 +74,9 @@ bedrock.events.on('bedrock-express.configure.routes', app => { return req.query.controller; } }), + getAllKeystoreConfigs, asyncHandler(async (req, res) => { - const controller = req.query.controller; - const results = await keystores.find({controller}); - res.json({ - results: results.map(r => r.config) - }); + res.json({results: req.keystores}); })); // create a new keystore From 75a3531c7815d5e7c688d62cbbdef23e786218ba Mon Sep 17 00:00:00 2001 From: James Easter Date: Fri, 21 Nov 2025 12:46:04 -0700 Subject: [PATCH 5/7] Remove unnecessary middleware and pass more args to getAll. --- lib/BedrockKeystoreConfigStorage.js | 10 +++++++--- lib/http.js | 12 +++++++----- lib/middleware.js | 10 ---------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/lib/BedrockKeystoreConfigStorage.js b/lib/BedrockKeystoreConfigStorage.js index 0a9f04e..f678436 100644 --- a/lib/BedrockKeystoreConfigStorage.js +++ b/lib/BedrockKeystoreConfigStorage.js @@ -34,9 +34,13 @@ export class BedrockKeystoreConfigStorage extends KeystoreConfigStorage { return returnRecord ? record : keystoreConfig; } - async getAll({req, returnRecord = false} = {}) { - const controller = req.query.controller; - const records = await keystores.find({controller}); + async getAll({ + controller, req, query = {}, options = {}, + explain = false, returnRecord = false + } = {}) { + const records = await keystores.find({ + controller, query, options, explain + }); const keystoreConfigs = records.map(r => r.config); // skip request checks if specifically requested if(req === false) { diff --git a/lib/http.js b/lib/http.js index 9a6af16..7375891 100644 --- a/lib/http.js +++ b/lib/http.js @@ -17,6 +17,7 @@ import { } from '@bedrock/kms'; import {reportOperationUsage, SERVICE_TYPE} from './metering.js'; import {asyncHandler} from '@bedrock/express'; +import {BedrockKeystoreConfigStorage} from './BedrockKeystoreConfigStorage.js'; import {generateRandom} from '@digitalbazaar/webkms-switch'; import {meters} from '@bedrock/meter-usage-reporter'; import {createValidateMiddleware as validate} from '@bedrock/validation'; @@ -44,12 +45,11 @@ bedrock.events.on('bedrock-express.configure.routes', app => { const {baseUri} = bedrock.config.server; + const storage = new BedrockKeystoreConfigStorage(); + // create middleware for handling KMS operations const handleOperation = middleware.createKmsOperationMiddleware(); - // create middleware for returning all keystores from root controller - const getAllKeystoreConfigs = middleware.createGetAllKeystoreConfigs(); - // create middleware for getting consistent view of the keystore config // associated with a request const getKeystoreConfig = middleware.createGetKeystoreConfig({routes}); @@ -74,9 +74,11 @@ bedrock.events.on('bedrock-express.configure.routes', app => { return req.query.controller; } }), - getAllKeystoreConfigs, asyncHandler(async (req, res) => { - res.json({results: req.keystores}); + const controller = req.query.controller; + const options = {projection: {_id: 0, config: 1}}; + const results = await storage.getAll({controller, req, options}); + res.json({results}); })); // create a new keystore diff --git a/lib/middleware.js b/lib/middleware.js index afc7d78..5a62747 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -43,16 +43,6 @@ export function createGetKeystoreConfig({routes}) { }); } -// creates middleware to get all the keystore configs for root controller -export function createGetAllKeystoreConfigs() { - return asyncHandler(async (req, _, next) => { - const controller = req.query.controller; - const results = await storage.getAll({req, controller}); - req.keystores = results.map(r => r.config); - next(); - }); -} - // creates middleware for handling KMS operations export function createKmsOperationMiddleware() { const {kmsOperationOptions} = config['kms-http']; From 399d4462ad669b6e95ae2aefabadb969fe9c930f Mon Sep 17 00:00:00 2001 From: James Easter Date: Fri, 21 Nov 2025 13:05:32 -0700 Subject: [PATCH 6/7] Apply suggestions from code review. Co-authored-by: Dave Longley --- lib/BedrockKeystoreConfigStorage.js | 6 +++--- lib/http.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/BedrockKeystoreConfigStorage.js b/lib/BedrockKeystoreConfigStorage.js index f678436..af71ccc 100644 --- a/lib/BedrockKeystoreConfigStorage.js +++ b/lib/BedrockKeystoreConfigStorage.js @@ -36,10 +36,10 @@ export class BedrockKeystoreConfigStorage extends KeystoreConfigStorage { async getAll({ controller, req, query = {}, options = {}, - explain = false, returnRecord = false + returnRecord = false } = {}) { const records = await keystores.find({ - controller, query, options, explain + controller, query, options }); const keystoreConfigs = records.map(r => r.config); // skip request checks if specifically requested @@ -47,7 +47,7 @@ export class BedrockKeystoreConfigStorage extends KeystoreConfigStorage { return returnRecord ? records : keystoreConfigs; } // verify that request is from an IP that is allowed to access the config - for(let i = 0; i < keystoreConfigs.length; i++) { + for(let i = 0; i < keystoreConfigs.length; ++i) { const keystoreConfig = keystoreConfigs[i]; const {verified} = helpers.verifyRequestIp({keystoreConfig, req}); if(!verified) { diff --git a/lib/http.js b/lib/http.js index 7375891..c4a64fa 100644 --- a/lib/http.js +++ b/lib/http.js @@ -76,7 +76,7 @@ bedrock.events.on('bedrock-express.configure.routes', app => { }), asyncHandler(async (req, res) => { const controller = req.query.controller; - const options = {projection: {_id: 0, config: 1}}; + const options = {projection: {_id: 0, config: 1}, limit: 100}; const results = await storage.getAll({controller, req, options}); res.json({results}); })); From 3b20442a2c6b8c38f79e894450c7d427854fd1c0 Mon Sep 17 00:00:00 2001 From: James Easter Date: Fri, 21 Nov 2025 13:10:07 -0700 Subject: [PATCH 7/7] Code cleanup and changelog entry. --- CHANGELOG.md | 5 +++++ lib/BedrockKeystoreConfigStorage.js | 7 ++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37b1ad2..b78234d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # bedrock-kms-http ChangeLog +## 23.2.0 - 2025-11-dd + +### Changed +- Include route for fetching all keystore configs for a given root controller. + ## 23.0.0 - 2025-10-14 ### Changed diff --git a/lib/BedrockKeystoreConfigStorage.js b/lib/BedrockKeystoreConfigStorage.js index af71ccc..995a5b0 100644 --- a/lib/BedrockKeystoreConfigStorage.js +++ b/lib/BedrockKeystoreConfigStorage.js @@ -35,12 +35,9 @@ export class BedrockKeystoreConfigStorage extends KeystoreConfigStorage { } async getAll({ - controller, req, query = {}, options = {}, - returnRecord = false + controller, req, query = {}, options = {}, returnRecord = false } = {}) { - const records = await keystores.find({ - controller, query, options - }); + const records = await keystores.find({controller, query, options}); const keystoreConfigs = records.map(r => r.config); // skip request checks if specifically requested if(req === false) {