Skip to content
This repository has been archived by the owner on Jan 31, 2023. It is now read-only.

Commit

Permalink
Attributes (#1145)
Browse files Browse the repository at this point in the history
Signed-off-by: Jake Turner <jaketurner25@live.com>
  • Loading branch information
Jakeeyturner authored and cazfletch committed Jul 16, 2019
1 parent 493778a commit a673598
Show file tree
Hide file tree
Showing 29 changed files with 789 additions and 283 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,20 @@ The `Add Identity to Wallet` command will ask for a name, MSPID and a method to

For wallets associated with other remote Fabric gateways, the `Add Wallet`,`Edit Wallet` ,`Export Wallet` and `Remove Wallet` commands are available in the `Fabric Wallets` panel for wallet management.

### Creating an identity with attributes
Identities can be registered and enrolled with attributes from the `local_fabric` certificate authority.

The `Create Identity (register and enroll)` command will ask for an identity name and whether the identity should have any attributes added.
Selecting `Yes` will ask for the identity's attributes that should be provided in the following format:

```
[{"name": "attr1", "value": "attr1value", "ecert": true}, {"name": "attr2", "value": "attr2value", "ecert": true}]
```

The key `ecert` must be set to true in order for a smart contract to be able to read the value of the attribute using ['getAttributeValue'](https://fabric-shim.github.io/release-1.4/fabric-shim.ClientIdentity.html#getAttributeValue).

Hovering over an identity in the `Fabric Wallets` panel will show any attributes associated with the identity.

## Useful Commands
The IBM Blockchain Platform extension provides an explorer and commands accessible from the Command Palette, for developing smart contracts quickly:
<!---Table of commands with columns: 'command' and 'description'
Expand Down
14 changes: 14 additions & 0 deletions client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,20 @@ The `Add Identity to Wallet` command will ask for a name, MSPID and a method to

For wallets associated with other remote Fabric gateways, the `Add Wallet`,`Edit Wallet` ,`Export Wallet` and `Remove Wallet` commands are available in the `Fabric Wallets` panel for wallet management.

### Creating an identity with attributes
Identities can be registered and enrolled with attributes from the `local_fabric` certificate authority.

The `Create Identity (register and enroll)` command will ask for an identity name and whether the identity should have any attributes added.
Selecting `Yes` will ask for the identity's attributes that should be provided in the following format:

```
[{"name": "attr1", "value": "attr1value", "ecert": true}, {"name": "attr2", "value": "attr2value", "ecert": true}]
```

The key `ecert` must be set to true in order for a smart contract to be able to read the value of the attribute using ['getAttributeValue'](https://fabric-shim.github.io/release-1.4/fabric-shim.ClientIdentity.html#getAttributeValue).

Hovering over an identity in the `Fabric Wallets` panel will show any attributes associated with the identity.

## Useful Commands
The IBM Blockchain Platform extension provides an explorer and commands accessible from the Command Palette, for developing smart contracts quickly:
<!---Table of commands with columns: 'command' and 'description'
Expand Down
21 changes: 14 additions & 7 deletions client/cucumber/features/wallet.feature
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,22 @@ Feature: Fabric Wallets
Then there should be a tree item with a label 'local_fabric_wallet' in the 'Fabric Wallets' panel
And the tree item should have a tooltip equal to 'local_fabric_wallet'
And there should be a identity tree item with a label 'admin ⭑' in the 'Fabric Wallets' panel for item local_fabric_wallet
And the tree item should have a tooltip equal to 'admin'
And the tree item should have a tooltip equal to 'Attributes:\n\nNone'

Scenario: create an identity with attributes
Given the Local Fabric is running
And the 'Local Fabric' wallet
When I register a new identity 'attributes_user' with the attributes '[{"name": "hello", "value": "world", "ecert": true}]'
Then there should be a identity tree item with a label 'attributes_user' in the 'Fabric Wallets' panel for item local_fabric_wallet
And the tree item should have a tooltip equal to 'Attributes:\n\nhello:world\nhf.Affiliation:\nhf.EnrollmentID:attributes_user\nhf.Type:client'

@otherFabric
Scenario: create a new wallet using certs
When I create a wallet 'myWallet' using certs with identity name 'conga' and mspid 'Org1MSP'
Then there should be a tree item with a label 'myWallet' in the 'Fabric Wallets' panel
And the tree item should have a tooltip equal to 'myWallet'
And there should be a identity tree item with a label 'conga' in the 'Fabric Wallets' panel for item myWallet
And the tree item should have a tooltip equal to 'conga'
And the tree item should have a tooltip equal to 'Attributes:\n\nNone'

@otherFabric
Scenario: create a new wallet using an enrollId and secret
Expand All @@ -24,34 +31,34 @@ Feature: Fabric Wallets
Then there should be a tree item with a label 'myOtherWallet' in the 'Fabric Wallets' panel
And the tree item should have a tooltip equal to 'myOtherWallet'
And there should be a identity tree item with a label 'biscuit' in the 'Fabric Wallets' panel for item myOtherWallet
And the tree item should have a tooltip equal to 'biscuit'
And the tree item should have a tooltip equal to 'Attributes:\n\nNone'

@otherFabric
Scenario: add a new identity using a JSON file
Given the wallet 'myOtherWallet' with identity 'biscuit' and mspid 'Org1MSP' exists
When I create an identity using JSON file with identity name 'secondBiscuit' and mspid 'Org1MSP' in wallet 'myOtherWallet'
Then there should be a identity tree item with a label 'secondBiscuit' in the 'Fabric Wallets' panel for item myOtherWallet
And the tree item should have a tooltip equal to 'secondBiscuit'
And the tree item should have a tooltip equal to 'Attributes:\n\nNone'

@otherFabric
Scenario: create a new wallet using a JSON file
When I create a wallet 'myWalletyWallet' using JSON file with identity name 'jason' and mspid 'Org1MSP'
Then there should be a tree item with a label 'myWalletyWallet' in the 'Fabric Wallets' panel
And the tree item should have a tooltip equal to 'myWalletyWallet'
And there should be a identity tree item with a label 'jason' in the 'Fabric Wallets' panel for item myWalletyWallet
And the tree item should have a tooltip equal to 'jason'
And the tree item should have a tooltip equal to 'Attributes:\n\nNone'

@otherFabric
Scenario: add a new identity using certs
Given the wallet 'myWalletyWallet' with identity 'jason' and mspid 'Org1MSP' exists
When I create an identity using certs with identity name 'jasonTwo' and mspid 'Org1MSP' in wallet 'myWalletyWallet'
Then there should be a identity tree item with a label 'jasonTwo' in the 'Fabric Wallets' panel for item myWalletyWallet
And the tree item should have a tooltip equal to 'jasonTwo'
And the tree item should have a tooltip equal to 'Attributes:\n\nNone'

@otherFabric
Scenario: add a new identity using an enrollId and secret
Given the gateway 'myGateway' is created
Given the wallet 'myWalletyWallet' with identity 'jason' and mspid 'Org1MSP' exists
When I create an identity using enrollId with identity name 'otherJason' and mspid 'Org1MSP' in wallet 'myWalletyWallet'
Then there should be a identity tree item with a label 'otherJason' in the 'Fabric Wallets' panel for item myWalletyWallet
And the tree item should have a tooltip equal to 'otherJason'
And the tree item should have a tooltip equal to 'Attributes:\n\nNone'
12 changes: 11 additions & 1 deletion client/cucumber/helpers/walletAndIdentityHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export class WalletAndIdentityHelper {
this.userInputUtilHelper = userInputUtilHelper;
}

public async createCAIdentity(name: string): Promise<void> {
public async createCAIdentity(name: string, attributes: string = '[]'): Promise<void> {
const walletEntry: FabricWalletRegistryEntry = new FabricWalletRegistryEntry();
walletEntry.name = FabricWalletUtil.LOCAL_WALLET;
walletEntry.walletPath = WalletAndIdentityHelper.localWalletPath;
Expand All @@ -57,6 +57,16 @@ export class WalletAndIdentityHelper {
if (!identityExists) {
this.userInputUtilHelper.showCertificateAuthorityQuickPickStub.withArgs('Choose certificate authority to create a new identity with').resolves('ca.org1.example.com');
this.userInputUtilHelper.inputBoxStub.withArgs('Provide a name for the identity').resolves(name);

if (attributes !== '[]') {
// The user has given us attributes
this.userInputUtilHelper.showYesNoQuickPick.withArgs('Do you want to add attributes to the identity?').resolves(UserInputUtil.YES);
this.userInputUtilHelper.inputBoxStub.withArgs(`What are the attributes for the identity? e.g. [{ "name":"hello", "value":"world", "ecert":true }]`, '[]').resolves(attributes);

} else {
this.userInputUtilHelper.showYesNoQuickPick.withArgs('Do you want to add attributes to the identity?').resolves(UserInputUtil.NO);
}

await vscode.commands.executeCommand(ExtensionCommands.CREATE_NEW_IDENTITY);
}
}
Expand Down
1 change: 1 addition & 0 deletions client/cucumber/steps/steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ module.exports = function(): any {
});

this.Then("the tree item should have a tooltip equal to '{string}'", this.timeout, async (tooltipValue: string) => {
tooltipValue = tooltipValue.replace(/\\n/g, `\n`); // Add line breaks
this.treeItem.tooltip.should.equal(tooltipValue);
});

Expand Down
8 changes: 6 additions & 2 deletions client/cucumber/steps/walletAndIdentity.steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ module.exports = function(): any {
this.identity = identity;
});

this.Given("the identity '{string}' exists", this.timeout, async (identity: string) => {
await this.walletAndIdentityHelper.createCAIdentity(identity);
this.Given(/the identity '(\S*?)'(?: with attributes )?'?(\S*?)?'? exists$/, this.timeout, async (identity: string, attributes: string) => {
await this.walletAndIdentityHelper.createCAIdentity(identity, attributes);
this.identity = identity;
});

Expand Down Expand Up @@ -87,4 +87,8 @@ module.exports = function(): any {
this.When(/^I create an identity using (certs|enrollId|JSON file) with identity name '(.*?)' and mspid '(.*?)' in wallet '(.*?)'$/, this.timeout, async (method: string, identityName: string, mspid: string, wallet: string) => {
await this.walletAndIdentityHelper.createIdentity(wallet, identityName, mspid, method);
});

this.When(/I register a new identity '(.*?)' (?:with the attributes)? '(.*?)'?$/, this.timeout, async (identity: string, attributes: string) => {
await this.walletAndIdentityHelper.createCAIdentity(identity, attributes);
});
};
10 changes: 7 additions & 3 deletions client/src/commands/UserInputUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { VSCodeBlockchainOutputAdapter } from '../logging/VSCodeBlockchainOutput
import { LogType } from '../logging/OutputAdapter';
import { FabricRuntimeManager } from '../fabric/FabricRuntimeManager';
import { FabricGatewayRegistry } from '../fabric/FabricGatewayRegistry';
import { ParsedCertificate } from '../fabric/ParsedCertificate';
import { FabricCertificate } from '../fabric/FabricCertificate';
import { FabricWalletRegistryEntry } from '../fabric/FabricWalletRegistryEntry';
import { FabricWalletRegistry } from '../fabric/FabricWalletRegistry';
import { IFabricWallet } from '../fabric/IFabricWallet';
Expand Down Expand Up @@ -780,14 +780,18 @@ export class UserInputUtil {
if (!certificatePath) {
return;
}
ParsedCertificate.validPEM(certificatePath, 'certificate');

const certificate: string = FabricCertificate.loadFileFromDisk(certificatePath);
FabricCertificate.validateCertificate(certificate);

// Get the private key file path
const privateKeyPath: string = await UserInputUtil.browse('Browse for a private key file', quickPickItems, openDialogOptions) as string;
if (!privateKeyPath) {
return;
}
ParsedCertificate.validPEM(privateKeyPath, 'private key');

const privateKey: string = FabricCertificate.loadFileFromDisk(privateKeyPath);
FabricCertificate.validatePrivateKey(privateKey);

return { certificatePath, privateKeyPath };
}
Expand Down
27 changes: 25 additions & 2 deletions client/src/commands/createNewIdentityCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { FabricRuntimeManager } from '../fabric/FabricRuntimeManager';
import { IFabricWallet } from '../fabric/IFabricWallet';
import { IFabricRuntimeConnection } from '../fabric/IFabricRuntimeConnection';
import { FabricNode } from '../fabric/FabricNode';
import { Attribute } from '../fabric/FabricCertificate';

export async function createNewIdentity(certificateAuthorityTreeItem?: CertificateAuthorityTreeItem): Promise<void> {
const outputAdapter: VSCodeBlockchainOutputAdapter = VSCodeBlockchainOutputAdapter.instance();
Expand Down Expand Up @@ -80,8 +81,23 @@ export async function createNewIdentity(certificateAuthorityTreeItem?: Certifica

const affiliation: string = ''; // Give it the same affiliation as the identity registrar

const addAttributes: any = await UserInputUtil.showQuickPickYesNo('Do you want to add attributes to the identity?');

let attributes: Attribute[];
let attributesString: string;
if (!addAttributes) {
return;
} else if (addAttributes === UserInputUtil.YES) {
attributesString = await UserInputUtil.showInputBox(`What are the attributes for the identity? e.g. [{ "name":"hello", "value":"world", "ecert":true }]`, '[]');
if (attributesString === undefined) {
return;
} else {
attributes = JSON.parse(attributesString);
}
}

// Register the user
const secret: string = await connection.register(certificateAuthorityName, identityName, affiliation);
const secret: string = await connection.register(certificateAuthorityName, identityName, affiliation, attributes);

// Enroll the user
const details: { certificate: string, privateKey: string } = await connection.enroll(certificateAuthorityName, identityName, secret);
Expand All @@ -90,7 +106,14 @@ export async function createNewIdentity(certificateAuthorityTreeItem?: Certifica
await wallet.importIdentity(details.certificate, details.privateKey, identityName, mspid);

await vscode.commands.executeCommand(ExtensionCommands.REFRESH_WALLETS);
outputAdapter.log(LogType.SUCCESS, 'Successfully added identity', `Successfully added ${identityName} to runtime gateway`);

let message: string;
if (attributes) {
message = `Successfully created identity '${identityName}' with the attributes: ${attributesString}`;
} else {
message = `Successfully created identity '${identityName}'`;
}
outputAdapter.log(LogType.SUCCESS, message);
Reporter.instance().sendTelemetryEvent('createNewIdentityCommand');
} catch (error) {
outputAdapter.log(LogType.ERROR, `Issue creating new identity: ${error.message}`, `Issue creating new identity: ${error.toString()}`);
Expand Down
9 changes: 4 additions & 5 deletions client/src/explorer/model/AdminIdentityTreeItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@
* limitations under the License.
*/
'use strict';
import { BlockchainTreeItem } from './BlockchainTreeItem';
import * as vscode from 'vscode';
import { BlockchainExplorerProvider } from '../BlockchainExplorerProvider';
import { IdentityTreeItem } from './IdentityTreeItem';

export class AdminIdentityTreeItem extends BlockchainTreeItem {
export class AdminIdentityTreeItem extends IdentityTreeItem {
contextValue: string = 'blockchain-admin-identity-item';

constructor(provider: BlockchainExplorerProvider, public readonly label: string, public readonly walletName: string) {
super(provider, label, vscode.TreeItemCollapsibleState.None);
constructor(provider: BlockchainExplorerProvider, public readonly label: string, public readonly walletName: string, public readonly attributes: any = {}) {
super(provider, label, walletName, attributes);
this.label += ' ⭑';
}
}
10 changes: 9 additions & 1 deletion client/src/explorer/model/IdentityTreeItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ import { BlockchainExplorerProvider } from '../BlockchainExplorerProvider';
export class IdentityTreeItem extends BlockchainTreeItem {
contextValue: string = 'blockchain-identity-item';

constructor(provider: BlockchainExplorerProvider, public readonly label: string, public readonly walletName: string) {
constructor(provider: BlockchainExplorerProvider, public readonly label: string, public readonly walletName: string, public readonly attributes: any = {}) {
super(provider, label, vscode.TreeItemCollapsibleState.None);
this.tooltip = `Attributes:\n`;
if (Object.keys(attributes).length > 0) {
for (const attr of Object.keys(attributes)) {
this.tooltip += `\n${attr}:${attributes[attr]}`;
}
} else {
this.tooltip += `\nNone`;
}
}
}
27 changes: 21 additions & 6 deletions client/src/explorer/walletExplorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ import { IdentityTreeItem } from './model/IdentityTreeItem';
import { IFabricWalletGenerator } from '../fabric/IFabricWalletGenerator';
import { AdminIdentityTreeItem } from './model/AdminIdentityTreeItem';
import { FabricRuntimeManager } from '../fabric/FabricRuntimeManager';
import { FabricIdentity } from '../fabric/FabricIdentity';
import { FabricCertificate, Attribute } from '../fabric/FabricCertificate';
import { FabricRuntimeUtil } from '../fabric/FabricRuntimeUtil';

export class BlockchainWalletExplorerProvider implements BlockchainExplorerProvider {

Expand Down Expand Up @@ -99,17 +100,31 @@ export class BlockchainWalletExplorerProvider implements BlockchainExplorerProvi

// Populate the tree with the identity names
const walletName: string = walletTreeItem.name;
for (const identityName of walletTreeItem.identities) {

const walletPath: string = walletTreeItem.registryEntry.walletPath;
const fabricWalletGenerator: IFabricWalletGenerator = FabricWalletGeneratorFactory.createFabricWalletGenerator();
const wallet: IFabricWallet = fabricWalletGenerator.getNewWallet(walletPath);
const identities: any[] = await wallet.getIdentities();

for (const identity of identities) {
let isAdminIdentity: boolean = false;
if (walletTreeItem instanceof LocalWalletTreeItem) {
const adminIdentities: FabricIdentity[] = await FabricRuntimeManager.instance().getRuntime().getIdentities(walletName);
isAdminIdentity = adminIdentities.some((adminIdentity: FabricIdentity): boolean => adminIdentity.name === identityName);
// Check 'admin' in local_fabric
if (identity.name === FabricRuntimeUtil.ADMIN_USER) {
isAdminIdentity = true;
}
}

// Get attributes fcn
const certificate: FabricCertificate = new FabricCertificate(identity.enrollment.identity.certificate);
const attributes: Attribute[] = certificate.getAttributes();

if (isAdminIdentity) {
// User can't delete this!
tree.push(new AdminIdentityTreeItem(this, identityName, walletName));
tree.push(new AdminIdentityTreeItem(this, identity.name, walletName, attributes));
} else {
tree.push(new IdentityTreeItem(this, identityName, walletName));

tree.push(new IdentityTreeItem(this, identity.name, walletName, attributes));
}
}
return tree;
Expand Down
Loading

0 comments on commit a673598

Please sign in to comment.