Skip to content

Commit

Permalink
[FAB-5974] NodeSDK - merge setUsers methods
Browse files Browse the repository at this point in the history
Have the setUserContext() method automatically
switch to using the network configuration settings
when a username and password are passed rather than
a user context object and remove the setUserFromConfig()
method. Updated network configuration testcase to
use the getEventHub(name) method.

Change-Id: I80c5ef403f5f2aa160ba8b65e91b25dc4112dbd6
Signed-off-by: Bret Harrison <[email protected]>
  • Loading branch information
harrisob committed Sep 8, 2017
1 parent 98d3063 commit 13aa7a9
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 48 deletions.
104 changes: 62 additions & 42 deletions fabric-client/lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -1025,39 +1025,36 @@ var Client = class extends BaseClient {
this.setAdminSigningIdentity(admin_key, admin_cert, mspid);
}
}
/**

/*
* Utility Method
* Sets the user context based on the passed in username and password
* and the organization in the client section of the network configuration
* settings.
*
* @param {string} username - username of the user
* @param {string} password - password of the user
* @param {boolean} clear_existing_user - to clear out any current
* user context of this client. The default will
* be to use the current user if they have the same
* username.
* @param {Object} opts - contains
* - username [required] - username of the user
* - password [optional] - password of the user
*/
setUserFromConfig(username, password, clear_existing_user) {
_setUserFromConfig(opts) {
var mspid = null;
if (typeof username === 'undefined' || username === null || username === '') {
if (!opts || typeof opts.username === 'undefined' || opts.username === null || opts.username === '') {
return Promise.reject( new Error('Missing parameter. Must have a username.'));
}
if(!this._network_config || !this._stateStore || !this._cryptoSuite ) {
return Promise.reject(new Error('Client requires a network configuration loaded, stores attached, and crypto suite.'));
}
if(clear_existing_user) {
this._userContext = null;
}
this._userContext = null;
var self = this;
return self.getUserContext(username, true)
return self.getUserContext(opts.username, true)
.then((user) => {
return new Promise((resolve, reject) => {
if (user && user.isEnrolled()) {
logger.debug('Successfully loaded member from persistence');
return resolve(user);
}

if (typeof password === 'undefined' || password === null || password === '') {
if (typeof opts.password === 'undefined' || opts. password === null || opts. password === '') {
return reject( new Error('Missing parameter. Must have a password.'));
}

Expand Down Expand Up @@ -1086,24 +1083,20 @@ var Client = class extends BaseClient {

let ca_service_class = Client.getConfigSetting('certificate-authority-client');
var ca_service_impl = require(ca_service_class);
var ca_service = new ca_service_impl(ca_url, tls_options, ca_name, crypto_suite);

var member = new User(username);
var crypto_suite = self.getCryptoSuite();
member.setCryptoSuite(crypto_suite);
var ca_service = new ca_service_impl(ca_url, tls_options, ca_name, self._crypto_suite);

return ca_service.enroll({
enrollmentID: username,
enrollmentSecret: password
enrollmentID: opts.username,
enrollmentSecret: opts.password
}).then((enrollment) => {
logger.debug('Successfully enrolled user \'' + username + '\'');

return member.setEnrollment(enrollment.key, enrollment.certificate, mspid);
}).then(() => {

return self.setUserContext(member, false);
}).then(() => {

logger.debug('Successfully enrolled user \'' + opts.username + '\'');

return self.createUser(
{username: opts.username,
mspid: mspid,
cryptoContent: { privateKeyPEM: enrollment.key.toBytes(), signedCertPEM: enrollment.certificate }
});
}).then((member) => {
return resolve(member);
}).catch((err) => {
logger.error('Failed to enroll and persist user. Error: ' + err.stack ? err.stack : err);
Expand Down Expand Up @@ -1154,6 +1147,15 @@ var Client = class extends BaseClient {
}
});
}
/**
* An alternate object to use on the 'setUserContext' call in place of the 'User' object.
* When using this object it is assumed that the current 'Client' instance has been loaded
* with a network configuration.
*
* @typedef {Object} UserNamePasswordObject
* @property {string} username - A string representing the user name of the user
* @property {string} password - A string repsesenting the password of the user
*/

/**
* Sets an instance of the {@link User} class as the security context of this client instance. This user’s
Expand All @@ -1164,29 +1166,47 @@ var Client = class extends BaseClient {
* has been set on the Client instance. If no state store has been set, this cache will not be established
* and the application is responsible for setting the user context again if the application crashes and is recovered.
*
* @param {User} user - An instance of the User class encapsulating the authenticated user’s signing materials
* (private key and enrollment certificate)
* @param {User | UserNamePasswordObject} user - An instance of the User class encapsulating the authenticated
* user’s signing materials (private key and enrollment certificate).
* The parameter may also be a {@link UserNamePasswordObject} that contains the username
* and optionaly the password. A network configuration must has been loaded to use the
* {@link UserNamePasswordObject} which will also create the user context and set it on
* this client instance. The created user context will be based on the current network
* configuration( i.e. the current organization's CA, current persistence stores).
* @param {boolean} skipPersistence - Whether to skip saving the user object into persistence. Default is false and the
* method will attempt to save the user object to the state store.
* method will attempt to save the user object to the state store. When using a
* network configuration and {@link UserNamePasswordObject}, the user object will
* always be stored to the persistence store.
* @returns {Promise} Promise of the 'user' object upon successful persistence of the user to the state store
*/
setUserContext(user, skipPersistence) {
logger.debug('setUserContext, user: ' + user + ', skipPersistence: ' + skipPersistence);
logger.debug('setUserContext - user: ' + user + ', skipPersistence: ' + skipPersistence);
var self = this;
return new Promise((resolve, reject) => {
if (user) {
self._userContext = user;
if (!skipPersistence) {
logger.debug('setUserContext begin promise to saveUserToStateStore');
self.saveUserToStateStore()
.then((user) => {
if(user.constructor && user.constructor.name === 'User') {
self._userContext = user;
if (!skipPersistence) {
logger.debug('setUserContext - begin promise to saveUserToStateStore');
self.saveUserToStateStore()
.then((return_user) => {
return resolve(return_user);
}).catch((err) => {
reject(err);
});
} else {
logger.debug('setUserContext - resolved user');
return resolve(user);
}
} else {
// must be they have passed in an object
logger.debug('setUserContext - will try to use network configuration to set the user');
self._setUserFromConfig(user)
.then((return_user) => {
return resolve(return_user);
}).catch((err) => {
reject(err);
});
} else {
logger.debug('setUserContext, resolved user');
return resolve(user);
}
} else {
logger.debug('setUserContext, Cannot save null userContext');
Expand Down Expand Up @@ -1362,7 +1382,7 @@ var Client = class extends BaseClient {
* @returns {Promise} Promise for the user object.
*/
createUser(opts) {
logger.debug('opts = %s', JSON.stringify(opts));
logger.debug('opts = %j', opts);
if (!opts) {
return Promise.reject(new Error('Client.createUser missing required \'opts\' parameter.'));
}
Expand Down
63 changes: 60 additions & 3 deletions test/integration/network-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ var testUtil = require('../unit/util.js');
var channel_name = 'mychannel2';

test('\n\n***** use the network configuration file *****\n\n', function(t) {
var memoryUsage = process.memoryUsage();
logger.debug(' Memory usage :: %j',memoryUsage);
testUtil.resetDefaults();
Client.setConfigSetting('request-timeout', 60000);

Expand Down Expand Up @@ -89,6 +91,9 @@ test('\n\n***** use the network configuration file *****\n\n', function(t) {
}).then((nothing) =>{
t.pass('Successfully set the stores for org2');

var memoryUsage = process.memoryUsage();
logger.debug(' Memory usage :: %j',memoryUsage);

// sign the config by admin from org2
var signature = client.signChannelConfig(config);
t.pass('Successfully signed config update for org2');
Expand Down Expand Up @@ -200,6 +205,9 @@ test('\n\n***** use the network configuration file *****\n\n', function(t) {
}).then(()=>{
t.pass('Successfully waited for peers to join the channel');

var memoryUsage = process.memoryUsage();
logger.debug(' Memory usage :: %j',memoryUsage);

process.env.GOPATH = path.join(__dirname, '../fixtures');
let tx_id = client.newTransactionID(true);
// send proposal to endorser
Expand Down Expand Up @@ -298,10 +306,13 @@ test('\n\n***** use the network configuration file *****\n\n', function(t) {
}).then((results) => {
t.pass('Successfully waited for chaincodes to startup');

var memoryUsage = process.memoryUsage();
logger.debug(' Memory usage :: %j',memoryUsage);

/*
* S T A R T U S I N G
*/
return client.setUserFromConfig('admin', 'adminpw', true);
return client.setUserContext({username:'admin', password:'adminpw'});
}).then((admin) => {
t.pass('Successfully enrolled user \'admin\' for org2');

Expand Down Expand Up @@ -342,17 +353,57 @@ test('\n\n***** use the network configuration file *****\n\n', function(t) {
admin : true
};

return channel.sendTransaction(request); //logged in as org2 user
var promises = [];
promises.push(channel.sendTransaction(request));

// be sure to get an eventhub the current user is authorized to use
var eventhub = client.getEventHub('peer0.org2.example.com');
eventhub.connect();

let txPromise = new Promise((resolve, reject) => {
let handle = setTimeout(() => {
eventhub.disconnect();
t.fail('REQUEST_TIMEOUT --- eventhub did not report back');
reject(new Error('REQUEST_TIMEOUT:' + eventhub._ep._endpoint.addr));
}, 30000);

eventhub.registerTxEvent(query_tx_id, (tx, code) => {
clearTimeout(handle);
eventhub.disconnect();

if (code !== 'VALID') {
t.fail('transaction was invalid, code = ' + code);
reject(new Error('INVALID:' + code));
} else {
t.pass('transaction has been committed on peer ' + eventhub._ep._endpoint.addr);
resolve();
}
});
});
promises.push(txPromise);

return Promise.all(promises);
}).then((results) => {
return results[0]; // the first returned value is from the 'sendTransaction()' call
}).then((response) => {
if (!(response instanceof Error) && response.status === 'SUCCESS') {
t.pass('Successfully sent transaction to invoke the chaincode to the orderer.');

return sleep(3000); // use sleep until the eventhub is integrated into the network config changes
return;
} else {
t.fail('Failed to order the transaction to invoke the chaincode. Error code: ' + response.status);
throw new Error('Failed to order the transaction to invoke the chaincode. Error code: ' + response.status);
}
}).then((results) => {
t.pass('Successfully moved to take place');

// check that we can get the user again without password
// also verifies that we can get a complete user properly stored
// when using a network config
return client.setUserContext({username:'admin'});
}).then((admin) => {
t.pass('Successfully loaded user \'admin\' from store for org2');

var request = {
chaincodeId : 'example',
fcn: 'query',
Expand All @@ -373,6 +424,9 @@ test('\n\n***** use the network configuration file *****\n\n', function(t) {
throw new Error('Failed to get response on query');
}

var memoryUsage = process.memoryUsage();
logger.debug(' Memory usage :: %j',memoryUsage);

return client.queryChannels('peer0.org2.example.com');
}).then((results) => {
logger.debug(' queryChannels ::%j',results);
Expand Down Expand Up @@ -467,6 +521,9 @@ test('\n\n***** use the network configuration file *****\n\n', function(t) {

return true;
}).then((results) => {
var memoryUsage = process.memoryUsage();
logger.debug(' Memory usage :: %j',memoryUsage);

t.end();
}).catch((error) =>{
logger.error('catch network config test error:: %s', error.stack ? error.stack : error);
Expand Down
6 changes: 3 additions & 3 deletions test/unit/network-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ test('\n\n ** configuration testing **\n\n', function (t) {
var p2 = clientp2.initCredentialStores().then(()=> {
t.pass('Should be able to load the stores from the config');
clientp2._network_config._network_config.client = {};
return clientp2.setUserFromConfig();
return clientp2._setUserFromConfig();
}).then((user)=>{
t.fail('Should not be able to load an user based on the config');
}).catch(function (err) {
Expand All @@ -741,7 +741,7 @@ test('\n\n ** configuration testing **\n\n', function (t) {
var p3 = clientp3.initCredentialStores().then(()=> {
t.pass('Should be able to load the stores from the config');
clientp4._network_config._network_config.client = {};
return clientp3.setUserFromConfig('username');
return clientp3._setUserFromConfig({username:'username'});
}).then((user)=>{
t.fail('Should not be able to load an user based on the config');
}).catch(function (err) {
Expand All @@ -755,7 +755,7 @@ test('\n\n ** configuration testing **\n\n', function (t) {

var clientp4 = Client.loadFromConfig('test/fixtures/network.yaml');
t.pass('Successfully loaded a network configuration');
var p4 = clientp4.setUserFromConfig('username', 'password').then(()=> {
var p4 = clientp4._setUserFromConfig({username:'username', password:'password'}).then(()=> {
t.fail('Should not be able to load an user based on the config');
}).catch(function (err) {
if (err.message.indexOf('Client requires a network configuration loaded, stores attached, and crypto suite.') >= 0) {
Expand Down

0 comments on commit 13aa7a9

Please sign in to comment.