Skip to content

Commit

Permalink
Update identity issued process to fit in with new idcard (hyperledger…
Browse files Browse the repository at this point in the history
…-archives#2120)

design. There are now options to use a new idcard yourself,
export it for another playground user, or copy the identity
to the clipboard to share with command line users

Closes #1955 and #1958

Signed-off-by: James Taylor <[email protected]>
  • Loading branch information
jt-nti authored and Simon Stone committed Sep 13, 2017
1 parent 516ddbc commit 579c1cd
Show file tree
Hide file tree
Showing 23 changed files with 597 additions and 186 deletions.
1 change: 1 addition & 0 deletions packages/composer-playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"file-saver": "^1.3.3",
"is-docker": "^1.1.0",
"marked": "^0.3.6",
"ngx-clipboard": "8.0.4",
"opener": "^1.4.2",
"socket.io": "^1.7.3",
"typescript": "2.4.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<section class="identity-card">
<header>
<button *ngIf="preview" type="button" class="icon dismiss"
<button *ngIf="showDismissIcon" type="button" class="icon dismiss"
(click)="dismiss()">
<svg class="ibm-icon" aria-hidden="true">
<use xlink:href="#icon-close_new"></use>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe(`IdentityCardComponent`, () => {

describe('#dismiss', () => {
it('should emit dismiss event', (done) => {
component.preview = true;
component.showDismissIcon = true;
component.identity = mockIdCard;

component.onDismiss.subscribe((e) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export class IdentityCardComponent {
@Input()
preview: boolean = false;

@Input()
showDismissIcon: boolean = false;

@Input()
indestructible: boolean = false;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { IdentityCardComponent } from './identity-card.component';

@NgModule({
imports: [CommonModule],
entryComponents: [],
declarations: [IdentityCardComponent],
providers: [],
exports: [IdentityCardComponent]
})

export class IdentityCardModule {
}
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './identity-card.component';
export * from './identity-card.module';
Original file line number Diff line number Diff line change
Expand Up @@ -8,55 +8,70 @@ <h1>Identity Issued</h1>
</button>
</header>
<div class="modal-body">
<identity-card [identity]="newCard" [preview]="true"></identity-card>

<section class="id-option">
<header>
<dl>
<dt>Option 1</dt>
<dd>Send someone the new user ID and secret so that they can add it to their wallet</dd>
</dl>
</header>
<div class="new-card-options">
<ngb-accordion [closeOthers]="true" activeIds="option-1">
<ngb-panel id="option-1">
<ng-template ngbPanelTitle>
<svg class="bx--accordion__arrow" width="12" height="12" viewBox="0 0 8 12" fill-rule="evenodd">
<path d="M0 10.6L4.7 6 0 1.4 1.4 0l6.1 6-6.1 6z"></path>
</svg>
<span>Use it yourself</span>
</ng-template>
<ng-template ngbPanelContent>
<p>Just add the ID card to your wallet to start using the new identity yourself</p>
<div class="actions">
<button type="button" class="primary" (click)="addToWallet();">Add to wallet</button>
</div>
</ng-template>
</ngb-panel>
<ngb-panel id="option-2">
<ng-template ngbPanelTitle>
<svg class="bx--accordion__arrow" width="12" height="12" viewBox="0 0 8 12" fill-rule="evenodd">
<path d="M0 10.6L4.7 6 0 1.4 1.4 0l6.1 6-6.1 6z"></path>
</svg>
<span>Send it to someone else (Playground)</span>
</ng-template>
<ng-template ngbPanelContent>
<p>Another Playground user can import the ID card to their wallet to start using the new identity</p>
<div class="actions">
<button type="button" class="primary" (click)="export();">Export ID card</button>
</div>
</ng-template>
</ngb-panel>
<ngb-panel id="option-3">
<ng-template ngbPanelTitle>
<svg class="bx--accordion__arrow" width="12" height="12" viewBox="0 0 8 12" fill-rule="evenodd">
<path d="M0 10.6L4.7 6 0 1.4 1.4 0l6.1 6-6.1 6z"></path>
</svg>
<span>Send it to someone else (Command line)</span>
</ng-template>
<ng-template ngbPanelContent>
<p>A Composer command line user will need to enroll the new identity using the secret shown below</p>

<dl class="enrollment-credentials">
<dt>User ID</dt>
<dd>{{userID}}</dd>

<div class="id-option-details">
<dl>
<div>
<dt>User ID</dt>
<dd>{{userID}}</dd>
</div>

<div class="id-secret">
<dt>User Secret</dt>
<dd>{{userSecret}}</dd>
</div>
</dl>
</div>
</section>

<section class="id-option">
<header>
<dl>
<dt>Option 2</dt>
<dd>Use it yourself</dd>
</dl>
</header>

<div class="id-option-details">
<button type="button" class="action" (click)="addToWallet();">+ Add to my Wallet</button>
</div>
</section>
<dt>User Secret</dt>
<dd>{{userSecret}}</dd>
</dl>

<div class="actions">
<button type="button" class="primary" ngxClipboard [cbContent]="newIdentity" (cbOnSuccess)="activeModal.close()">Copy to clipboard</button>
</div>
</ng-template>
</ngb-panel>
</ngb-accordion>
</div>
</div>
<footer>
<p class="footer-text">
<svg class="standard-icon" aria-hidden="true">
<use xlink:href="#icon-warn_32"></use>
</svg>
For security, this secret will only ever be shown once.
For security, new identities can only be enrolled once
</p>
<button type="button" class="primary" (click)="activeModal.close();">
<div>
<span>Ok</span>
</div>
</button>
</footer>
</section>
Original file line number Diff line number Diff line change
Expand Up @@ -3,50 +3,42 @@

identity-issued-modal {
.identity-issued {
dt, dd {
display: inline;
margin: 0;
}

.modal-body {
margin-left: 5rem;
}
display: flex;
flex-direction: row;
overflow-y: auto;

.new-card-options {
position: absolute;
margin-left: 275px; // the size of an ID card
padding: 0 $space-medium $space-medium $space-medium;

.id-option {
margin-bottom: 1.3rem;
dl.enrollment-credentials {
margin-left: $space-medium;

header {
display: flex;
color: $primary-text;
display: flex;
flex-flow: row wrap;

dt, dd {
margin: 0;
flex: 0 0 50%;
}

dl {
dt {
position: absolute;
left: -4rem;
font-weight: 700;
flex: 0 0 33%;
}
}
}

.id-option-details {
dl {
div {
display: flex;
margin-bottom: 1.3em;

&.id-secret {
font-weight: 700;
}

dt {
flex: 1;
}

dd {
flex: 5;
}
dd {
flex: 0 0 66%;
}
}

.actions {
display: flex;
justify-content: flex-end;
margin-bottom: $space-medium;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,78 @@
/* tslint:disable:no-var-requires */
/* tslint:disable:max-classes-per-file */
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { Component, Directive, Input } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';

import { IdentityIssuedComponent } from './identity-issued.component';
import { IdentityCardService } from '../../services/identity-card.service';

import { IdCard } from 'composer-common';

import * as sinon from 'sinon';

@Directive({
selector: '[ngxClipboard]'
})
class MockClipboardDirective {
@Input() cbContent: any;
}

@Component({
selector: 'ngb-accordion',
template: ''
})
class MockAccordionComponent {
@Input() closeOthers: boolean;
}

@Component({
selector: 'ngb-panel',
template: ''
})
class MockPanelComponent {
}

@Component({
selector: 'identity-card',
template: ''
})
class MockIdentityCardComponent {
@Input() identity: any;
@Input() preview: boolean;
}

describe('IdentityIssuedComponent', () => {
let component: IdentityIssuedComponent;
let fixture: ComponentFixture<IdentityIssuedComponent>;

let mockActiveModal;
let mockIdentityCard;
let mockIdentityCardService;
let mockIdCard;
let mockConnectionProfile;

beforeEach(() => {
mockActiveModal = sinon.createStubInstance(NgbActiveModal);
mockIdentityCard = sinon.createStubInstance(IdentityCardService);
mockIdentityCardService = sinon.createStubInstance(IdentityCardService);
mockIdCard = sinon.createStubInstance(IdCard);
mockIdCard.getBusinessNetworkName.returns('dan-net');
mockConnectionProfile = {
name: 'dan-profile'
};
mockIdCard.getConnectionProfile.returns(mockConnectionProfile);
mockIdentityCardService.getCurrentIdentityCard.returns(mockIdCard);

TestBed.configureTestingModule({
declarations: [IdentityIssuedComponent],
declarations: [
IdentityIssuedComponent,
MockClipboardDirective,
MockAccordionComponent,
MockPanelComponent,
MockIdentityCardComponent
],
providers: [
{provide: NgbActiveModal, useValue: mockActiveModal},
{provide: IdentityCardService, useValue: mockIdentityCard},
{provide: IdentityCardService, useValue: mockIdentityCardService},
]
});

Expand All @@ -37,50 +86,35 @@ describe('IdentityIssuedComponent', () => {
component.should.be.ok;
});

describe('addToWallet', () => {
let mockCard;

beforeEach(() => {
mockCard = {
getConnectionProfile: sinon.stub().returns({name: 'myProfile'}),
getBusinessNetworkName: sinon.stub().returns('myNetwork')
};

mockIdentityCard.getCurrentIdentityCard.returns(mockCard);
describe('ngOnInit', () => {
it('should', fakeAsync(() => {
component.userID = 'dan';
component.userSecret = 'wotnodolphin';

mockIdentityCard.createIdentityCard.returns(Promise.resolve());

component['userID'] = 'myId';
component['userSecret'] = 'mySecret';
});

it('should add to wallet', fakeAsync(() => {
component.addToWallet();
component.ngOnInit();

tick();

mockIdentityCard.getCurrentIdentityCard.should.have.been.calledThrice;
mockCard.getConnectionProfile.should.have.been.called;
mockCard.getBusinessNetworkName.should.have.been.called;

mockIdentityCard.createIdentityCard.should.have.been.calledWith('myId', 'myNetwork', 'myId', 'mySecret', {name: 'myProfile'}, sinon.match.any, []);

mockActiveModal.close.should.have.been.called;
component['newCard'].getName().should.equal('dan');
component['newCard'].getEnrollmentCredentials().should.deep.equal({id: 'dan', secret: 'wotnodolphin'});
component['newCard'].getBusinessNetworkName().should.equal('dan-net');
component['newCard'].getConnectionProfile().should.deep.equal({name: 'dan-profile'});
}));
});

it('should handle error', fakeAsync(() => {
mockIdentityCard.createIdentityCard.returns(Promise.reject('some error'));

describe('addToWallet', () => {
it('should close the modal with the add to wallet option', () => {
component.addToWallet();

tick();
mockActiveModal.close.should.have.been.calledWith({card: undefined, choice: 'add'});
});
});

mockIdentityCard.getCurrentIdentityCard.should.have.been.calledThrice;
mockCard.getConnectionProfile.should.have.been.called;
mockCard.getBusinessNetworkName.should.have.been.called;
describe('export', () => {
it('should close the modal with the export option', () => {
component.export();

mockIdentityCard.createIdentityCard.should.have.been.calledWith('myId', 'myNetwork', 'myId', 'mySecret', {name: 'myProfile'}, sinon.match.any, []);
mockActiveModal.dismiss.should.have.been.called;
}));
mockActiveModal.close.should.have.been.calledWith({card: undefined, choice: 'export'});
});
});
});
Loading

0 comments on commit 579c1cd

Please sign in to comment.