Skip to content

Commit

Permalink
Prevent initial identity cards being deleted (hyperledger-archives#1997)
Browse files Browse the repository at this point in the history
It is currently possible to delete every identity card in the playground
and end up with an empty wallet on the login screen. While refreshing
the screen does recreate the initial identity cards created by the
playground automatically, it is a confusing state to get into. This
fix prevents any of the initial identity cards being deleted. It
might be that we want to allow, or even encourage, people to delete
some of those admin cards in the future, however that would currently
leave no way to get them back if required and this seems like a
reasonable way to address the problem for now.

Contributes to #1930

Signed-off-by: James Taylor <[email protected]>
  • Loading branch information
jt-nti authored Aug 25, 2017
1 parent d2f0a9c commit 9f4fdb2
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<span *ngFor="let role of identity.getRoles() last as lastRole">{{role}}{{lastRole ? '': ', '}}</span>
</section>
<section *ngIf="!preview" class="actions">
<button type="button" class="action circular delete"
<button *ngIf="!indestructible" type="button" class="action circular delete"
(click)="delete()">
<svg class="ibm-icon vertical-top" aria-hidden="true">
<use xlink:href="#icon-trash_32"></use>
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()
indestructible: boolean = false;

@Output()
onConnect: EventEmitter<string> = new EventEmitter<string>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ <h2>Identities for {{this.connectionProfileNames.get(connectionProfileRef) === '
<div class="identities">
<identity-card *ngFor="let cardRef of idCardRefs.get(connectionProfileRef)"
[identity]="idCards.get(cardRef)"
[indestructible]="(indestructibleCards.indexOf(cardRef) > -1)"
(onConnect)="changeIdentity(cardRef)"
(onExport)="exportIdentity(cardRef)"
(onDelete)="removeIdentity(cardRef)">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class MockFooterComponent {
})
class MockIdentityCardComponent {
@Input() identity: any;
@Input() indestructible: any;
}

@Component({
Expand Down Expand Up @@ -254,6 +255,7 @@ describe(`LoginComponent`, () => {

it('should load identity cards and sort the profiles', fakeAsync(() => {
mockIdentityCardService.getIdentityCards.returns(Promise.resolve(mockIdCards));
mockIdentityCardService.getIndestructibleIdentityCards.returns(['myCardRef4']);
let sortCards = sinon.stub(component, 'sortIdCards');

component.loadIdentityCards();
Expand All @@ -275,6 +277,7 @@ describe(`LoginComponent`, () => {
component['idCardRefs'].get('xxx-myProfile2').should.deep.equal(['myCardRef2']);
component['idCardRefs'].get('xxx-bobProfile').should.deep.equal(['myCardRef5']);
component['idCardRefs'].get('web-$default').should.deep.equal(['myCardRef4']);
component['indestructibleCards'].should.deep.equal(['myCardRef4']);
}));

it('should handle error', fakeAsync(() => {
Expand Down
2 changes: 2 additions & 0 deletions packages/composer-playground/src/app/login/login.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export class LoginComponent implements OnInit {
private connectionProfiles: Map<string, string>;
private idCardRefs: Map<string, string[]>;
private idCards: Map<string, IdCard>;
private indestructibleCards: string[];
private showDeployNetwork: boolean = false;
private editingConnectionProfile = null;
private creatingIdCard: boolean = false;
Expand Down Expand Up @@ -58,6 +59,7 @@ export class LoginComponent implements OnInit {

loadIdentityCards(): Promise<void> {
return this.identityCardService.getIdentityCards().then((cards) => {
this.indestructibleCards = this.identityCardService.getIndestructibleIdentityCards();
this.idCards = cards;
this.connectionProfileNames = new Map<string, string>();
this.connectionProfiles = new Map<string, string>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ describe('IdentityCardService', () => {
})));
});

describe('#getIndestructibleIdentityCards', () => {
it('should get an array of indestructible identity card refs', fakeAsync(inject([IdentityCardService], (service: IdentityCardService) => {
service['indestructibleCards'] = ['uuid1xxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', 'uuid2xxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'];

service.getIndestructibleIdentityCards().should.deep.equal(['uuid1xxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', 'uuid2xxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx']);
})));
});

describe('#getIdentityCardForExport', () => {
let mockIdCard1;
let mockIdCard2;
Expand Down Expand Up @@ -224,6 +232,23 @@ describe('IdentityCardService', () => {
service['currentCard'].should.equal('uuid1xxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
setCurrentIdentityCardStub.should.have.been.called;
})));

it('should keep track of indestructible identity cards', fakeAsync(inject([IdentityCardService], (service: IdentityCardService) => {
let setCurrentIdentityCardStub = sinon.stub(service, 'setCurrentIdentityCard');
setCurrentIdentityCardStub.returns(Promise.resolve());

mockIdentityCardStorageService.get.withArgs('uuid1xxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx').returns(JSON.parse('{"metadata":{"name":"NetworkAdmin","businessNetwork":"basic-sample-network"},"connectionProfile":{"name":"$default","type":"web"},"credentials":null}'));
mockIdentityCardStorageService.get.withArgs('uuid1xxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-pd').returns(JSON.parse('{"indestructible":true}'));

service.loadIdentityCards(false);

tick();

should.not.exist(service['currentCard']);
service['indestructibleCards'].length.should.equal(1);
service['indestructibleCards'][0].should.equal('uuid1xxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx');
setCurrentIdentityCardStub.should.not.have.been.called;
})));
});

describe('#getIdentityCards', () => {
Expand Down Expand Up @@ -334,9 +359,10 @@ describe('IdentityCardService', () => {
tick();

service['idCards'].size.should.equal(1);
service['indestructibleCards'].length.should.equal(0);
mockIdentityCardStorageService.set.should.have.been.calledTwice;
mockIdentityCardStorageService.set.should.have.been.calledWith(result);
mockIdentityCardStorageService.set.should.have.been.calledWith(result + '-pd', {unused: true});
mockIdentityCardStorageService.set.should.have.been.calledWith(result + '-pd', {unused: true, indestructible: false});
activateIdentityCardStub.should.not.have.been.called;
})));

Expand All @@ -355,11 +381,32 @@ describe('IdentityCardService', () => {
tick();

service['idCards'].size.should.equal(1);
service['indestructibleCards'].length.should.equal(0);
mockIdentityCardStorageService.set.should.have.been.calledTwice;
mockIdentityCardStorageService.set.should.have.been.calledWith(result);
mockIdentityCardStorageService.set.should.have.been.calledWith(result + '-pd', {unused: true});
mockIdentityCardStorageService.set.should.have.been.calledWith(result + '-pd', {unused: true, indestructible: false});
activateIdentityCardStub.should.have.been.called;
})));

it('should add an indestructible identity card', fakeAsync(inject([IdentityCardService], (service: IdentityCardService) => {
let activateIdentityCardStub = sinon.stub(service, 'activateIdentityCard');
let mockIdCard = sinon.createStubInstance(IdCard);
mockIdCard.getName.returns('bcc');

let result;
service.addIdentityCard(mockIdCard, true).then((cardRef) => {
result = cardRef;
});

tick();

service['idCards'].size.should.equal(1);
service['indestructibleCards'].length.should.equal(1);
service['indestructibleCards'][0].should.equal(result);
mockIdentityCardStorageService.set.should.have.been.calledTwice;
mockIdentityCardStorageService.set.should.have.been.calledWith(result);
mockIdentityCardStorageService.set.should.have.been.calledWith(result + '-pd', {unused: true, indestructible: true});
})));
});

describe('#deleteIdentityCard', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ export class IdentityCardService {

private idCards: Map<string, IdCard> = new Map<string, IdCard>();

private indestructibleCards: string[] = [];

constructor(private adminService: AdminService,
private connectionProfileService: ConnectionProfileService,
private identityService: IdentityService,
Expand All @@ -54,6 +56,10 @@ export class IdentityCardService {
return this.getIdentityCard(this.currentCard);
}

getIndestructibleIdentityCards(): string[] {
return this.indestructibleCards;
}

getIdentityCardRefsWithProfileAndRole(qualifiedProfileName: string, role: string): string[] {
let cardRefs: string[] = [];
this.idCards.forEach((card, key) => {
Expand Down Expand Up @@ -100,6 +106,7 @@ export class IdentityCardService {

loadIdentityCards(webOnly: boolean): Promise<number> {
this.currentCard = null;
this.indestructibleCards = [];

return new Promise((resolve, reject) => {
this.idCards = this.identityCardStorageService
Expand All @@ -117,8 +124,13 @@ export class IdentityCardService {
let cardObject = new IdCard(cardProperties.metadata, cardProperties.connectionProfile);
cardObject.setCredentials(cardProperties.credentials);
let data: any = this.identityCardStorageService.get(this.dataRef(cardRef));
if (data && data.current) {
this.currentCard = cardRef;
if (data) {
if (data.current) {
this.currentCard = cardRef;
}
if (data.indestructible) {
this.indestructibleCards.push(cardRef);
}
}
return [cardRef, cardObject];
}
Expand Down Expand Up @@ -154,7 +166,7 @@ export class IdentityCardService {
initialCards.unshift(defaultCardObject);

let addCardPromises: Promise<any>[] = initialCards.map((card, index) => {
return this.addIdentityCard(card).then((cardRef: string) => {
return this.addIdentityCard(card, true).then((cardRef: string) => {
return cardRef;
});
});
Expand All @@ -176,14 +188,21 @@ export class IdentityCardService {
return this.addIdentityCard(card);
}

addIdentityCard(card: IdCard): Promise<string> {
addIdentityCard(card: IdCard, indestructible: boolean = false): Promise<string> {
let cardRef: string = uuid.v4();
let data = {
unused: true,
indestructible: indestructible
};

return Promise.resolve()
.then(() => {
this.identityCardStorageService.set(cardRef, card);
this.identityCardStorageService.set(this.dataRef(cardRef), {unused: true});
this.identityCardStorageService.set(this.dataRef(cardRef), data);
this.idCards.set(cardRef, card);
if (indestructible) {
this.indestructibleCards.push(cardRef);
}

let credentials = card.getCredentials();
if (credentials && credentials.certificate && credentials.privateKey) {
Expand Down

0 comments on commit 9f4fdb2

Please sign in to comment.