[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[reclaim-ui] 261/459: towards cleanup of codebase and fixes
From: |
gnunet |
Subject: |
[reclaim-ui] 261/459: towards cleanup of codebase and fixes |
Date: |
Fri, 11 Jun 2021 23:25:53 +0200 |
This is an automated email from the git hooks/post-receive script.
martin-schanzenbach pushed a commit to branch master
in repository reclaim-ui.
commit 091f5d2d7e075df70bda55d17fd8eab5bb3ec0c4
Author: Martin Schanzenbach <mschanzenbach@posteo.de>
AuthorDate: Thu Aug 6 00:12:57 2020 +0200
towards cleanup of codebase and fixes
---
.../authorization-request.component.html | 6 +-
.../authorization-request.component.ts | 8 +-
src/app/edit-identity/edit-identity.component.html | 281 ++++++++---
src/app/edit-identity/edit-identity.component.ts | 544 ++++++++-------------
src/app/identity-list/identity-list.component.html | 41 +-
src/app/identity-list/identity-list.component.ts | 253 ++++++----
src/app/open-id.service.ts | 190 ++++++-
7 files changed, 780 insertions(+), 543 deletions(-)
diff --git a/src/app/authorization-request/authorization-request.component.html
b/src/app/authorization-request/authorization-request.component.html
index 2041736..0b66b6d 100644
--- a/src/app/authorization-request/authorization-request.component.html
+++ b/src/app/authorization-request/authorization-request.component.html
@@ -14,12 +14,12 @@
asks you to share personal information.<br/>
Choose an identity to let it access the following self-asserted
information:
<ul>
- <li *ngFor="let attribute of getScopes()"><strong>{{attribute}}</strong>
+ <li *ngFor="let attribute of
getScopesDescription()"><strong>{{attribute}}</strong>
</ul>
- <div *ngIf="getRefScope().length > 0">
+ <div *ngIf="getRequestedClaims().length > 0">
as well as the following third-party attested attributes:
<ul>
- <ng-container *ngFor="let reference of getRefScope()">
+ <ng-container *ngFor="let reference of getRequestedClaims()">
<li *ngIf="reference[1] === true"><strong>{{reference[0]}}</strong>
<li *ngIf="reference[1] !== true"><strong>{{reference[0]}}
(optional)</strong>
</ng-container>
diff --git a/src/app/authorization-request/authorization-request.component.ts
b/src/app/authorization-request/authorization-request.component.ts
index fa7b77b..0ce2c6d 100644
--- a/src/app/authorization-request/authorization-request.component.ts
+++ b/src/app/authorization-request/authorization-request.component.ts
@@ -16,12 +16,12 @@ export class AuthorizationRequestComponent implements
OnInit {
this.retryVerify();
}
- getScopes() {
- return this.oidcService.getScope();
+ getScopesDescription() {
+ return this.oidcService.getScopesDescriptionList();
}
- getRefScope() {
- return this.oidcService.getAttestedScope();
+ getRequestedClaims() {
+ return this.oidcService.getRequestedClaims();
}
isClientVerified() {
diff --git a/src/app/edit-identity/edit-identity.component.html
b/src/app/edit-identity/edit-identity.component.html
index a6fb0fb..e592a61 100644
--- a/src/app/edit-identity/edit-identity.component.html
+++ b/src/app/edit-identity/edit-identity.component.html
@@ -2,41 +2,215 @@
<div class="m-2 card">
<div class="card-avatar card-img-top">
<div class="card-avatar-character text-dark">
- Manage identity: <i>{{ identity.name }}</i>
+ <h2>Manage identity: <i>{{ identity.name }}</i></h2>
</div>
</div>
<!-- Attribute table -->
<div class="card-body">
<div>
- <h6 class="card-subtitle mb-2">Attributes:</h6>
- <!-- Missing attributes -->
- <table class="table pb-1" *ngIf="isAttributeMissing() ||
isAttestedMissing()">
- <thead>
- <tr>
- <th>Attribute Name</th>
- <th>Attribute Value</th>
+ <h3 class="card-subtitle mb-2" (click)="showGeneralInfo =
!showGeneralInfo"><span *ngIf="showGeneralInfo" class="fa
fa-chevron-down"></span><span class="fa fa-chevron-right"
*ngIf="!showGeneralInfo"></span> General user information</h3>
+ <table class="table pb-1" *ngIf="showGeneralInfo">
+ <tbody>
+ <!-- Standard "profile" claims first -->
+ <tr [class.openid]="inOpenIdFlow()"
+ [class.text-primary]="isClaimRequested(claim)"
+ [class.alert-danger]="newAttribute.name === claim.name"
+ *ngFor="let claim of existingProfileClaims">
+ <td><div style="min-width: 15em"><b>{{ getDescription(claim)
}}</b></div></td>
+ <td>
+ <input *ngIf="!isClaimAttested(claim)" placeholder="Value"
[(ngModel)]="claim.value">
+ <span *ngIf="isClaimAttested(claim)" >{{ getAttestedValue(claim)
}} issued by <i>{{ getIssuer(claim) }}</i> as attribute for ``{{ claim.value
}}''</span>
+ </td>
+ <td>
+ <button class="btn btn-primary" (click)="deleteAttribute(claim)"
*ngIf="!isClaimAttested(claim)">
+ <span class="fa fa-trash"></span>
+ </button>
+ <button class="btn btn-primary"
(click)="deleteAttribute(claim)" *ngIf="isClaimAttested(claim)">
+ <span class="fa fa-trash"></span>
+ </button>
+ </td>
+ </tr>
+ <!-- Standard "profile" claims missing on the identity -->
+ <tr [class.openid]="inOpenIdFlow()"
+ [class.text-primary]="isClaimRequested(claim)"
+ [class.alert-danger]="newAttribute.name === claim.name"
+ *ngFor="let claim of missingProfileClaims">
+ <td><div style="min-width: 15em"><b>{{ getDescription(claim)
}}</b></div></td>
+ <td>
+ <!-- FIXME Allow adding of attestation OR plain value -->
+ <input placeholder="Value" [(ngModel)]="claim.value">
+ <!--<input *ngIf="!isClaimAttested(claim)" placeholder="Value"
[(ngModel)]="claim.value">
+ <span *ngIf="isClaimAttested(claim)" >{{ getAttestedValue(claim)
}} issued by <i>{{ getIssuer(claim) }}</i> as attribute for ``{{ claim.value
}}''</span>-->
+ </td>
+ <td>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <h3 class="card-subtitle mb-2" (click)="showEmailInfo =
!showEmailInfo"><span class="fa" [class.fa-eye-slash]="!showEmailInfo"
[class.fa-eye]="showEmailInfo"></span> Email</h3>
+
+ <table class="table pb-1">
+ <tbody>
+ <!-- Standard "email" claims first -->
+ <tr [class.openid]="inOpenIdFlow()"
+ [class.text-primary]="isClaimRequested(claim)"
+ [class.alert-danger]="newAttribute.name === claim.name"
+ *ngFor="let claim of existingEmailClaims">
+ <td><div style="min-width: 15em"><b>{{ getDescription(claim)
}}</b></div></td>
+ <td>
+ <input *ngIf="!isClaimAttested(claim)" placeholder="Value"
[(ngModel)]="claim.value">
+ <span *ngIf="isClaimAttested(claim)" >{{ getAttestedValue(claim)
}} issued by <i>{{ getIssuer(claim) }}</i> as attribute for ``{{ claim.value
}}''</span>
+ </td>
+ <td>
+ <button class="btn btn-primary" (click)="deleteAttribute(claim)"
*ngIf="!isClaimAttested(claim)">
+ <span class="fa fa-trash"></span>
+ </button>
+ <button class="btn btn-primary"
(click)="deleteAttribute(claim)" *ngIf="isClaimAttested(claim)">
+ <span class="fa fa-trash"></span>
+ </button>
+ </td>
+ </tr>
+ <!-- Standard "email" claims missing on the identity -->
+ <tr [class.openid]="inOpenIdFlow()"
+ [class.text-primary]="isClaimRequested(claim)"
+ [class.alert-danger]="newAttribute.name === claim.name"
+ *ngFor="let claim of missingEmailClaims">
+ <td><div style="min-width: 15em"><b>{{ getDescription(claim)
}}</b></div></td>
+ <td>
+ <!-- FIXME Allow adding of attestation OR plain value -->
+ <input placeholder="Value" [(ngModel)]="claim.value">
+ <!--<input *ngIf="!isClaimAttested(claim)" placeholder="Value"
[(ngModel)]="claim.value">
+ <span *ngIf="isClaimAttested(claim)" >{{ getAttestedValue(claim)
}} issued by <i>{{ getIssuer(claim) }}</i> as attribute for ``{{ claim.value
}}''</span>-->
+ </td>
+ <td>
+ </td>
</tr>
- </thead>
+ </tbody>
+ </table>
+
+
+ <h6 class="card-subtitle mb-2">Address:</h6>
+ <table class="table pb-1">
<tbody>
- <tr [class.openid]="inOpenIdFlow()"
[class.alert-danger]="newAttribute.name === missing.name" class="text-primary"
*ngFor="let missing of missingAttributes">
- <td><div style="min-width: 15em">{{missing.name}}</div></td>
+ <!-- Standard "address" claims first -->
+ <tr [class.openid]="inOpenIdFlow()"
+ [class.text-primary]="isClaimRequested(claim)"
+ [class.alert-danger]="newAttribute.name === claim.name"
+ *ngFor="let claim of existingAddressClaims">
+ <td><div style="min-width: 15em"><b>{{ getDescription(claim)
}}</b></div></td>
+ <td>
+ <input *ngIf="!isClaimAttested(claim)" placeholder="Value"
[(ngModel)]="claim.value">
+ <span *ngIf="isClaimAttested(claim)" >{{ getAttestedValue(claim)
}} issued by <i>{{ getIssuer(claim) }}</i> as attribute for ``{{ claim.value
}}''</span>
+ </td>
+ <td>
+ <button class="btn btn-primary" (click)="deleteAttribute(claim)"
*ngIf="!isClaimAttested(claim)">
+ <span class="fa fa-trash"></span>
+ </button>
+ <button class="btn btn-primary"
(click)="deleteAttribute(claim)" *ngIf="isClaimAttested(claim)">
+ <span class="fa fa-trash"></span>
+ </button>
+ </td>
+ </tr>
+ <!-- Standard "profile" claims missing on the identity -->
+ <tr [class.openid]="inOpenIdFlow()"
+ [class.text-primary]="isClaimRequested(claim)"
+ [class.alert-danger]="newAttribute.name === claim.name"
+ *ngFor="let claim of missingAddressClaims">
+ <td><div style="min-width: 15em"><b>{{ getDescription(claim)
}}</b></div></td>
<td>
- <input placeholder="Value" [(ngModel)]="missing.value">
+ <!-- FIXME Allow adding of attestation OR plain value -->
+ <input placeholder="Value" [(ngModel)]="claim.value">
+ <!--<input *ngIf="!isClaimAttested(claim)" placeholder="Value"
[(ngModel)]="claim.value">
+ <span *ngIf="isClaimAttested(claim)" >{{ getAttestedValue(claim)
}} issued by <i>{{ getIssuer(claim) }}</i> as attribute for ``{{ claim.value
}}''</span>-->
</td>
<td>
</td>
</tr>
- <tr [class.openid]="inOpenIdFlow()"
[class.alert-danger]="newAttested.name === missing.name" class="text-primary"
*ngFor="let missing of missingAttested">
- <td><div style="min-width: 15em">{{missing.name}}</div></td>
+ </tbody>
+ </table>
+
+ <h6 class="card-subtitle mb-2">Phone:</h6>
+ <table class="table pb-1">
+ <tbody>
+ <!-- Standard "phone" claims first -->
+ <tr [class.openid]="inOpenIdFlow()"
+ [class.text-primary]="isClaimRequested(claim)"
+ [class.alert-danger]="newAttribute.name === claim.name"
+ *ngFor="let claim of existingPhoneClaims">
+ <td><div style="min-width: 15em"><b>{{ getDescription(claim)
}}</b></div></td>
<td>
- <select class="custom-select"
(change)="missing.attestation=$event.target.value; ">
+ </td>
+ <td>
+ <input *ngIf="!isClaimAttested(claim)" placeholder="Value"
[(ngModel)]="claim.value">
+ <span *ngIf="isClaimAttested(claim)" >{{ getAttestedValue(claim)
}} issued by <i>{{ getIssuer(claim) }}</i> as attribute for ``{{ claim.value
}}''</span>
+ </td>
+ <td>
+ <button class="btn btn-primary" (click)="deleteAttribute(claim)"
*ngIf="!isClaimAttested(claim)">
+ <span class="fa fa-trash"></span>
+ </button>
+ <button class="btn btn-primary"
(click)="deleteAttribute(claim)" *ngIf="isClaimAttested(claim)">
+ <span class="fa fa-trash"></span>
+ </button>
+ </td>
+ </tr>
+ <!-- Standard "phone" claims missing on the identity -->
+ <tr [class.openid]="inOpenIdFlow()"
+ [class.text-primary]="isClaimRequested(claim)"
+ [class.alert-danger]="newAttribute.name === claim.name"
+ *ngFor="let claim of missingPhoneClaims">
+ <td><div style="min-width: 15em"><b>{{ getDescription(claim)
}}</b></div></td>
+ <td>
+ <select class="custom-select"
(change)="claim.flag=$event.target.value; ">
+ <option value="0">Plain</option>
+ <option value="1">Attested</option>
+ </select>
+ </td>
+ <td>
+ <input *ngIf="claim.flag == '0'" placeholder="Value"
[(ngModel)]="claim.value">
+ <select *ngIf="claim.flag == '1'" class="custom-select"
+ (change)="claim.attestation=$event.target.value; ">
+ <option value="">Select attestation source</option>
+ <option *ngFor="let attest of attestations"
value={{attest.id}}>
+ {{attest.name}}
+ </option>
+ </select>
+
+ <select class="custom-select"
+ *ngIf="claim.attestation !== '' && claim.flag == '1'"
(change)="claim.value=$event.target.value">
+ <option value="">Select claim</option>
+ <option *ngFor="let att of attestationValuesForClaim(claim)"
value={{att.name}}>
+ {{att.value}} <i>({{att.name}})</i>
+ </option>
+ </select>
+
+ </td>
+ <td>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+
+ <h6 class="card-subtitle mb-2">Additional information:</h6>
+
+ <!-- Missing "non-standard" claims that are requested -->
+ <table class="table pb-1"
*ngIf="isAnyRequestedNonStandardClaimMissing()">
+ <tbody>
+ <tr [class.openid]="inOpenIdFlow()"
+ [class.alert-danger]="newAttribute.name === missing.name"
+ class="text-primary"
+ *ngFor="let missing of missingNonStandardClaims">
+ <td><div style="min-width: 15em"><b>{{missing.name}}</b></div></td>
+ <td>
+ <input *ngIf="!isClaimAttestationRequested(missing)"
placeholder="Value" [(ngModel)]="missing.value">
+ <select *ngIf="isClaimAttestationRequested(missing)"
class="custom-select" (change)="missing.attestation=$event.target.value; ">
<option value="">Select attestation source</option>
<option *ngFor="let attest of attestations"
value={{attest.id}}>
{{attest.name}}
</option>
</select>
- <select class="custom-select" *ngIf="missing.attestation !== ''"
(change)="missing.value=$event.target.value">
+ <select *ngIf="isClaimAttestationRequested(missing) &&
missing.attestation !== ''" class="custom-select"
(change)="missing.value=$event.target.value">
<option value="">Select claim</option>
<option *ngFor="let claim of
attestationValuesForAttested(missing)" value={{claim.name}}>
{{claim.value}} <i>({{claim.name}})</i>
@@ -46,29 +220,26 @@
<td>
</td>
</tr>
-
</tbody>
</table>
- <!-- Requested attributes -->
+ <!-- "non-standard" claims which do exist -->
<table class="table pb-1" style="">
- <thead *ngIf="!isAttributeMissing()">
- <tr>
- <th>Attribute Name</th>
- <th>Attribute Value</th>
- </tr>
- </thead>
<tbody>
- <tr [class.openid]="inOpenIdFlow()"
[class.text-primary]="isRequested(attribute)"
[class.alert-danger]="newAttribute.name === attribute.name"
[class.text-secondary]="isAttestation(attribute)" *ngFor="let attribute of
attributes">
- <td><div style="min-width: 15em">{{ attribute.name }}</div></td>
+ <tr [class.openid]="inOpenIdFlow()"
+ [class.text-primary]="isClaimRequested(attribute)"
+ [class.alert-danger]="newAttribute.name === attribute.name"
+ [class.text-secondary]="isClaimAttested(attribute)"
+ *ngFor="let attribute of existingNonStandardClaims">
+ <td><div style="min-width: 15em"><b>{{ attribute.name
}}</b></div></td>
<td>
- <input *ngIf="!isAttestation(attribute)" placeholder="Value"
[(ngModel)]="attribute.value">
- <span *ngIf="isAttestation(attribute)" >{{
getAttestedValue(attribute) }} issued by <i>{{ getIssuer(attribute) }}</i> as
attribute for ``{{ attribute.value }}''</span>
+ <input *ngIf="!isClaimAttested(attribute)" placeholder="Value"
[(ngModel)]="attribute.value">
+ <span *ngIf="isClaimAttested(attribute)" >{{
getAttestedValue(attribute) }} issued by <i>{{ getIssuer(attribute) }}</i> as
attribute for ``{{ attribute.value }}''</span>
</td>
<td>
- <button class="btn btn-primary"
(click)="deleteAttribute(attribute)" *ngIf="!isAttestation(attribute)">
+ <button class="btn btn-primary"
(click)="deleteAttribute(attribute)" *ngIf="!isClaimAttested(attribute)">
<span class="fa fa-trash"></span>
</button>
- <button class="btn btn-primary"
(click)="deleteAttribute(attribute)" *ngIf="isAttestation(attribute)">
+ <button class="btn btn-primary"
(click)="deleteAttribute(attribute)" *ngIf="isClaimAttested(attribute)">
<span class="fa fa-trash"></span>
</button>
</td>
@@ -79,42 +250,38 @@
<input [class.text-danger]="!attributeNameValid(newAttribute)"
placeholder="Attribute" [(ngModel)]="newAttribute.name">
</td>
<td>
- <input placeholder="Value"
[class.text-danger]="!attributeValueValid(newAttribute)"
[(ngModel)]="newAttribute.value">
- </td>
- <td>
- <button [disabled]="!canAddAttribute(newAttribute)" class="btn
btn-primary" (click)="addAttribute()">
- <span class="fa fa-plus"></span>
- </button>
- </td>
- </tr>
- <!--new Attested Attribute-->
- <tr [class.alert-danger]="isAttestedInConflict(newAttested)"
*ngIf="0 != attestations.length">
- <td>
- <input [class.text-danger]="!attestedNameValid(newAttested)"
placeholder="Attested attribute" [(ngModel)]="newAttested.name">
+ <select class="custom-select"
(change)="newAttribute.flag=$event.target.value; ">
+ <option value="0">Plain</option>
+ <option value="1">Attested</option>
+ </select>
</td>
<td>
- <select class="custom-select"
(change)="newAttested.attestation=$event.target.value; ">
+
+
+ <input *ngIf="newAttribute.flag == '0'" placeholder="Value"
[class.text-danger]="!attributeValueValid(newAttribute)"
[(ngModel)]="newAttribute.value">
+ <select *ngIf="newAttribute.flag == '1'" class="custom-select"
+ (change)="newAttribute.attestation=$event.target.value; ">
<option value="">Select attestation source</option>
<option *ngFor="let attest of attestations"
value={{attest.id}}>
{{attest.name}}
</option>
</select>
- <select class="custom-select" *ngIf="newAttested.attestation !==
''" (change)="newAttested.value=$event.target.value">
+ <select class="custom-select"
+ *ngIf="newAttribute.attestation !== '' && newAttribute.flag ==
'0'" (change)="newAttribute.value=$event.target.value">
<option value="">Select claim</option>
- <option *ngFor="let claim of
attestationValuesForAttested(newAttested)" value={{claim.name}}>
+ <option *ngFor="let claim of
attestationValuesForClaim(newAttribute)" value={{claim.name}}>
{{claim.value}} <i>({{claim.name}})</i>
</option>
</select>
- </td>
+ </td>
<td>
- <button [disabled]="!canAddAttested(newAttested)" class="btn
btn-primary" (click)="addAttested()">
- <span class="fa fa-plus"></span>
+ <button [disabled]="!canAddAttribute(newAttribute)" class="btn
btn-primary" (click)="addAttribute()">
+ <span class="fa fa-plus"></span>
</button>
</td>
</tr>
-
</tbody>
</table>
</div>
@@ -127,22 +294,6 @@
<li>Attribute values may not be empty!</li>
</ul>
</div>
- <!-- optional attributes information -->
- <div *ngIf="optionalAttested.length !== 0" class="text-primary">
- <span class="fa fa-openid"></span> You may optionally provide
attestations for:
- <ul>
- <li *ngFor="let attribute of
optionalAttested"><strong>{{attribute.name}}</strong>
- </ul>
- </div>
- <!-- Attested creation warning -->
- <div *ngIf="!attestedNameValid(newAttested) ||
!attestedValueValid(newAttested)" class="alert alert-primary alert-dismissible
fade show" role="alert">
- <span class="fa fa-warning"></span> Note:
- <ul>
- <li>Only use alphanumeric attribute names.</li>
- <li>You cannot define the same name twice.</li>
- <li>IDs and values may not be empty!</li>
- </ul>
- </div>
<hr/>
diff --git a/src/app/edit-identity/edit-identity.component.ts
b/src/app/edit-identity/edit-identity.component.ts
index 50a6f45..4cf9706 100644
--- a/src/app/edit-identity/edit-identity.component.ts
+++ b/src/app/edit-identity/edit-identity.component.ts
@@ -26,18 +26,25 @@ export class EditIdentityComponent implements OnInit {
attributes: Attribute[];
attestations: Attestation[];
attestationValues: {};
- requestedAttributes: Attribute[];
- missingAttributes: Attribute[];
newAttribute: Attribute;
- newAttested: Attribute;
- newAttestation: Attestation;
+ newAttestedClaim: Attribute;
missingAttested: Attribute[];
- requestedAttested: Attribute[];
- optionalAttested: Attribute[];
+ requestedClaims: Attribute[];
+ optionalClaims: Attribute[];
webfingerEmail: string;
authorizations: Authorization[];
newIdProvider: IdProvider;
emailNotFoundAlertClosed: boolean;
+ existingProfileClaims: Attribute[];
+ missingProfileClaims: Attribute[];
+ existingPhoneClaims: Attribute[];
+ missingPhoneClaims: Attribute[];
+ existingEmailClaims: Attribute[];
+ missingEmailClaims: Attribute[];
+ existingAddressClaims: Attribute[];
+ missingAddressClaims: Attribute[];
+ existingNonStandardClaims: Attribute[];
+ missingNonStandardClaims: Attribute[];
constructor(private reclaimService: ReclaimService,
private identityService: IdentityService,
@@ -51,7 +58,7 @@ export class EditIdentityComponent implements OnInit {
ngOnInit() {
this.attributes = [];
this.attestations = [];
- this.optionalAttested = [];
+ this.optionalClaims = [];
this.attestationValues = {};
this.webfingerEmail = '';
this.emailNotFoundAlertClosed = true;
@@ -59,8 +66,7 @@ export class EditIdentityComponent implements OnInit {
this.loadAuthorizationsFromLocalStorage();
this.identity = new Identity('','');
this.newAttribute = new Attribute('', '', '', '', 'STRING', '0');
- this.newAttested = new Attribute('', '', '', '', 'STRING', '1');
- this.newAttestation = new Attestation('', '', '', 'JWT', '', null, []);
+ this.newAttestedClaim = new Attribute('', '', '', '', 'STRING', '1');
this.activatedRoute.params.subscribe(p => {
if (p['id'] === undefined) {
return;
@@ -78,31 +84,67 @@ export class EditIdentityComponent implements OnInit {
});
}
+ getDescription(claim: Attribute) : string {
+ return this.oidcService.getClaimDescription(claim);
+ }
+
+ private bootstrapClaimArray(claimTemplate: Object): Attribute[] {
+ var result = [];
+ for (let claim in claimTemplate) {
+ let attr = new Attribute(claim, '', '', '', 'STRING', '0');
+ result.push(attr);
+ }
+ return result;
+ }
+ private updateClaimArray(claimArray: Attribute[], attr: Attribute):
Attribute[] {
+ var result = [];
+ for (let i = 0; i < claimArray.length; i++) {
+ if (claimArray[i].name === attr.name) {
+ result.push(attr);
+ } else {
+ result.push(claimArray[i]);
+ }
+ }
+ return result;
+ }
+
+ private cleanupClaimArray(claimArray: Attribute[]) {
+ var result = []
+ for (let attr of claimArray) {
+ if (attr.value !== '') {
+ result.push(attr);
+ }
+ }
+ return result;
+ }
private updateAttributes() {
this.reclaimService.getAttributes(this.identity).subscribe(attributes => {
- this.attributes = [];
- this.requestedAttributes = [];
- this.requestedAttested = [];
- if (attributes === null) {
- this.getMissingAttributes();
- return;
- }
- let i;
+ this.existingProfileClaims = this.bootstrapClaimArray
(this.oidcService.getStandardProfileClaims());
+ this.existingEmailClaims = this.bootstrapClaimArray
(this.oidcService.getStandardEmailClaims());
+ this.existingPhoneClaims = this.bootstrapClaimArray
(this.oidcService.getStandardPhoneClaims());
+ this.existingAddressClaims = this.bootstrapClaimArray
(this.oidcService.getStandardAddressClaims());
+ this.existingNonStandardClaims = [];
this.attributes = attributes;
- for (i = 0; i < attributes.length; i++) {
- if ((attributes[i].flag === '0') &&
- this.oidcService.getScope().includes(attributes[i].name)) {
- this.requestedAttributes.push(attributes[i]);
- }
- if ((attributes[i].flag === '1') &&
- this.oidcService.getAttestedScope().includes(attributes[i].name)) {
- this.requestedAttested.push(attributes[i]);
+ for (let attr of this.attributes) {
+ if (this.oidcService.isStandardProfileClaim(attr)) {
+ this.existingProfileClaims =
this.updateClaimArray(this.existingProfileClaims, attr);
+ } else if (this.oidcService.isStandardEmailClaim(attr)) {
+ this.existingEmailClaims =
this.updateClaimArray(this.existingEmailClaims, attr);
+ } else if (this.oidcService.isStandardAddressClaim(attr)) {
+ this.existingAddressClaims =
this.updateClaimArray(this.existingAddressClaims, attr);
+ } else if (this.oidcService.isStandardPhoneClaim(attr)) {
+ this.existingPhoneClaims =
this.updateClaimArray(this.existingPhoneClaims, attr);
+ } else {
+ this.existingNonStandardClaims.push(attr);
}
}
- this.getMissingAttributes();
- this.getMissingAttested();
+ this.existingProfileClaims =
this.cleanupClaimArray(this.existingProfileClaims);
+ this.existingEmailClaims =
this.cleanupClaimArray(this.existingEmailClaims);
+ this.existingPhoneClaims =
this.cleanupClaimArray(this.existingPhoneClaims);
+ this.existingAddressClaims =
this.cleanupClaimArray(this.existingAddressClaims);
+ this.updateMissingAttributes();
},
err => {
//this.errorInfos.push("Error retrieving attributes for ``" +
identity.name + "''");
@@ -114,47 +156,52 @@ export class EditIdentityComponent implements OnInit {
return this.oidcService.inOpenIdFlow();
}
- isRequested(attribute) {
- if (undefined === this.requestedAttributes) {
+ isClaimRequested(attribute) {
+ const claims = this.oidcService.getClaimNamesForRequest();
+ if (undefined === claims) {
return false;
} else {
return -1 !==
- this.requestedAttributes.indexOf(attribute);
+ claims.indexOf(attribute.name);
}
}
- getMissingAttributes() {
- const scopes = this.oidcService.getScope();
- let i;
- for (i = 0; i < this.requestedAttributes.length; i++) {
+ updateMissingAttributes() {
+ var claims = this.oidcService.getStandardClaimNames();
+ for (let attr of this.attributes) {
const j =
- scopes.indexOf(this.requestedAttributes[i].name);
+ claims.indexOf(attr.name);
if (j >= 0) {
- scopes.splice(j, 1);
+ claims.splice(j, 1);
}
}
- this.missingAttributes = [];
- for (i = 0; i < scopes.length; i++) {
+ this.missingProfileClaims = [];
+ this.missingEmailClaims = [];
+ this.missingPhoneClaims = [];
+ this.missingAddressClaims = [];
+ this.missingNonStandardClaims = [];
+ for (let claim of claims) {
const attribute = new Attribute('', '', '', '', 'STRING', '');
attribute.flag = '0';
- attribute.name = scopes[i];
- this.missingAttributes.push(attribute);
+ attribute.name = claim;
+ if (this.oidcService.isStandardProfileClaim(attribute)) {
+ this.missingProfileClaims.push(attribute);
+ } else if (this.oidcService.isStandardEmailClaim(attribute)) {
+ this.missingEmailClaims.push(attribute);
+ } else if (this.oidcService.isStandardPhoneClaim(attribute)) {
+ this.missingPhoneClaims.push(attribute);
+ } else if (this.oidcService.isStandardAddressClaim(attribute)) {
+ this.missingAddressClaims.push(attribute);
+ } else {
+ this.missingNonStandardClaims.push(attribute);
+ }
}
}
- isInConflict(attribute) {
- let i;
- if (undefined !== this.missingAttributes) {
- for (i = 0; i < this.missingAttributes.length; i++) {
- if (attribute.name ===
- this.missingAttributes[i].name) {
- return true;
- }
- }
- }
- if (undefined !== this.attributes) {
- for (i = 0; i < this.attributes.length; i++) {
- if (attribute.name === this.attributes[i].name) {
+ checkConflict(attrs: Attribute[], attribute: Attribute): boolean {
+ if (undefined !== attrs) {
+ for (let attr of attrs) {
+ if (attribute.name === attr.name) {
return true;
}
}
@@ -162,7 +209,16 @@ export class EditIdentityComponent implements OnInit {
return false;
}
- canAddAttribute(attribute) {
+ isInConflict(attribute: Attribute): boolean {
+ return this.checkConflict(this.missingProfileClaims, attribute) ||
+ this.checkConflict(this.missingEmailClaims, attribute) ||
+ this.checkConflict(this.missingPhoneClaims, attribute) ||
+ this.checkConflict(this.missingAddressClaims, attribute) ||
+ this.checkConflict(this.missingNonStandardClaims, attribute) ||
+ this.checkConflict(this.attributes, attribute);
+ }
+
+ canAddAttribute(attribute: Attribute): boolean {
if ((attribute.name === '') || (attribute.value === '')) {
return false;
}
@@ -172,12 +228,15 @@ export class EditIdentityComponent implements OnInit {
return !this.isInConflict(attribute);
}
- canSaveIdentity() {
- return (this.canSaveAttribute() &&
- this.canSaveAttested());
+ canSaveIdentity(): boolean {
+ return this.canSaveAttributes();
}
- canSaveAttribute() {
+ /**
+ * TODO fix newAttribute so that we can make
+ * it either attested or plain
+ */
+ canSaveAttributes(): boolean {
if (this.canAddAttribute(this.newAttribute)) {
return true;
}
@@ -186,44 +245,18 @@ export class EditIdentityComponent implements OnInit {
!this.isInConflict(this.newAttribute);
}
- canSaveAttested() {
- if (this.canAddAttested(this.newAttested)) {
- return true;
- }
- return ((this.newAttested.name === '') &&
- (this.newAttested.attestation === '') &&
- (this.newAttested.id === '')) &&
- !this.isAttestedInConflict(this.newAttested);
- }
-
-
- isAttestedInConflict(attested) {
- let i;
- if (undefined !== this.missingAttested) {
- for (i = 0; i < this.missingAttested.length; i++) {
- if (attested.name ===
- this.missingAttested[i].name) {
- return true;
- }
- }
- }
- if (undefined !== this.attributes) {
- for (i = 0; i < this.attributes.length; i++) {
- if (attested.name === this.attributes[i].name) {
- return true;
- }
- }
- }
- return false;
- }
-
-
saveIdentity() {
localStorage.removeItem("userForAttestation");
this.saveIdentityAttributes();
}
saveIdentityAttributes() {
+ if (this.newAttribute.flag === '0') {
+ /**
+ * Make sure attestation is not still set
+ */
+ this.newAttribute.attestation = '';
+ }
this.storeAttributes()
.pipe(
finalize(() => {
@@ -257,25 +290,45 @@ export class EditIdentityComponent implements OnInit {
});
}
- private storeAttributes() {
+ private storeMissingAttributes(missing: Attribute[]): any {
const promises = [];
let i;
- if (undefined !== this.missingAttributes) {
- for (i = 0; i < this.missingAttributes.length; i++) {
- if (this.missingAttributes[i].value === '') {
+ if (undefined !== missing) {
+ for (let attr of missing) {
+ if (attr.value === '') {
continue;
}
+ if (attr.flag === '0') {
+ attr.attestation = '';
+ }
promises.push(from(this.reclaimService.addAttribute(
- this.identity, this.missingAttributes[i])));
+ this.identity, attr)));
}
}
+ return promises;
+ }
+
+ /**
+ * FIXME incorporate attested attributes here!
+ */
+ private storeAttributes() {
+ var promises = [];
+ promises = promises.concat (this.storeMissingAttributes
(this.missingProfileClaims));
+ promises = promises.concat (this.storeMissingAttributes
(this.missingEmailClaims));
+ promises = promises.concat (this.storeMissingAttributes
(this.missingPhoneClaims));
+ promises = promises.concat (this.storeMissingAttributes
(this.missingAddressClaims));
+ promises = promises.concat (this.storeMissingAttributes
(this.missingNonStandardClaims));
+
if (undefined !== this.attributes) {
- for (i = 0; i < this.attributes.length; i++) {
- if (this.attributes[i].flag === '1') {
+ for (let attr of this.attributes) {
+ /*if (attr.flag === '1') {
continue; //Is an attestation
+ }*/
+ if (attr.flag === '0') {
+ attr.attestation = '';
}
promises.push(
- from(this.reclaimService.addAttribute(this.identity,
this.attributes[i])));
+ from(this.reclaimService.addAttribute(this.identity, attr)));
}
}
if (this.newAttribute.value !== '') {
@@ -285,6 +338,9 @@ export class EditIdentityComponent implements OnInit {
return forkJoin(promises);
}
+ /**
+ * Adds a new attribute, stores all changes and STAYS on this page.
+ */
addAttribute() {
this.storeAttributes()
.pipe(
@@ -305,7 +361,7 @@ export class EditIdentityComponent implements OnInit {
});
}
- attributeNameValid(attribute) {
+ attributeNameValid(attribute: Attribute): boolean {
if (attribute.name === '' && attribute.value === '') {
return true;
}
@@ -325,84 +381,67 @@ export class EditIdentityComponent implements OnInit {
return true;
}
- isAttributeMissing() {
- if (!this.oidcService.inOpenIdFlow()) {
- return false;
+ isAttributeNameInList(name: string, attrs: Attribute[]) {
+ for (let attr of attrs) {
+ if (name === attr.name) {
+ return true;
+ }
}
- if (undefined === this.requestedAttributes) {
+ return false;
+ }
+
+ isAnyRequestedNonStandardClaimMissing(): boolean {
+ if (!this.oidcService.inOpenIdFlow()) {
return false;
}
- var scopes = this.oidcService.getScope();
- for (var i = 0; i < this.requestedAttributes.length; i++) {
- if (!scopes.includes(this.requestedAttributes[i].name))
- {
+ var requestedClaims = this.oidcService.getClaimNamesForRequest();
+ for (let claim in requestedClaims) {
+ if (this.isAttributeNameInList(claim, this.missingNonStandardClaims)) {
return true;
}
}
return false;
- }
- private saveIdentityAttested() {
- this.storeAttested()
- .pipe(
- finalize(() => {
- this.newAttested.name = '';
- this.newAttested.attestation = '';
- this.newAttested.id = '';
- this.newAttested.value = '';
- this.newAttested.flag = '1';
- }))
- .subscribe(res => {
- //FIXME success dialog/banner
- this.updateAttributes();
- },
- err => {
- console.log(err);
- //this.errorInfos.push("Failed to update identity ``" +
this.identityInEdit.name + "''");
- });
}
- deleteAttested(attribute) {
- this.reclaimService.deleteAttribute(this.identity, attribute)
- .subscribe(res => {
- //FIXME info dialog
- this.updateAttributes();
- },
- err => {
- //this.errorInfos.push("Failed to delete reference ``" +
reference.name + "''");
- console.log(err);
- });
+ isAnyRequestedAttributeMissing(): boolean {
+ if (!this.oidcService.inOpenIdFlow()) {
+ return false;
+ }
+ var requestedClaims = this.oidcService.getClaimNamesForRequest();
+ for (let claim in requestedClaims) {
+ if (this.isAttributeNameInList(claim, this.missingProfileClaims) ||
+ this.isAttributeNameInList(claim, this.missingEmailClaims) ||
+ this.isAttributeNameInList(claim, this.missingAddressClaims) ||
+ this.isAttributeNameInList(claim, this.missingPhoneClaims) ||
+ this.isAttributeNameInList(claim, this.missingNonStandardClaims)) {
+ return true;
+ }
+ }
+ return false;
}
-
- getMissingAttested() {
- const refscopes = this.oidcService.getAttestedScope();
- let i;
- for (i = 0; i < this.requestedAttested.length; i++) {
- for (var j = 0; j < refscopes.length; j++) {
- if (this.requestedAttested[i].name === refscopes[j][0] ) {
- refscopes.splice(j,1);
- }
+ isClaimAttestationRequested(attr: Attribute) {
+ //TODO check if this claim is in claims parameter and needs attestation
+ var claims = this.oidcService.getRequestedClaims();
+ for (let claim of claims) {
+ if (claim[0] == attr.name) {
+ return true;
}
}
- this.missingAttested = [];
- this.optionalAttested = [];
- for (i = 0; i < refscopes.length; i++) {
- const attribute = new Attribute('', '', '', '', 'STRING', '');
- attribute.flag = '1';
- if (refscopes[i][1] === true)
- {
- attribute.name = refscopes[i][0];
- this.missingAttested.push(attribute);
- }
- if (refscopes[i][1] === false)
- {
- attribute.name = refscopes[i][0];
- this.optionalAttested.push(attribute);
- }
+ return false;
+ }
+
+ isClaimOptional(claim: Attribute) {
+ //TODO check if this claim is in claims parameter and optional
+ var claims = this.oidcService.getRequestedClaims();
+ for (let claim of claims) {
+ return claim[1];
}
+ return true;
}
+
private updateAttestations() {
this.reclaimService.getAttestations(this.identity).subscribe(attestations
=> {
this.attestations = attestations;
@@ -413,94 +452,11 @@ export class EditIdentityComponent implements OnInit {
});
}
- private storeAttested() {
- const promises = [];
- let i;
- if (undefined !== this.missingAttested) {
- for (i = 0; i < this.missingAttested.length; i++) {
- if ((this.missingAttested[i].value === '') ||
(this.missingAttested[i].attestation !== '')) {
- console.log("Empty Attestation: " + this.missingAttested[i]);
- continue;
- }
- console.log("Missing Attestation: " + this.missingAttested[i]);
- promises.push(from(this.reclaimService.addAttribute(
- this.identity, this.missingAttested[i])));
- }
- }
- if (undefined !== this.attributes) {
- for (i = 0; i < this.attributes.length; i++) {
- if (this.attributes[i].attestation === '') {
- continue;
- }
- promises.push(
- from(this.reclaimService.addAttribute(this.identity,
this.attributes[i])));
- }
- }
- if ((this.newAttested.value !== '') && (this.newAttested.attestation !==
'')
- && (this.newAttested.name !== '')) {
- promises.push(from(this.reclaimService.addAttribute(this.identity,
this.newAttested)));
- }
-
- return forkJoin(promises);
- }
-
- addAttested() {
- this.storeAttested()
- .pipe(
- finalize(() => {
- this.newAttested.name = '';
- this.newAttested.value= '';
- this.newAttested.id = '';
- this.newAttested.attestation = '';
- this.newAttested.flag = '1';
- this.updateAttributes();
- }))
- .subscribe(res => {
- console.log(res);
- },
- err => {
- console.log(err);
- //this.errorInfos.push("Failed to update identity ``" +
this.identityInEdit.name + "''");
- EMPTY
- });
- }
-
-
- isAttestation(attribute) {
+ isClaimAttested(attribute) {
return attribute.flag === '1';
}
- canAddAttested(attribute) {
- if ((attribute.name === '') || (attribute.value === '') ||
(attribute.attestation === '')) {
- return false;
- }
- if (attribute.name.indexOf(' ') >= 0) {
- return false;
- }
- return !this.isAttestedInConflict(attribute);
- }
-
- attestedNameValid(attribute) {
- if (attribute.name === '' && attribute.value === '' &&
attribute.attestation === '') {
- return true;
- }
- if (attribute.name.indexOf(' ') >= 0) {
- return false;
- }
- if (!/^[a-zA-Z0-9-_]+$/.test(attribute.name)) {
- return false;
- }
- return !this.isAttestedInConflict(attribute);
- }
-
- attestedValueValid(attribute: Attribute) {
- if (attribute.value === '') {
- return attribute.name === '';
- }
- return true;
- }
-
- attestedAttestationValid(attribute: Attribute) {
+ isClaimAttestationValid(attribute: Attribute) {
if (attribute.attestation === '') {
return attribute.name === '';
}
@@ -508,88 +464,7 @@ export class EditIdentityComponent implements OnInit {
}
- isAttestedRequested(attribute: Attribute) {
- if (undefined === this.requestedAttested) {
- return false;
- } else {
- return -1 !==
- this.requestedAttested.indexOf(attribute);
- }
- }
-
- isAttrRefRequested(attribute: Attribute) {
- if (undefined === this.requestedAttested) {
- return false;
- } else {
- for (var j = 0; j < this.requestedAttested.length; j++) {
- if (attribute.name === this.requestedAttested[j].name) {
- return true;
- }
- }
- return false;
- }
- }
-
- isoptAttestedRequested(attribute: Attribute) {
- if (undefined === this.optionalAttested) {
- return false;
- } else {
- return -1 !==
- this.optionalAttested.indexOf(attribute);
- }
- }
-
- isAttestedMissing() {
- if (!this.inOpenIdFlow()) {
- return false;
- }
- if (undefined === this.requestedAttested) {
- return false;
- }
- for (var i = 0; i < this.oidcService.getAttestedScope().length; i++) {
- if (this.oidcService.getAttestedScope()[i][1] === true) {
- var j;
- for (j = 0; j < this.requestedAttested.length; j++) {
- if (this.oidcService.getAttestedScope()[i][0] ===
this.requestedAttested[j].name){
- break;
- }
- }
- if (j === this.requestedAttested.length){
- return true;
- }
- }
- }
- return false;
- }
-
- /*private setAttestationValue(attestation) {
- var value_string="";
- return this.reclaimService.parseAttest(attestation).subscribe(json_string
=>{
- this.attestation_val[attestation.id]=json_string;
- },
- err => {
- //this.errorInfos.push("Error parsing attestation ``" + attestation.name +
"''");
- console.log(err);
- });
- }*/
-
- isAttestedValid(attribute: Attribute) {
- for (let i = 0; i < this.attestations.length; i++) {
- if (attribute.attestation === this.attestations[i].id) {
- return this.isAttestationValid(this.attestations[i]);
- }
- }
- return false;
- }
-
- isAttestationValid(attestation: Attestation) {
- //FIXME JWT specific
- //FIXME the expiration of the JWT should be a property of the attestation
- //Not part of the values
- return true;
- }
-
- attestationValuesForAttested(attribute: Attribute) {
+ attestationValuesForClaim(attribute: Attribute) {
for (let i = 0; i < this.attestations.length; i++) {
if (this.attestations[i].id == attribute.attestation) {
return this.attestations[i].attributes;
@@ -597,21 +472,6 @@ export class EditIdentityComponent implements OnInit {
}
}
- isAnyAttestationInvalid() {
- if (!this.inOpenIdFlow()) {
- return false;
- }
- if (undefined === this.requestedAttested) {
- return false;
- }
- for (var j = 0; j < this.attestations.length; j++) {
- if (!this.isAttestationValid(this.attestations[j])) {
- return true;
- }
- }
- return false;
- }
-
//FIXME attestations need an issuer field
getIssuer(attribute: Attribute) {
for (let i = 0; i < this.attestations.length; i++) {
diff --git a/src/app/identity-list/identity-list.component.html
b/src/app/identity-list/identity-list.component.html
index fc8b24a..40606d9 100644
--- a/src/app/identity-list/identity-list.component.html
+++ b/src/app/identity-list/identity-list.component.html
@@ -53,7 +53,6 @@
<div class="card" *ngFor="let identity of identities | search: searchTerm">
<div class="card-avatar card-img-top">
<div class="card-avatar-character text-dark">
- <!--<div class="icon m-1 text-uppercase"
[style.background-color]="intToRGB(identity.pubkey)">{{
identity.name[0]}}</div>-->
<i class="fa fa-user-circle"></i>
<span class="m-1" style="display: inline-block"><i>
{{identity.name}}</i></span>
<button class="btn btn-primary" *ngIf="showConfirmDelete != identity"
(click)="confirmDelete(identity)">
@@ -64,6 +63,7 @@
</button>
</div>
</div>
+ <!-- Identity deletion confirmation -->
<div class="alert alert-danger fade show" *ngIf="showConfirmDelete ==
identity">
Do you really want to delete this identity?<br/><br/>
<button class="btn btn-primary" (click)="deleteIdentity(identity)">
@@ -73,41 +73,36 @@
<span class="fa fa-close"></span> Cancel
</button>
</div>
+ <!-- Maybe add some info to identities? - dialog -->
<div class="alert alert-secondary fade show"
*ngIf="!hasAttributes(identity)">
This identity has no attributes. Maybe try <a class="buttonlink"
[routerLink]="['/edit-identity', identity.name]">adding some?</a>
</div>
- <div *ngIf="isAttributeMissing(identity) || isAttestedMissing(identity)"
class="alert alert-danger alert-dismissible fade show" role="alert">
- <span class="fa fa-openid"></span> This identity cannot be used because
it's missing requested information:
+ <div *ngIf="isClaimMissing(identity) || isRequestedScopeMissing(identity)"
class="alert alert-danger alert-dismissible fade show" role="alert">
+ <span class="fa fa-openid"></span> This identity is missing some
requested information:
<ul>
- <li *ngFor="let attr of getMissing(identity)">{{attr}}</li>
- <li *ngFor="let ref of getOptional(identity)">{{ref}}(optional)</li>
+ <li *ngIf="isProfileRequested() &&
isProfileMissing(identity)">getProfileDescription()</li>
+ <li *ngIf="isEmailRequested() &&
isEmailMissing(identity)">getEmailDescription()</li>
+ <li *ngIf="isPhoneRequested() &&
isPhoneMissing(identity)">getPhoneDescription()</li>
+ <li *ngIf="isAddressRequested() &&
isAddressMissing(identity)">getAddressDescription()</li>
+ <li *ngFor="let attr of getMissingClaims(identity)">{{attr}}</li>
+ <li *ngFor="let ref of getOptional(identity)">{{ref}} (optional)</li>
</ul>
<button class="btn btn-primary" [routerLink]="['/edit-identity',
identity.name]">
- <span class="fa fa-plus"></span> Add
- </button>
- </div>
- <div *ngIf="isReqReferenceInvalid(identity)" class="alert alert-danger
alert-dismissible fade show" role="alert">
- <span class="fa fa-openid"></span> This identity cannot be used because
one or more attested attributes are invalid.
- <button class="btn btn-primary" (click)="editIdentity(identity)">
- <span class="fa fa-pencil-square-o"></span> Edit
+ <span class="fa fa-plus"></span> Edit identity
</button>
</div>
- <div *ngIf="(0 < requestedAttested[identity.pubkey]?.length) &&
inOpenIdFlow()" class="alert alert-danger alert-dismissible fade show"
role="alert">
- <span class="fa fa-openid"></span> Please note that sharing attested
attributes might disclose additional information stored in the attestation.
- </div>
<div class="card-body">
- <!-- Attribute table -->
+ <!-- Attribute table -->
<div>
- <h6 class="card-subtitle mb-2" *ngIf="hasAttributes(identity)">
- <strong>Information:</strong>
- </h6>
<table class="table pb-1">
<tbody>
- <tr [class.openid]="inOpenIdFlow()"
[class.text-primary]="isRequested(identity, attribute) ||
isAttestedRequested(identity, attribute)"
- [class.text-secondary]="!isAttestedRequested(identity,
attribute) && isAttestation(attribute)"
+ <tr [class.openid]="inOpenIdFlow()"
[class.text-primary]="isClaimRequested(identity, attribute)"
+ [class.text-secondary]="!isClaimRequested(identity, attribute)"
*ngFor="let attribute of attributes[identity.pubkey]">
<td>
- <div style="min-width: 15em"><span
*ngIf="isRequested(identity, attribute) || isAttestedRequested(identity,
attribute)" class="fa fa-openid"></span> {{attribute.name}}</div>
+ <div style="min-width: 15em">
+ <span *ngIf="isClaimRequested(identity, attribute)"
class="fa fa-openid"></span> <b>{{getAttributeDescription(attribute)}}</b>
+ </div>
</td>
<td>
<div *ngIf="attribute.value.length <= 20" style="min-width:
15em">{{attribute.value}}</div>
@@ -123,7 +118,7 @@
<div>
<div>
- <button *ngIf="canAuthorize(identity)" [disabled]="!inOpenIdFlow()
|| isAttributeMissing(identity) || isAttestedMissing(identity) ||
!isClientVerified() || isReqReferenceInvalid(identity)"
(click)="loginIdentity(identity)" class="btn btn-primary mr-1 openid-login">
+ <button *ngIf="inOpenIdFlow()" [disabled]="!isClientVerified()"
(click)="loginIdentity(identity)" class="btn btn-primary mr-1 openid-login">
<span *ngIf="isClientVerified()"><i class="fa fa-openid"></i>
Share information from this identity</span>
<span *ngIf="!isClientVerified()"><i class="fa
fa-exclamation-circle"></i> Sharing disabled</span>
</button>
diff --git a/src/app/identity-list/identity-list.component.ts
b/src/app/identity-list/identity-list.component.ts
index 6ca1dbf..c491e73 100644
--- a/src/app/identity-list/identity-list.component.ts
+++ b/src/app/identity-list/identity-list.component.ts
@@ -21,11 +21,14 @@ import { from, forkJoin, EMPTY } from 'rxjs';
export class IdentityListComponent implements OnInit {
- requestedAttributes: any;
- requestedAttested: any;
- missingAttributes: any;
- missingAttestations: any;
- optionalAttested: any;
+ profileAttributes: any;
+ emailAttribute: any;
+ phoneAttribute: any;
+ addressAttributes: any;
+ requestedScopes: any;
+ requestedClaims: any;
+ missingClaims: any;
+ optionalClaims: any;
attributes: any;
attestations: any;
identities: Identity[];
@@ -34,6 +37,7 @@ export class IdentityListComponent implements OnInit {
modalOpened: any;
errorInfos: any;
searchTerm: any;
+ sortAttributeByStandardClaim: any;
constructor(private route: ActivatedRoute, private oidcService:
OpenIdService,
private identityService: IdentityService,
@@ -49,11 +53,10 @@ export class IdentityListComponent implements OnInit {
this.attestations = {};
this.identities = [];
this.showConfirmDelete = null;
- this.requestedAttributes = {};
- this.missingAttributes = {};
- this.requestedAttested = {};
- this.missingAttestations = {};
- this.optionalAttested = {};
+ this.requestedScopes = {};
+ this.missingClaims = {};
+ this.requestedClaims = {};
+ this.optionalClaims = {};
this.connected = false;
this.modalOpened = false;
if (undefined !== this.route.snapshot.queryParams["code"]) {
@@ -89,54 +92,41 @@ export class IdentityListComponent implements OnInit {
hideConfirmDelete() { this.showConfirmDelete = null; }
- getMissingAttributes(identity) {
- const scopes = this.getScopes();
+ /**
+ * Returns missing claims specifically requested
+ * through the "claims" OIDC parameter
+ */
+ updateMissingClaims(identity) {
+ const refscopes = this.oidcService.getRequestedClaims();
let i;
- for (i = 0; i < this.requestedAttributes[identity.pubkey].length; i++) {
- const j =
- scopes.indexOf(this.requestedAttributes[identity.pubkey][i].name);
- if (j >= 0) {
- scopes.splice(j, 1);
- }
- }
- this.missingAttributes[identity.pubkey] = [];
- for (i = 0; i < scopes.length; i++) {
- const attribute = new Attribute('', '', '', '', 'STRING', '');
- attribute.name = scopes[i];
- this.missingAttributes[identity.pubkey].push(attribute);
- }
- }
-
- getMissingAttested(identity) {
- const refscopes = this.oidcService.getAttestedScope();
- let i;
- for (i = 0; i < this.requestedAttested[identity.pubkey].length; i++) {
+ for (i = 0; i < this.attributes[identity.pubkey].length; i++) {
for (var j = 0; j < refscopes.length; j++) {
- if (this.requestedAttested[identity.pubkey][i].name ===
refscopes[j][0] ) {
+ if (this.attributes[identity.pubkey][i].name === refscopes[j][0] ) {
refscopes.splice(j,1);
}
}
}
- this.missingAttestations[identity.pubkey] = [];
- this.optionalAttested[identity.pubkey] = [];
+ this.missingClaims[identity.pubkey] = [];
+ this.optionalClaims[identity.pubkey] = [];
for (i = 0; i < refscopes.length; i++) {
const attested = new Attribute('', '', '', '', 'STRING', '');
if (refscopes[i][1] === true)
{
attested.name = refscopes[i][0];
- this.missingAttestations[identity.pubkey].push(attested);
+ this.missingClaims[identity.pubkey].push(attested);
}
if (refscopes[i][1] === false)
{
attested.name = refscopes[i][0];
- this.optionalAttested[identity.pubkey].push(attested);
+ this.optionalClaims[identity.pubkey].push(attested);
}
}
}
private updateAttestations(identity) {
this.attestations[identity.pubkey] = [];
- this.requestedAttested[identity.pubkey] = [];
+ this.requestedClaims[identity.pubkey] = [];
+ this.optionalClaims[identity.pubkey] = [];
this.reclaimService.getAttestations(identity).subscribe(attestations => {
if (attestations !== null) {
this.attestations[identity.pubkey] = attestations;
@@ -148,29 +138,28 @@ export class IdentityListComponent implements OnInit {
});
}
+ getAttributeDescription(attr: Attribute): string {
+ return this.oidcService.getClaimDescription(attr);
+ }
+
+ sortAttributeByStandardClaims(mylist: string[]) {
+ return function(a1: Attribute, a2: Attribute) {
+ var claimNames = mylist;
+ let idx1 = claimNames.indexOf(a1.name);
+ let idx2 = claimNames.indexOf(a2.name);
+ if ((idx1 == -1) && (idx2 != -1)) { return 1;}
+ if ((idx2 == -1) && (idx1 != -1)) { return -1;}
+ if (idx1 > idx2) {return 1;}
+ if (idx1 < idx2) {return -1;}
+ return 0;
+ }
+ }
+
private updateAttributes(identity) {
this.reclaimService.getAttributes(identity).subscribe(attributes => {
- this.attributes[identity.pubkey] = [];
- this.requestedAttributes[identity.pubkey] = [];
- this.requestedAttested = [];
- if (attributes === null) {
- this.getMissingAttributes(identity);
- return;
- }
- let i;
- for (i = 0; i < attributes.length; i++) {
- this.attributes[identity.pubkey].push(attributes[i]);
- if ((attributes[i].flag === '0') &&
- this.oidcService.getScope().includes(attributes[i].name)) {
- this.requestedAttributes[identity.pubkey].push(attributes[i]);
- }
- if ((attributes[i].flag === '1') &&
- this.oidcService.getAttestedScope().includes(attributes[i].name)) {
- this.requestedAttested[identity.pubkey].push(attributes[i]);
- }
- }
- this.getMissingAttributes(identity);
- this.getMissingAttested(identity);
+ this.requestedClaims[identity.pubkey] = [];
+ this.attributes[identity.pubkey] =
attributes.sort(this.sortAttributeByStandardClaims(this.oidcService.getStandardClaimNames()));
+ this.updateMissingClaims(identity);
},
err => {
this.errorInfos.push("Error retrieving attributes for ``" +
identity.name + "''");
@@ -191,7 +180,7 @@ export class IdentityListComponent implements OnInit {
}
loginIdentity(identity) {
- this.oidcService.setAttestations(this.requestedAttested[identity.pubkey]);
+ this.oidcService.setAttestations(this.requestedClaims[identity.pubkey]);
this.oidcService.login(identity).subscribe(() => {
console.log('Successfully logged in');
this.authorize();
@@ -217,45 +206,119 @@ export class IdentityListComponent implements OnInit {
return this.oidcService.inOpenIdFlow();
}
- getScopes() { return this.oidcService.getScope(); }
+ getScopes() { return this.oidcService.getRequestedScope(); }
- getMissing(identity) {
+ getMissingClaims(identity) {
const arr = [];
let i = 0;
- for (i = 0; i < this.missingAttributes[identity.pubkey].length; i++) {
- arr.push(this.missingAttributes[identity.pubkey][i].name);
+ for (i = 0; i < this.missingClaims[identity.pubkey].length; i++) {
+ arr.push(this.missingClaims[identity.pubkey][i].name);
}
return arr;
}
- canAuthorize(identity) {
- return this.inOpenIdFlow();
- }
-
- isRequested(identity, attribute) {
- if (undefined === this.requestedAttributes[identity.pubkey]) {
+ isClaimRequested(identity, attribute) {
+ if (this.isProfileRequested() &&
+ this.oidcService.isStandardProfileClaim(attribute)) {
+ return true;
+ }
+ if (this.isEmailRequested() &&
+ this.oidcService.isStandardEmailClaim(attribute)) {
+ return true;
+ }
+ if (this.isPhoneRequested() &&
+ this.oidcService.isStandardPhoneClaim(attribute)) {
+ return true;
+ }
+ if (this.isAddressRequested() &&
+ this.oidcService.isStandardAddressClaim(attribute)) {
+ return true;
+ }
+ if (undefined === this.requestedClaims[identity.pubkey]) {
return false;
} else {
return -1 !==
- this.requestedAttributes[identity.pubkey].indexOf(attribute);
+ this.requestedClaims[identity.pubkey].indexOf(attribute);
}
}
- isAttributeMissing(identity) {
+ isProfileRequested() {
if (!this.inOpenIdFlow()) {
return false;
}
- if (undefined === this.requestedAttributes[identity.pubkey]) {
+ return this.oidcService.isProfileRequested();
+ }
+
+ isEmailRequested() {
+ if (!this.inOpenIdFlow()) {
return false;
}
- var scopes = this.getScopes();
- for (var i = 0; i < this.requestedAttributes[identity.pubkey].length; i++)
{
- if (!scopes.includes(this.requestedAttributes[identity.pubkey][i].name))
- {
- return true;
- }
+ return this.oidcService.isEmailRequested();
+ }
+
+ isPhoneRequested() {
+ if (!this.inOpenIdFlow()) {
+ return false;
}
- return false;
+ return this.oidcService.isPhoneRequested();
+ }
+
+ isAddressRequested() {
+ if (!this.inOpenIdFlow()) {
+ return false;
+ }
+ return this.oidcService.isAddressRequested();
+ }
+
+ isProfileMissing(identity) {
+ if (!this.inOpenIdFlow()) {
+ return false;
+ }
+ return this.oidcService.isProfileMissing(this.attributes[identity.pubkey]);
+ }
+
+ isEmailMissing(identity) {
+ if (!this.inOpenIdFlow()) {
+ return false;
+ }
+ return this.oidcService.isEmailMissing(this.attributes[identity.pubkey]);
+ }
+
+ isPhoneMissing(identity) {
+ if (!this.inOpenIdFlow()) {
+ return false;
+ }
+ return this.oidcService.isPhoneMissing(this.attributes[identity.pubkey]);
+ }
+
+ isRequestedScopeMissing(identity) {
+ return (this.isPhoneRequested() && this.isPhoneMissing(identity)) ||
+ (this.isEmailRequested() && this.isEmailMissing(identity)) ||
+ (this.isProfileRequested() && this.isProfileMissing(identity)) ||
+ (this.isAddressRequested() && this.isAddressMissing(identity));
+ }
+
+ isAddressMissing(identity) {
+ if (!this.inOpenIdFlow()) {
+ return false;
+ }
+ return this.oidcService.isAddressMissing(this.attributes[identity.pubkey]);
+ }
+
+ getProfileDescription() {
+ return this.oidcService.getScopeDescription("profile");
+ }
+
+ getEmailDescription() {
+ return this.oidcService.getScopeDescription("email");
+ }
+
+ getPhoneDescription() {
+ return this.oidcService.getScopeDescription("phone");
+ }
+
+ getAddressDescription() {
+ return this.oidcService.getScopeDescription("address");
}
hasAttributes(identity) {
@@ -297,36 +360,34 @@ export class IdentityListComponent implements OnInit {
return this.isConnected() && 0 != this.identities.length;
}
- isAttestedMissing(identity) {
+ isClaimMissing(identity) {
if (!this.inOpenIdFlow()) {
return false;
}
- if (undefined === this.requestedAttested) {
+ if (undefined === this.requestedClaims) {
return false;
}
- for (var i = 0; i < this.oidcService.getAttestedScope().length; i++) {
- if (this.oidcService.getAttestedScope()[i][1] === true) {
+ var claims = this.oidcService.getRequestedClaims();
+ for (var i = 0; i < claims.length; i++) {
+ if (claims[i][1] === true) {
var j;
- for (j = 0; j < this.requestedAttested.length; j++) {
- if (this.oidcService.getAttestedScope()[i][0] ===
this.requestedAttested[j].name){
- break;
+ for (j = 0; j < this.requestedClaims.length; j++) {
+ if (claims[i][0] === this.requestedClaims[j].name){
+ return true;
}
}
- if (j === this.requestedAttested.length){
- return true;
- }
}
}
return false;
}
isAttestedRequested(identity: Identity, attribute: Attribute) {
- if (undefined === this.requestedAttested[identity.pubkey]) {
+ if (undefined === this.requestedClaims[identity.pubkey]) {
return false;
} else {
- for (var j = 0; j < this.requestedAttested[identity.pubkey].length; j++)
{
+ for (var j = 0; j < this.requestedClaims[identity.pubkey].length; j++) {
if ((attribute.flag === '1') &&
- (attribute.name ===
this.requestedAttested[identity.pubkey][j].name)) {
+ (attribute.name ===
this.requestedClaims[identity.pubkey][j].name)) {
return true;
}
}
@@ -345,17 +406,17 @@ export class IdentityListComponent implements OnInit {
return false; //FIXME actually handle this
https://gitlab.com/voggenre/ui/commit/dd9b6656dee7dbf59809dcc9bc2508ee70d8afe6
}
- getOptional(identity) {
+ getOptionalClaims(identity) {
const arr = [];
let i = 0;
if (!this.inOpenIdFlow()) {
return [];
}
- if (undefined === this.optionalAttested[identity.pubkey]) {
+ if (undefined === this.optionalClaims[identity.pubkey]) {
return [];
}
- for (i = 0; i < this.optionalAttested[identity.pubkey].length; i++) {
- arr.push(this.optionalAttested[identity.pubkey][i].name);
+ for (i = 0; i < this.optionalClaims[identity.pubkey].length; i++) {
+ arr.push(this.optionalClaims[identity.pubkey][i].name);
}
return arr;
}
diff --git a/src/app/open-id.service.ts b/src/app/open-id.service.ts
index 7181445..6264783 100644
--- a/src/app/open-id.service.ts
+++ b/src/app/open-id.service.ts
@@ -14,6 +14,10 @@ export class OpenIdService {
clientNameVerified: Boolean;
clientName: String;
referenceString: String;
+ scopesDescriptions = {"profile": "User profile access such as email,
(nick)name, gender and birthdate.",
+ "email": "Your email information.",
+ "address": "Your physical address information.",
+ "phone": "Your phone number."};
constructor(private http: HttpClient,
private config: ConfigService,
@@ -121,7 +125,7 @@ export class OpenIdService {
return this.params['client_id'];
}
- getScope(): any {
+ getRequestedScope(): any {
if (!this.inOpenIdFlow()) {
return [];
}
@@ -130,36 +134,202 @@ export class OpenIdService {
}
const scopes = this.params['scope'].split(' ');
+ /* Ignore openid scope */
var i = scopes.indexOf('openid');
if (i >= 0)
{
scopes.splice(i, 1);
}
- i = scopes.indexOf('profile');
- if (i >= 0)
- {
- scopes.splice(i, 1);
- }
return scopes;
}
- getAttestedScope(): any {
+ getScopeDescription(scope: string) {
+ if (undefined === this.scopesDescriptions[scope]) {
+ return scope;
+ }
+ return this.scopesDescriptions[scope];
+ }
+
+ getScopesDescriptionList(): any {
+ var scopes = this.getRequestedScope();
+ var res = [];
+ for (var i = 0; i < scopes.length; i++) {
+ res.push(this.scopesDescriptions[scopes[i]])
+ }
+ return res;
+ }
+
+ isClaimsMissing(attributes: Attribute[], claims: Object): boolean {
+ for (let attr of attributes) {
+ for (let claim in claims) {
+ /* if any attribute for profile exists, we are good */
+ if (attr.name === claim) {
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+
+ isProfileMissing(attributes: Attribute[]): boolean {
+ let profileClaims = this.getStandardProfileClaims();
+ return this.isClaimsMissing(attributes, profileClaims);
+ }
+
+ isEmailMissing(attributes: Attribute[]): boolean {
+ let profileClaims = this.getStandardEmailClaims();
+ return this.isClaimsMissing(attributes, profileClaims);
+ }
+
+ isPhoneMissing(attributes: Attribute[]): boolean {
+ let profileClaims = this.getStandardPhoneClaims();
+ return this.isClaimsMissing(attributes, profileClaims);
+ }
+
+ isAddressMissing(attributes: Attribute[]): boolean {
+ let profileClaims = this.getStandardAddressClaims();
+ return this.isClaimsMissing(attributes, profileClaims);
+ }
+
+ isProfileRequested(): boolean {
+ return this.getRequestedScope().include("profile");
+ }
+
+ isEmailRequested(): boolean {
+ return this.getRequestedScope().include("email");
+ }
+
+ isPhoneRequested(): boolean {
+ return this.getRequestedScope().include("phone");
+ }
+
+ isAddressRequested(): boolean {
+ return this.getRequestedScope().include("address");
+ }
+
+ getStandardProfileClaims(): Object {
+ return {"family_name": "Family name",
+ "given_name": "Given name",
+ "middle_name": "Middle name",
+ "nickname": "Nickname",
+ "preferred_username": "Preferred username",
+ //"profile": "Profile URL",
+ "picture": "Picture URL",
+ "website": "Website URL",
+ "gender": "Gender",
+ //"birthdate": "Birthdate YYYY-MM-DD", FIXME make pretty calendar
+ //"zoneinfo": "Timezone, e.g. Europe/Paris", Make pretty dropdown
+ //"locale": "Locale, e.g. en-US" Make pretty dropdown
+ };
+ }
+
+ getStandardEmailClaims(): Object {
+ return {"email": "Email address"};
+ }
+
+ getStandardPhoneClaims(): Object {
+ return {"phone": "Phone number"};
+ }
+
+ getStandardAddressClaims(): Object {
+ return {"street_address": "Street",
+ "locality": "City",
+ "region": "State, province or prefecture",
+ "postal_code": "Zip code",
+ "country": "Country"};
+ }
+
+ /**
+ * Return all claim names requested implicitly
+ * using scope and explicitly using claims parameter.
+ */
+ getClaimNamesForRequest(): string[] {
+ var scopes = this.getRequestedScope();
+ var result = [];
+ if (scopes.includes("profile")) {
+ result = result.concat(Object.keys(this.getStandardProfileClaims()));
+ }
+ if (scopes.includes("email")) {
+ result = result.concat(Object.keys(this.getStandardEmailClaims()));
+ }
+ if (scopes.includes("address")) {
+ result = result.concat(Object.keys(this.getStandardAddressClaims()));
+ }
+ if (scopes.includes("phone")) {
+ result = result.concat(Object.keys(this.getStandardPhoneClaims()));
+ }
+ result = result.concat(this.getRequestedClaimNames());
+
+ return result;
+ }
+
+ getStandardClaimNames(): string[] {
+ var result = [];
+ result = result.concat(Object.keys(this.getStandardProfileClaims()));
+ result = result.concat(Object.keys(this.getStandardEmailClaims()));
+ result = result.concat(Object.keys(this.getStandardAddressClaims()));
+ result = result.concat(Object.keys(this.getStandardPhoneClaims()));
+ return result;
+ }
+
+ getClaimDescription(claim: Attribute) {
+ if (undefined !== this.getStandardProfileClaims()[claim.name]) {
+ return this.getStandardProfileClaims()[claim.name];
+ }
+ if (undefined !== this.getStandardEmailClaims()[claim.name]) {
+ return this.getStandardEmailClaims()[claim.name];
+ }
+ if (undefined !== this.getStandardAddressClaims()[claim.name]) {
+ return this.getStandardAddressClaims()[claim.name];
+ }
+ if (undefined !== this.getStandardPhoneClaims()[claim.name]) {
+ return this.getStandardPhoneClaims()[claim.name];
+ }
+ return claim.name;
+ }
+
+ isStandardProfileClaim(attribute: Attribute): boolean {
+ return -1 !=
Object.keys(this.getStandardProfileClaims()).indexOf(attribute.name);
+ }
+
+ isStandardEmailClaim(attribute: Attribute): boolean {
+ return -1 !=
Object.keys(this.getStandardEmailClaims()).indexOf(attribute.name);
+ }
+
+ isStandardPhoneClaim(attribute: Attribute): boolean {
+ return -1 !=
Object.keys(this.getStandardPhoneClaims()).indexOf(attribute.name);
+ }
+
+ isStandardAddressClaim(attribute: Attribute): boolean {
+ return -1 !=
Object.keys(this.getStandardAddressClaims()).indexOf(attribute.name);
+ }
+
+ getRequestedClaims(): any {
if (!this.inOpenIdFlow()) {
return [];
}
if ((this.params['claims'] === "") || (this.params['claims'] ===
undefined)) {
return [];
}
- var scope = [];
+ var claims = [];
var json = JSON.parse(this.params['claims'])['userinfo'];
for(var key in json)
{
if (json[key]['attestation'] === true)
{
- scope.push([key, json[key]['essential'], json[key]['attestation'],
json[key]['format']]);
+ claims.push([key, json[key]['essential'], json[key]['attestation'],
json[key]['format']]);
}
}
- return scope;
+ return claims;
+ }
+
+ getRequestedClaimNames(): string[] {
+ var claimNames = [];
+ var claims = this.getRequestedClaims();
+ for (let claim in claims) {
+ claimNames.push(claim[0]);
+ }
+ return claimNames;
}
}
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [reclaim-ui] 249/459: fix conflict, (continued)
- [reclaim-ui] 249/459: fix conflict, gnunet, 2021/06/11
- [reclaim-ui] 267/459: refactoring, style, gnunet, 2021/06/11
- [reclaim-ui] 276/459: more test, gnunet, 2021/06/11
- [reclaim-ui] 253/459: improve error/retry UX, gnunet, 2021/06/11
- [reclaim-ui] 232/459: update dependencies, gnunet, 2021/06/11
- [reclaim-ui] 278/459: fix display requested, gnunet, 2021/06/11
- [reclaim-ui] 281/459: fix, gnunet, 2021/06/11
- [reclaim-ui] 266/459: include(s), gnunet, 2021/06/11
- [reclaim-ui] 270/459: improve authz display, gnunet, 2021/06/11
- [reclaim-ui] 256/459: fix: ftbfs, gnunet, 2021/06/11
- [reclaim-ui] 261/459: towards cleanup of codebase and fixes,
gnunet <=
- [reclaim-ui] 271/459: fix, gnunet, 2021/06/11
- [reclaim-ui] 269/459: mark requested attributes, gnunet, 2021/06/11
- [reclaim-ui] 292/459: update, gnunet, 2021/06/11
- [reclaim-ui] 294/459: add padding, gnunet, 2021/06/11
- [reclaim-ui] 296/459: minor fix, gnunet, 2021/06/11
- [reclaim-ui] 272/459: fix, gnunet, 2021/06/11
- [reclaim-ui] 300/459: fix class on missing non-standard claims display, gnunet, 2021/06/11
- [reclaim-ui] 274/459: fix, gnunet, 2021/06/11
- [reclaim-ui] 297/459: fix claim processing, gnunet, 2021/06/11
- [reclaim-ui] 299/459: fix claim missing check, gnunet, 2021/06/11