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 a72272d..995a5b0 100644 --- a/lib/BedrockKeystoreConfigStorage.js +++ b/lib/BedrockKeystoreConfigStorage.js @@ -33,4 +33,28 @@ export class BedrockKeystoreConfigStorage extends KeystoreConfigStorage { return returnRecord ? record : keystoreConfig; } + + async getAll({ + controller, req, query = {}, options = {}, returnRecord = false + } = {}) { + const records = await keystores.find({controller, query, options}); + 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; + } } diff --git a/lib/http.js b/lib/http.js index deafeed..c4a64fa 100644 --- a/lib/http.js +++ b/lib/http.js @@ -6,16 +6,18 @@ 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 {BedrockKeystoreConfigStorage} from './BedrockKeystoreConfigStorage.js'; import {generateRandom} from '@digitalbazaar/webkms-switch'; import {meters} from '@bedrock/meter-usage-reporter'; import {createValidateMiddleware as validate} from '@bedrock/validation'; @@ -41,6 +43,10 @@ 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; + + const storage = new BedrockKeystoreConfigStorage(); + // create middleware for handling KMS operations const handleOperation = middleware.createKmsOperationMiddleware(); @@ -51,6 +57,30 @@ 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 options = {projection: {_id: 0, config: 1}, limit: 100}; + const results = await storage.getAll({controller, req, options}); + res.json({results}); + })); + // 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