From 34656b0730f886619efbbddb512c094029cbbebd Mon Sep 17 00:00:00 2001 From: Peter Somogyvari Date: Thu, 12 Nov 2020 12:38:29 -0800 Subject: [PATCH] =?UTF-8?q?feat(core-api):=20=F0=9F=8E=B8=20add=20IKeychai?= =?UTF-8?q?nPlugin#getKeychainId()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New method on the generic interface for keychain plugins that enables the handling of multiple different keychain backends on the same API server. This is useful if you want to have different instances of keychain plugins deployed because for example your consortium member operates multiple keychain backends such as one provided by the cloud provider of their choice and another one that could be a self-hosted, open source software deployment. Cactus aims not to limit the deployment architecture where possible and this feature is aimed at maintaining that design principle. The idea is that API requests can specify which keychain they want to use when looking up the signing key for a transaction. The implementation that serves the request then reaches to the PluginRegistry and retrieves a list of keychain plugins, then proceeds to filter that list down to just the one instance based on the keychain ID that has to match up to what was specified in the API request being served. Signed-off-by: Peter Somogyvari --- .../plugin/keychain/i-plugin-keychain.ts | 14 ++++ .../typescript/plugin-factory-keychain.ts | 8 ++- .../main/typescript/plugin-keychain-memory.ts | 69 +++++++++++++------ .../src/main/typescript/public-api.ts | 5 +- 4 files changed, 71 insertions(+), 25 deletions(-) diff --git a/packages/cactus-core-api/src/main/typescript/plugin/keychain/i-plugin-keychain.ts b/packages/cactus-core-api/src/main/typescript/plugin/keychain/i-plugin-keychain.ts index ac8d2c94b8..bdfa112126 100644 --- a/packages/cactus-core-api/src/main/typescript/plugin/keychain/i-plugin-keychain.ts +++ b/packages/cactus-core-api/src/main/typescript/plugin/keychain/i-plugin-keychain.ts @@ -1,6 +1,20 @@ import { IPluginKVStorage } from "../storage/key-value/i-plugin-kv-storage"; +/** + * Common interface to be implemented by classes that act as plugins behind + * keychains. + */ export interface IPluginKeychain extends IPluginKVStorage { rotateEncryptionKeys(): Promise; getEncryptionAlgorithm(): string; + /** + * Returns the unique identifier of the keychain pointed to (or backed) by + * this `IPluginKeychain` instance. + * This therefore does not uniqely identify the plugin instance itself, but + * its backend instead. + * Useful for being able to reference keychains by their IDs in deployment + * scenarios when there are multiple keychain backends for different sets of + * secrets. + */ + getKeychainId(): string; } diff --git a/packages/cactus-plugin-keychain-memory/src/main/typescript/plugin-factory-keychain.ts b/packages/cactus-plugin-keychain-memory/src/main/typescript/plugin-factory-keychain.ts index adb0cfbec9..f05baddeb4 100644 --- a/packages/cactus-plugin-keychain-memory/src/main/typescript/plugin-factory-keychain.ts +++ b/packages/cactus-plugin-keychain-memory/src/main/typescript/plugin-factory-keychain.ts @@ -2,18 +2,20 @@ import { v4 as uuidv4 } from "uuid"; import { PluginFactory } from "@hyperledger/cactus-core-api"; import { - IPluginKeychainOptions, + IPluginKeychainMemoryOptions, PluginKeychainMemory, } from "./plugin-keychain-memory"; export class PluginFactoryKeychain extends PluginFactory< PluginKeychainMemory, - IPluginKeychainOptions + IPluginKeychainMemoryOptions > { async create( - options: IPluginKeychainOptions = { + options: IPluginKeychainMemoryOptions = { backend: new Map(), instanceId: uuidv4(), + keychainId: uuidv4(), + logLevel: "TRACE", } ): Promise { return new PluginKeychainMemory(options); diff --git a/packages/cactus-plugin-keychain-memory/src/main/typescript/plugin-keychain-memory.ts b/packages/cactus-plugin-keychain-memory/src/main/typescript/plugin-keychain-memory.ts index ee49707f36..2e46e33c45 100644 --- a/packages/cactus-plugin-keychain-memory/src/main/typescript/plugin-keychain-memory.ts +++ b/packages/cactus-plugin-keychain-memory/src/main/typescript/plugin-keychain-memory.ts @@ -1,35 +1,62 @@ import { - ICactusPlugin, + Logger, + Checks, + LogLevelDesc, + LoggerProvider, +} from "@hyperledger/cactus-common"; +import { ICactusPluginOptions, - IPluginKeychain, PluginAspect, } from "@hyperledger/cactus-core-api"; -import { Checks } from "@hyperledger/cactus-common"; - -export interface IPluginKeychainOptions extends ICactusPluginOptions { - backend: Map; +export interface IPluginKeychainMemoryOptions extends ICactusPluginOptions { + logLevel?: LogLevelDesc; + backend?: Map; + keychainId: string; } -export class PluginKeychainMemory implements ICactusPlugin, IPluginKeychain { +export class PluginKeychainMemory { + public static readonly CLASS_NAME = "PluginKeychainMemory"; + + private readonly backend: Map; + private readonly log: Logger; private readonly instanceId: string; - constructor(public readonly options: IPluginKeychainOptions) { - const fnTag = `PluginKeychainMemory#constructor()`; - if (!options) { - throw new Error(`${fnTag} options falsy.`); - } - Checks.truthy(options.instanceId, `${fnTag} options.instanceId`); - if (!options.backend) { - options.backend = new Map(); - } - this.instanceId = this.options.instanceId; + public get className() { + return PluginKeychainMemory.CLASS_NAME; + } + + constructor(public readonly opts: IPluginKeychainMemoryOptions) { + const fnTag = `${this.className}#constructor()`; + Checks.truthy(opts, `${fnTag} arg options`); + Checks.truthy(opts.keychainId, `${fnTag} arg options.keychainId`); + Checks.truthy(opts.instanceId, `${fnTag} options.instanceId`); + Checks.nonBlankString(opts.keychainId, `${fnTag} options.keychainId`); + + this.backend = opts.backend || new Map(); + Checks.truthy(this.backend, `${fnTag} arg options.backend`); + + const level = this.opts.logLevel || "INFO"; + const label = this.className; + this.log = LoggerProvider.getOrCreate({ level, label }); + + this.instanceId = this.opts.instanceId; + + this.log.info(`Created ${this.className}. KeychainID=${opts.keychainId}`); + this.log.warn( + `Never use ${this.className} in production. ` + + `It does not support encryption. It stores everything in plain text.` + ); } public getInstanceId(): string { return this.instanceId; } + public getKeychainId(): string { + return this.opts.keychainId; + } + public getPackageName(): string { return `@hyperledger/cactus-plugin-keychain-memory`; } @@ -47,18 +74,18 @@ export class PluginKeychainMemory implements ICactusPlugin, IPluginKeychain { } async get(key: string): Promise { - return this.options.backend.get(key); + return this.backend.get(key); } async has(key: string): Promise { - return this.options.backend.has(key); + return this.backend.has(key); } async set(key: string, value: T): Promise { - this.options.backend.set(key, value); + this.backend.set(key, value); } async delete(key: string): Promise { - this.options.backend.delete(key); + this.backend.delete(key); } } diff --git a/packages/cactus-plugin-keychain-memory/src/main/typescript/public-api.ts b/packages/cactus-plugin-keychain-memory/src/main/typescript/public-api.ts index b7f57b0aba..4fce4d782f 100755 --- a/packages/cactus-plugin-keychain-memory/src/main/typescript/public-api.ts +++ b/packages/cactus-plugin-keychain-memory/src/main/typescript/public-api.ts @@ -1,4 +1,7 @@ -export { PluginKeychainMemory } from "./plugin-keychain-memory"; +export { + PluginKeychainMemory, + IPluginKeychainMemoryOptions, +} from "./plugin-keychain-memory"; export { PluginFactoryKeychain } from "./plugin-factory-keychain"; import { PluginFactoryKeychain } from "./plugin-factory-keychain";