From 5ef917ff2f4b3f287bcb14f504b097d8839e1a6e Mon Sep 17 00:00:00 2001 From: Dave Kelsey Date: Thu, 23 Aug 2018 15:07:36 +0100 Subject: [PATCH] FABN-856 submitTransaction API Add proper support for mutual TLS Improve javadoc Change-Id: I210abfbbd8dbd94d59393af739f3085d719ad623 Signed-off-by: Dave Kelsey --- fabric-network/lib/channel.js | 13 +++++---- fabric-network/lib/network.js | 40 +++++++++++++++++--------- fabric-network/test/network.js | 18 +++++++++++- test/integration/network-e2e/invoke.js | 20 +++++++------ 4 files changed, 64 insertions(+), 27 deletions(-) diff --git a/fabric-network/lib/channel.js b/fabric-network/lib/channel.js index d092007413..652d696440 100644 --- a/fabric-network/lib/channel.js +++ b/fabric-network/lib/channel.js @@ -14,8 +14,8 @@ class Channel { /** * Channel constructor for internal use only - * @param network - * @param channel + * @param {Network} network The owning network instance + * @param {Channel} channel The fabric-client channel instance * @private */ constructor(network, channel) { @@ -107,6 +107,11 @@ class Channel { } } + /** + * Initialize this channel instance + * @private + * @memberof Channel + */ async _initialize() { logger.debug('in initialize'); @@ -140,9 +145,6 @@ class Channel { */ getContract(chaincodeId) { logger.debug('in getContract'); - - // check initialized flag - // Create the new Contract let contract = this.contracts.get(chaincodeId); if (!contract) { contract = new Contract( @@ -150,6 +152,7 @@ class Channel { chaincodeId, this.network ); + this.contracts.set(chaincodeId, contract); } return contract; } diff --git a/fabric-network/lib/network.js b/fabric-network/lib/network.js index 93882352ed..d6bf3db9e2 100644 --- a/fabric-network/lib/network.js +++ b/fabric-network/lib/network.js @@ -37,18 +37,28 @@ class Network { // default options this.options = { - commitTimeout: 300 * 1000, + commitTimeout: 300 * 1000 }; } + /** + * @typedef {Object} NetworkOptions + * @property {Wallet} wallet The identity wallet implementation for use with this network instance + * @property {string} identity The identity in the wallet for all interactions on this network instance + * @property {string} [clientTlsIdentity] the identity in the wallet to use as the client TLS identity + * @property {number} [commitTimeout = 300000] The timout period in milliseconds to wait for commit notification to complete + */ + /** * Initialize the network with a connection profile * - * @param {*} ccp - * @param {*} options + * @param {Client | string} config The configuration for this network which can come from a common connection + * profile or an existing fabric-client Client instance + * @see see {Client} + * @param {NetworkOptions} options specific options for creating this network instance * @memberof Network */ - async initialize(ccp, options) { + async initialize(config, options) { logger.debug('in initialize'); if (!options || !options.wallet) { @@ -58,14 +68,14 @@ class Network { Network._mergeOptions(this.options, options); - if (!(ccp instanceof Client)) { + if (!(config instanceof Client)) { // still use a ccp for the discovery peer and ca information logger.debug('initialize: loading client from ccp'); - this.client = Client.loadFromConfig(ccp); + this.client = Client.loadFromConfig(config); } else { // initialize from an existing Client object instance logger.debug('initialize: using existing client object'); - this.client = ccp; + this.client = config; } // setup an initial identity for the network @@ -73,12 +83,17 @@ class Network { logger.debug('initialize: setting identity'); this.currentIdentity = await options.wallet.setUserContext(this.client, options.identity); } + + if (options.clientTlsIdentity) { + const tlsIdentity = await options.wallet.export(options.clientTlsIdentity); + this.client.setTlsClientCertAndKey(tlsIdentity.certificate, tlsIdentity.privateKey); + } } /** * Get the current identity * - * @returns + * @returns {User} a fabric-client User instance of the current identity used by this network * @memberof Network */ getCurrentIdentity() { @@ -89,7 +104,7 @@ class Network { /** * Get the underlying Client object instance * - * @returns + * @returns {Client} the underlying fabric-client Client instance * @memberof Network */ getClient() { @@ -99,8 +114,8 @@ class Network { /** * Returns the set of options associated with the network connection - * @returns {{commitTimeout: number}|*} - * @memberOf Network + * @returns {NetworkOptions} the network options + * @memberof Network */ getOptions() { logger.debug('in getOptions'); @@ -124,7 +139,7 @@ class Network { * Returns an object representing the channel * @param channelName * @returns {Promise} - * @memberOf Network + * @memberof Network */ async getChannel(channelName) { logger.debug('in getChannel'); @@ -141,5 +156,4 @@ class Network { } } - module.exports = Network; diff --git a/fabric-network/test/network.js b/fabric-network/test/network.js index b24fbb2a31..b77e8751b9 100644 --- a/fabric-network/test/network.js +++ b/fabric-network/test/network.js @@ -188,6 +188,22 @@ describe('Network', () => { network.currentIdentity.should.equal('foo'); }); + it('should initialize the network with identity and set client tls crypto material', async () => { + mockWallet.export.withArgs('tlsId').resolves({certificate: 'acert', privateKey: 'akey'}); + + const options = { + wallet: mockWallet, + identity: 'admin', + clientTlsIdentity: 'tlsId' + }; + await network.initialize('ccp', options); + network.client.should.equal(mockClient); + network.currentIdentity.should.equal('foo'); + sinon.assert.calledOnce(mockClient.setTlsClientCertAndKey); + sinon.assert.calledWith(mockClient.setTlsClientCertAndKey, 'acert', 'akey'); + }); + + it('should initialize from an existing client object', async () => { const options = { wallet: mockWallet, @@ -287,4 +303,4 @@ describe('Network', () => { }); }); -}); \ No newline at end of file +}); diff --git a/test/integration/network-e2e/invoke.js b/test/integration/network-e2e/invoke.js index c58fa41fa5..e3109d856b 100644 --- a/test/integration/network-e2e/invoke.js +++ b/test/integration/network-e2e/invoke.js @@ -38,17 +38,18 @@ test('\n\n***** Network End-to-end flow: invoke transaction to move money using t.fail('Failed to import User1@org1.example.com into wallet'); } + const tlsInfo = await e2eUtils.tlsEnroll('org1'); + await inMemoryWallet.import('tlsId', X509WalletMixin.createIdentity('org1', tlsInfo.certificate, tlsInfo.key)); + const network = new Network(); const ccp = fs.readFileSync(fixtures + '/network.json'); await network.initialize(JSON.parse(ccp.toString()), { wallet: inMemoryWallet, - identity: 'User1@org1.example.com' + identity: 'User1@org1.example.com', + clientTlsIdentity: 'tlsId' }); - const tlsInfo = await e2eUtils.tlsEnroll('org1'); - network.getClient().setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); - t.pass('Initialized the network'); const channel = await network.getChannel(channelName); @@ -103,18 +104,21 @@ test('\n\n***** Network End-to-end flow: invoke transaction to move money using await fileSystemWallet.import(identityLabel, X509WalletMixin.createIdentity('Org1MSP', cert, key)); const exists = await fileSystemWallet.exists(identityLabel); t.ok(exists, 'Successfully imported User1@org1.example.com into wallet'); + const tlsInfo = await e2eUtils.tlsEnroll('org1'); + + await fileSystemWallet.import('tlsId', X509WalletMixin.createIdentity('org1', tlsInfo.certificate, tlsInfo.key)); + + const network = new Network(); const ccp = fs.readFileSync(fixtures + '/network.json'); await network.initialize(JSON.parse(ccp.toString()), { wallet: fileSystemWallet, - identity: identityLabel + identity: identityLabel, + clientTlsIdentity: 'tlsId' }); - const tlsInfo = await e2eUtils.tlsEnroll('org1'); - network.getClient().setTlsClientCertAndKey(tlsInfo.certificate, tlsInfo.key); - t.pass('Initialized the network'); const channel = await network.getChannel(channelName);