From b9106810db11a2af19c8c06d6be39d2648f96fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Augusto?= Date: Sun, 8 Jan 2023 18:57:20 +0000 Subject: [PATCH] feat(fabric-test-ledger): add support to enrolling users in different Orgs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created new methods to avoid breaking changes in the API exported New methods created: * getConnectionProfileOrgX * enrollAdminV2 * enrollUserV2 * createCaClientV2 closes #2248 Co-authored-by: Peter Somogyvari Signed-off-by: André Augusto Signed-off-by: Peter Somogyvari --- .../fabric-v2-2-x/add-orgs.test.ts | 16 +- .../fabric-watch-blocks-v1-endpoint.test.ts | 7 +- .../fabric-v2-2-x/obtain-profiles.test.ts | 14 +- .../fabric/fabric-test-ledger-v1.ts | 155 +++++++++++++----- 4 files changed, 135 insertions(+), 57 deletions(-) diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/add-orgs.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/add-orgs.test.ts index 5ca6e6e074..756d56c4ae 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/add-orgs.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/add-orgs.test.ts @@ -3,6 +3,9 @@ import test, { Test } from "tape-promise/tape"; import { + DEFAULT_FABRIC_2_AIO_FABRIC_VERSION, + DEFAULT_FABRIC_2_AIO_IMAGE_NAME, + DEFAULT_FABRIC_2_AIO_IMAGE_VERSION, FabricTestLedgerV1, pruneDockerAllIfGithubAction, } from "@hyperledger/cactus-test-tooling"; @@ -18,7 +21,6 @@ const testCase = "adds org4 to the network"; const logLevel: LogLevelDesc = "TRACE"; test.skip("BEFORE " + testCase, async (t: Test) => { - t.skip(); const pruning = pruneDockerAllIfGithubAction({ logLevel }); await t.doesNotReject(pruning, "Pruning did not throw OK"); t.end(); @@ -38,9 +40,10 @@ test.skip(testCase, async (t: Test) => { const ledger = new FabricTestLedgerV1({ emitContainerLogs: true, publishAllPorts: true, - logLevel: "debug", - imageName: "ghcr.io/hyperledger/cactus-fabric2-all-in-one", - envVars: new Map([["FABRIC_VERSION", "2.2.0"]]), + logLevel, + imageName: DEFAULT_FABRIC_2_AIO_IMAGE_NAME, + imageVersion: DEFAULT_FABRIC_2_AIO_IMAGE_VERSION, + envVars: new Map([["FABRIC_VERSION", DEFAULT_FABRIC_2_AIO_FABRIC_VERSION]]), extraOrgs: [extraOrg], }); @@ -88,12 +91,9 @@ test.skip(testCase, async (t: Test) => { ); //Should return error, as there is no org101 in the default deployment of Fabric AIO image nor it was added - const error = "/Error.*/"; - + const error = "/no such container - Could not find the file.*/"; await t.rejects(ledger.getConnectionProfileOrgX("org101"), error); - //Let us add org 3 and retrieve the connection profile - t.end(); }); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/fabric-watch-blocks-v1-endpoint.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/fabric-watch-blocks-v1-endpoint.test.ts index 9840b1ca04..632029c43b 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/fabric-watch-blocks-v1-endpoint.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/fabric-watch-blocks-v1-endpoint.test.ts @@ -60,8 +60,8 @@ const ledgerChannelName = "mychannel"; const ledgerContractName = "basic"; // Log settings -const testLogLevel: LogLevelDesc = "info"; // default: info -const sutLogLevel: LogLevelDesc = "info"; // default: info +const testLogLevel: LogLevelDesc = "TRACE"; // default: info +const sutLogLevel: LogLevelDesc = "TRACE"; // default: info // Logger setup const log: Logger = LoggerProvider.getOrCreate({ @@ -108,12 +108,15 @@ describe("watchBlocksV1 of fabric connector tests", () => { // Get connection profile log.info("Get fabric connection profile for Org1..."); const connectionProfile = await ledger.getConnectionProfileOrg1(); + log.debug("Fabric connection profile for Org1 OK: %o", connectionProfile); expect(connectionProfile).toBeTruthy(); // Enroll admin and user const enrollAdminOut = await ledger.enrollAdmin(); + log.debug("Enrolled admin OK."); const adminWallet = enrollAdminOut[1]; const [userIdentity] = await ledger.enrollUser(adminWallet); + log.debug("Enrolled user OK."); // Create Keychain Plugin const keychainId = uuidv4(); diff --git a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/obtain-profiles.test.ts b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/obtain-profiles.test.ts index 776e4895f8..08ad4d3727 100644 --- a/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/obtain-profiles.test.ts +++ b/packages/cactus-plugin-ledger-connector-fabric/src/test/typescript/integration/fabric-v2-2-x/obtain-profiles.test.ts @@ -15,13 +15,13 @@ import { LogLevelDesc } from "@hyperledger/cactus-common"; const testCase = "obtains configuration profiles from Fabric 2.x ledger"; const logLevel: LogLevelDesc = "TRACE"; -test.skip("BEFORE " + testCase, async (t: Test) => { +test("BEFORE " + testCase, async (t: Test) => { const pruning = pruneDockerAllIfGithubAction({ logLevel }); await t.doesNotReject(pruning, "Pruning did not throw OK"); t.end(); }); -test.skip(testCase, async (t: Test) => { +test(testCase, async (t: Test) => { const ledger = new FabricTestLedgerV1({ emitContainerLogs: true, publishAllPorts: true, @@ -42,10 +42,10 @@ test.skip(testCase, async (t: Test) => { t.ok(connectionProfile, "getConnectionProfileOrg1() out truthy OK"); - const connectionProfileOrg1 = await ledger.getConnectionProfileOrgX("1"); + const connectionProfileOrg1 = await ledger.getConnectionProfileOrgX("org1"); t.isEquivalent(connectionProfile, connectionProfileOrg1); - const connectionProfileOrg2 = await ledger.getConnectionProfileOrgX("2"); + const connectionProfileOrg2 = await ledger.getConnectionProfileOrgX("org2"); t.ok(connectionProfileOrg2, "getConnectionProfileOrg2() out truthy OK"); t.notDeepEqual( @@ -55,13 +55,13 @@ test.skip(testCase, async (t: Test) => { ); //Should return error, as there is no Org3 in the default deployment of Fabric AIO image - const error = "/Error.*/"; - await t.rejects(ledger.getConnectionProfileOrgX("3"), error); + const error = "/no such container - Could not find the file.*/"; + await t.rejects(ledger.getConnectionProfileOrgX("org3"), error); t.end(); }); -test.skip("AFTER " + testCase, async (t: Test) => { +test("AFTER " + testCase, async (t: Test) => { const pruning = pruneDockerAllIfGithubAction({ logLevel }); await t.doesNotReject(pruning, "Pruning did not throw OK"); t.end(); diff --git a/packages/cactus-test-tooling/src/main/typescript/fabric/fabric-test-ledger-v1.ts b/packages/cactus-test-tooling/src/main/typescript/fabric/fabric-test-ledger-v1.ts index e1e4abd5c0..f0095ba8fe 100644 --- a/packages/cactus-test-tooling/src/main/typescript/fabric/fabric-test-ledger-v1.ts +++ b/packages/cactus-test-tooling/src/main/typescript/fabric/fabric-test-ledger-v1.ts @@ -21,6 +21,7 @@ import { LogLevelDesc, LoggerProvider, Bools, + safeStringifyException, } from "@hyperledger/cactus-common"; import Dockerode from "dockerode"; import { @@ -33,6 +34,7 @@ import path from "path"; import fs from "fs"; import yaml from "js-yaml"; import { envMapToDocker } from "../common/env-map-to-docker"; +import { RuntimeError } from "run-time-error"; export interface organizationDefinitionFabricV2 { path: string; @@ -43,6 +45,12 @@ export interface organizationDefinitionFabricV2 { port: string; } +export interface EnrollFabricIdentityOptionsV1 { + readonly wallet: Wallet; + readonly enrollmentID: string; + readonly organization: string; +} + /* * Contains options for Fabric container */ @@ -178,33 +186,59 @@ export class FabricTestLedgerV1 implements ITestLedger { return `${this.envVars.get("FABRIC_VERSION")}`; } + public capitalizedMspIdOfOrg(organization: string): string { + return organization.charAt(0).toUpperCase() + organization.slice(1) + "MSP"; + } + public getDefaultMspId(): string { return "Org1MSP"; } - public async createCaClient(): Promise { - const fnTag = `${this.className}#createCaClient()`; + public async createCaClientV2( + organization: string, + ): Promise { + const fnTag = `${this.className}#createCaClientV2()`; + this.log.debug(`${fnTag} ENTER`); try { - const ccp = await this.getConnectionProfileOrg1(); - const caInfo = ccp.certificateAuthorities["ca.org1.example.com"]; + const ccp = await this.getConnectionProfileOrgX(organization); + const caInfo = + ccp.certificateAuthorities["ca." + organization + ".example.com"]; const { tlsCACerts, url: caUrl, caName } = caInfo; const { pem: caTLSCACertPem } = tlsCACerts; const tlsOptions = { trustedRoots: caTLSCACertPem, verify: false }; - this.log.debug(`createCaClient() caName=%o caUrl=%o`, caName, caUrl); - this.log.debug(`createCaClient() tlsOptions=%o`, tlsOptions); + this.log.debug(`createCaClientV2() caName=%o caUrl=%o`, caName, caUrl); + this.log.debug(`createCaClientV2() tlsOptions=%o`, tlsOptions); return new FabricCAServices(caUrl, tlsOptions, caName); + } catch (ex) { + this.log.error(`createCaClientV2() Failure:`, ex); + throw new RuntimeError(`${fnTag} Inner Exception:`, ex); + } + } + + public async createCaClient(): Promise { + const fnTag = `${this.className}#createCaClient()`; + try { + return this.createCaClientV2("org1"); } catch (ex) { this.log.error(`createCaClient() Failure:`, ex); - throw new Error(`${fnTag} Inner Exception: ${ex}`); + throw new RuntimeError(`${fnTag} Inner Exception:`, ex); } } - public async enrollUser(wallet: Wallet): Promise { - const fnTag = `${this.className}#enrollUser()`; + public async enrollUserV2(opts: EnrollFabricIdentityOptionsV1): Promise { + const fnTag = `${this.className}#enrollUserV2()`; + + Checks.truthy(opts, "enrollUserV2 opts"); + Checks.nonBlankString(opts.organization, "enrollUserV2 opts.organization"); + Checks.nonBlankString(opts.enrollmentID, "enrollUserV2 opts.enrollmentID"); + Checks.truthy(opts.wallet, "enrollUserV2 opts.wallet"); + + const { enrollmentID, organization, wallet } = opts; try { - const mspId = this.getDefaultMspId(); - const enrollmentID = "user"; - const connectionProfile = await this.getConnectionProfileOrg1(); + const mspId = this.capitalizedMspIdOfOrg(organization); + const connectionProfile = await this.getConnectionProfileOrgX( + organization, + ); // Create a new gateway for connecting to our peer node. const gateway = new Gateway(); const discovery = { enabled: true, asLocalhost: true }; @@ -217,17 +251,17 @@ export class FabricTestLedgerV1 implements ITestLedger { // Get the CA client object from the gateway for interacting with the CA. // const ca = gateway.getClient().getCertificateAuthority(); - const ca = await this.createCaClient(); + const ca = await this.createCaClientV2(opts.organization); const adminIdentity = gateway.getIdentity(); // Register the user, enroll the user, and import the new identity into the wallet. const registrationRequest = { - affiliation: "org1.department1", - enrollmentID, + affiliation: opts.organization + ".department1", + enrollmentID: opts.enrollmentID, role: "client", }; - const provider = wallet + const provider = opts.wallet .getProviderRegistry() .getProvider(adminIdentity.type); const adminUser = await provider.getUserContext(adminIdentity, "admin"); @@ -255,8 +289,21 @@ export class FabricTestLedgerV1 implements ITestLedger { return [x509Identity, wallet]; } catch (ex) { - this.log.error(`enrollUser() Failure:`, ex); - throw new Error(`${fnTag} Exception: ${ex}`); + this.log.error(`${fnTag} failed with inner exception:`, ex); + throw new RuntimeError(`${fnTag} failed with inner exception:`, ex); + } + } + + public async enrollUser(wallet: Wallet): Promise { + const fnTag = `${this.className}#enrollUser()`; + try { + const enrollmentID = "user"; + const opts = { enrollmentID, organization: "org1", wallet }; + const out = await this.enrollUserV2(opts); + return out; + } catch (ex) { + this.log.error(`${fnTag} failed with inner exception:`, ex); + throw new RuntimeError(`${fnTag} failed with inner exception:`, ex); } } @@ -267,10 +314,20 @@ export class FabricTestLedgerV1 implements ITestLedger { return ["admin", "adminpw"]; } - public async enrollAdmin(): Promise<[X509Identity, Wallet]> { - const fnTag = `${this.className}#enrollAdmin()`; + public async enrollAdminV2( + opts: Partial, + ): Promise<[X509Identity, Wallet]> { + const fnTag = `${this.className}#enrollAdminV2()`; + this.log.debug(`${fnTag} ENTER`); + + const { organization } = opts; + if (!organization) { + throw new RuntimeError(`${fnTag} opts.organization cannot be falsy.`); + } + Checks.nonBlankString(organization, `${fnTag}:opts.organization`); + try { - const ca = await this.createCaClient(); + const ca = await this.createCaClientV2(organization); const wallet = await Wallets.newInMemoryWallet(); // Enroll the admin user, and import the new identity into the wallet. @@ -280,7 +337,7 @@ export class FabricTestLedgerV1 implements ITestLedger { }; const enrollment = await ca.enroll(request); - const mspId = this.getDefaultMspId(); + const mspId = this.capitalizedMspIdOfOrg(organization); const { certificate, key } = enrollment; const keyBytes = key.toBytes(); @@ -296,8 +353,19 @@ export class FabricTestLedgerV1 implements ITestLedger { await wallet.put("admin", x509Identity); return [x509Identity, wallet]; } catch (ex) { - this.log.error(`enrollAdmin() Failure:`, ex); - throw new Error(`${fnTag} Exception: ${ex}`); + this.log.error(`${fnTag} Failure:`, ex); + throw new RuntimeError(`${fnTag} Exception:`, ex); + } + } + + public async enrollAdmin(): Promise<[X509Identity, Wallet]> { + const fnTag = `${this.className}#enrollAdmin()`; + try { + const out = await this.enrollAdminV2({ organization: "org1" }); + return out; + } catch (ex) { + this.log.error(`${fnTag} Failure:`, ex); + throw new RuntimeError(`${fnTag} Exception:`, ex); } } @@ -405,27 +473,30 @@ export class FabricTestLedgerV1 implements ITestLedger { return ccp; } - public async getConnectionProfileOrgX(OrgName: string): Promise { + public async getConnectionProfileOrgX(orgName: string): Promise { + const fnTag = `${this.className}:getConnectionProfileOrgX()`; + this.log.debug(`${fnTag} ENTER - orgName=%s`, orgName); + const connectionProfilePath = - OrgName === "org1" || OrgName === "org2" + orgName === "org1" || orgName === "org2" ? path.join( "fabric-samples/test-network", "organizations/peerOrganizations", - OrgName + ".example.com", - "connection-" + OrgName + ".json", + orgName + ".example.com", + "connection-" + orgName + ".json", ) : path.join( - "add-org-" + OrgName, + "add-org-" + orgName, "organizations/peerOrganizations", - OrgName + ".example.com", - "connection-" + OrgName + ".json", + orgName + ".example.com", + "connection-" + orgName + ".json", ); - const peer0Name = `peer0.${OrgName}.example.com`; - const peer1Name = `peer1.${OrgName}.example.com`; + const peer0Name = `peer0.${orgName}.example.com`; + const peer1Name = `peer1.${orgName}.example.com`; const cInfo = await this.getContainerInfo(); const container = this.getContainer(); const CCP_JSON_PATH_FABRIC_V1 = - "/fabric-samples/first-network/connection-org" + OrgName + ".json"; + "/fabric-samples/first-network/connection-org" + orgName + ".json"; const CCP_JSON_PATH_FABRIC_V2 = connectionProfilePath; const ccpJsonPath = compareVersions.compare( this.getFabricVersion(), @@ -435,7 +506,10 @@ export class FabricTestLedgerV1 implements ITestLedger { ? CCP_JSON_PATH_FABRIC_V1 : CCP_JSON_PATH_FABRIC_V2; try { + const cId = container.id; + this.log.debug(`${fnTag} Pull Fabric CP %s :: %s`, cId, ccpJsonPath); const ccpJson = await Containers.pullFile(container, ccpJsonPath); + this.log.debug(`${fnTag} Got Fabric CP %s :: %s OK`, cId, ccpJsonPath); const ccp = JSON.parse(ccpJson); // Treat peer0 @@ -446,7 +520,7 @@ export class FabricTestLedgerV1 implements ITestLedger { ccp["peers"][peer0Name]["url"] = `grpcs://localhost:${hostPort}`; // if there is a peer1 - if (ccp.peers["peer1.org" + OrgName + ".example.com"]) { + if (ccp.peers["peer1.org" + orgName + ".example.com"]) { const urlGrpcs = ccp["peers"][peer1Name]["url"]; const privatePortPeer1 = parseFloat(urlGrpcs.replace(/^\D+/g, "")); @@ -458,7 +532,7 @@ export class FabricTestLedgerV1 implements ITestLedger { } { // ca_peerOrg1 - const caName = `ca.${OrgName}.example.com`; + const caName = `ca.${orgName}.example.com`; const urlGrpcs = ccp["certificateAuthorities"][caName]["url"]; const caPort = parseFloat(urlGrpcs.replace(/^\D+/g, "")); @@ -503,7 +577,7 @@ export class FabricTestLedgerV1 implements ITestLedger { }, }, }; - const specificPeer = "peer0." + OrgName + ".example.com"; + const specificPeer = "peer0." + orgName + ".example.com"; ccp.channels = { mychannel: { @@ -532,9 +606,10 @@ export class FabricTestLedgerV1 implements ITestLedger { // } } return ccp; - } catch (error) { - this.log.debug(`error on get connection profile`); - throw new Error(error as string); + } catch (ex: unknown) { + this.log.debug(`getConnectionProfileOrgX() crashed: `, ex); + const e = ex instanceof Error ? ex : safeStringifyException(ex); + throw new RuntimeError(`getConnectionProfileOrgX() crashed.`, e); } }