gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[reclaim-ui] 172/459: manual merge from https://gitlab.com/voggenre/ui d


From: gnunet
Subject: [reclaim-ui] 172/459: manual merge from https://gitlab.com/voggenre/ui due to refactoring
Date: Fri, 11 Jun 2021 23:24:24 +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 08ad046c1125fb469eecb896d13dc6f06fedb372
Author: Schanzenbach, Martin <mschanzenbach@posteo.de>
AuthorDate: Fri Jan 17 16:39:38 2020 +0100

    manual merge from https://gitlab.com/voggenre/ui due to refactoring
---
 src/app/app-routing.module.ts                      |   4 +
 src/app/app.module.ts                              |   6 +-
 src/app/attestation.ts                             |   6 +
 src/app/attribute.ts                               |   8 +-
 .../authorization-request.component.html           |  10 +-
 .../authorization-request.component.ts             |   4 +
 .../edit-attestations.component.css                |   0
 .../edit-attestations.component.html               |  83 ++++
 .../edit-attestations.component.ts                 | 182 +++++++++
 .../edit-authorizations.component.css              |   0
 .../edit-authorizations.component.html             |  64 +++
 .../edit-authorizations.component.ts               | 144 +++++++
 src/app/edit-identity/edit-identity.component.html | 168 ++++++--
 src/app/edit-identity/edit-identity.component.ts   | 443 +++++++++++++++++----
 src/app/identity-list/identity-list.component.html |  21 +-
 src/app/identity-list/identity-list.component.ts   | 114 +++++-
 src/app/open-id.service.ts                         |  31 +-
 src/app/reclaim.service.ts                         |  46 ++-
 src/app/reference.ts                               |   6 +
 19 files changed, 1203 insertions(+), 137 deletions(-)

diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index 51b645f..bbe1986 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -3,6 +3,8 @@ import { RouterModule, Routes } from '@angular/router';
 import { IdentityListComponent } from 
'./identity-list/identity-list.component';
 import { NewIdentityComponent } from './new-identity/new-identity.component';
 import { EditIdentityComponent } from 
'./edit-identity/edit-identity.component';
+import { EditAuthorizationsComponent } from 
'./edit-authorizations/edit-authorizations.component';
+import { EditAttestationsComponent } from 
'./edit-attestations/edit-attestations.component';
 import { AuthorizationRequestComponent } from 
'./authorization-request/authorization-request.component';
 
 const routes: Routes = [
@@ -11,6 +13,8 @@ const routes: Routes = [
     { path: '', redirectTo: '/index.html', pathMatch: 'full' },
     { path: 'new-identity', component: NewIdentityComponent },
     { path: 'edit-identity/:id', component: EditIdentityComponent },
+    { path: 'edit-authorizations/:id', component: EditAuthorizationsComponent 
},
+    { path: 'edit-attestations/:id', component: EditAttestationsComponent },
     { path: 'authorization-request', component: AuthorizationRequestComponent }
 ];
 
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index d1c8b7a..13fe6db 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -18,6 +18,8 @@ import { OpenIdService } from './open-id.service';
 import { NewIdentityComponent } from './new-identity/new-identity.component';
 import { EditIdentityComponent } from 
'./edit-identity/edit-identity.component';
 import { AuthorizationRequestComponent } from 
'./authorization-request/authorization-request.component';
+import { EditAuthorizationsComponent } from 
'./edit-authorizations/edit-authorizations.component';
+import { EditAttestationsComponent } from 
'./edit-attestations/edit-attestations.component';
 
 @NgModule({
   declarations: [
@@ -27,7 +29,9 @@ import { AuthorizationRequestComponent } from 
'./authorization-request/authoriza
     SearchPipe,
     NewIdentityComponent,
     EditIdentityComponent,
-    AuthorizationRequestComponent
+    AuthorizationRequestComponent,
+    EditAuthorizationsComponent,
+    EditAttestationsComponent
   ],
   imports: [
     BrowserModule,
diff --git a/src/app/attestation.ts b/src/app/attestation.ts
new file mode 100644
index 0000000..a71d114
--- /dev/null
+++ b/src/app/attestation.ts
@@ -0,0 +1,6 @@
+export class Attestation {
+  constructor(public name: string,
+              public id: string,
+              public value: string,
+              public type: string) {}
+}
diff --git a/src/app/attribute.ts b/src/app/attribute.ts
index ccd7f46..f720887 100644
--- a/src/app/attribute.ts
+++ b/src/app/attribute.ts
@@ -1,5 +1,7 @@
 export class Attribute {
-  constructor(public name: string, public id: string, public value: string,
-              public type: string) {
-  }
+  constructor(public name: string,
+              public id: string,
+              public value: string,
+              public type: string,
+              public flag: string) {}
 }
diff --git a/src/app/authorization-request/authorization-request.component.html 
b/src/app/authorization-request/authorization-request.component.html
index 421b066..44fd346 100644
--- a/src/app/authorization-request/authorization-request.component.html
+++ b/src/app/authorization-request/authorization-request.component.html
@@ -15,10 +15,18 @@
   <div class="card-body">
     <strong>{{ oidcService.clientName }}</strong>
     asks you to share personal information.<br/>
-    Choose an identity to let it access the following information:
+    Choose an identity to let it access the following self-asserted 
information:
     <ul>
       <li *ngFor="let attribute of getScopes()"><strong>{{attribute}}</strong>
     </ul>
+    as well as the following third-party attested attributes:
+    <ul *ngIf="clientNameFound">
+    <ng-container *ngFor="let reference of getRefScope()">
+      <li *ngIf="reference[1] === true"><strong>{{reference[0]}}</strong>
+      <li *ngIf="reference[1] !== true"><strong>{{reference[0]}} 
(optional)</strong>
+    </ng-container>
+  </ul>
+
   </div>
   <div style="margin: 1em;">
     <button class="btn btn-danger m-1 mt-4" *ngIf="isClientVerified()" 
(click)="cancelRequest()" style="margin-bottom: -4%;">
diff --git a/src/app/authorization-request/authorization-request.component.ts 
b/src/app/authorization-request/authorization-request.component.ts
index ce0c7d1..72325e9 100644
--- a/src/app/authorization-request/authorization-request.component.ts
+++ b/src/app/authorization-request/authorization-request.component.ts
@@ -20,6 +20,10 @@ export class AuthorizationRequestComponent implements OnInit 
{
     return this.oidcService.getScope();
   }
 
+  getRedSCope() {
+    return this.oidcService.getRefScope();
+  }
+
   isClientVerified() {
     return this.oidcService.isClientVerified();
   }
diff --git a/src/app/edit-attestations/edit-attestations.component.css 
b/src/app/edit-attestations/edit-attestations.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/edit-attestations/edit-attestations.component.html 
b/src/app/edit-attestations/edit-attestations.component.html
new file mode 100644
index 0000000..6a8611f
--- /dev/null
+++ b/src/app/edit-attestations/edit-attestations.component.html
@@ -0,0 +1,83 @@
+<!-- Identity edit screen -->
+<div class="m-2 card">
+  <div class="card-avatar card-img-top">
+    <div class="card-avatar-character text-dark">
+      Manage attestations for <i>{{ identity.name }}</i>
+    </div>
+  </div>
+  <!-- Attestation management -->
+  <div class="card-body">
+    <h6 class="card-subtitle mb-2">Attestations:</h6>
+    <!-- Requested attestation -->
+    <table class="table pb-1" style="">
+      <thead>
+        <tr>
+          <th>Attestation Name</th>
+          <th>Attestation Type</th>
+          <th>Attestation Value</th>
+          <th>Isser</th>
+          <th>Attestation ID</th>
+        </tr>
+      </thead>
+      <tbody>
+        <tr [class.alert-danger]="newAttestation.name === attestation.name" 
[class.alert-warning]="!isAttestationValid(attestation.id)" *ngFor="let 
attestation of attestations">
+          <td><div style="min-width: 15em">{{attestation.name}}</div></td>
+          <td>
+            <div>{{attestation.type}}</div>
+          </td>
+          <td>
+            <input placeholder="Attestation Value" 
[(ngModel)]="attestation.value">
+          </td>
+          <td>
+            <div style="min-width: 15em">
+              {{attestation_val[attestation.id]['iss']}}
+            </div>
+          </td>
+          <td><div style="min-width: 15em">{{attestation.id}}</div></td>
+          <td>
+            <button class="btn btn-primary"  
(click)="deleteAttestation(attestation)">
+              <span class="fa fa-trash"></span>
+            </button>
+          </td>
+          <td>
+              <div *ngIf= "isAttestationValid(attestation.id)"> Valid <span 
class="fa fa-check"></span> </div>
+              <div *ngIf= "!isAttestationValid(attestation.id)"> <span 
style="color:#f00"> Expired </span> <span class="fa fa-times"></span> </div>
+            </td>
+        </tr>
+        <tr [class.alert-danger]="isAttestInConflict(newAttestation)">
+          <td>
+            <input [class.text-danger]="!attestationNameValid(newAttestation)" 
placeholder="Attestation" [(ngModel)]="newAttestation.name">
+          </td>
+          <td>
+            <input placeholder="Type [JWT]" 
[class.text-danger]="!attestationTypeValid(newAttestation)" 
[(ngModel)]="newAttestation.type">
+          </td>
+          <td>
+            <input placeholder="Attestation Value" 
[class.text-danger]="!attestationValueValid(newAttestation)" 
[(ngModel)]="newAttestation.value">
+          </td>
+          <td>
+            <button [disabled]="!canAddAttestation(newAttestation)" class="btn 
btn-primary"  (click)="addAttestation(newAttestation)">
+              <span class="fa fa-plus"></span> 
+            </button>
+          </td>
+        </tr>
+      </tbody>
+    </table>
+    <!-- Attestation creation warning -->
+    <div *ngIf="!attestationNameValid(newAttestation) || 
!attestationTypeValid(newAttestation) || 
!attestationValueValid(newAttestation)" class="alert alert-primary 
alert-dismissible fade show" role="alert">
+      <span class="fa fa-warning"></span> Note:
+      <ul>
+        <li>Only use alphanumeric attestation names.</li>
+        <li>You cannot define the same name twice.</li>
+        <li>Types and values may not be empty!</li>
+      </ul>
+    </div>
+
+    <!-- Edit card buttons -->
+    <div>
+      <button class="btn btn-primary" (click)="saveAttestation()" 
[disabled]="!canSaveAttestation()">
+        <span class="fa fa-save"></span> Save and Back
+      </button>
+    </div>
+  </div>
+</div>
+
diff --git a/src/app/edit-attestations/edit-attestations.component.ts 
b/src/app/edit-attestations/edit-attestations.component.ts
new file mode 100644
index 0000000..77e23fd
--- /dev/null
+++ b/src/app/edit-attestations/edit-attestations.component.ts
@@ -0,0 +1,182 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { ReclaimService } from '../reclaim.service';
+import { Identity } from '../identity';
+import { Attestation } from '../attestation';
+import { IdentityService } from '../identity.service';
+import { from, forkJoin, EMPTY } from 'rxjs';
+import { finalize } from 'rxjs/operators';
+
+@Component({
+  selector: 'app-edit-attestations',
+  templateUrl: './edit-attestations.component.html',
+  styleUrls: ['./edit-attestations.component.css']
+})
+export class EditAttestationsComponent implements OnInit {
+
+  identity: Identity;
+  attestations: Attestation[];
+  newAttestation: Attestation;
+
+  constructor(private reclaimService: ReclaimService,
+              private identityService: IdentityService,
+              private activatedRoute: ActivatedRoute,
+              private router: Router) { }
+
+  ngOnInit() {
+    this.newAttestation = new Attestation('', '', '', '');
+    this.identity = new Identity('','');
+    this.activatedRoute.params.subscribe(p => {
+      if (p['id'] === undefined) {
+        return;
+      }
+      this.identityService.getIdentities().subscribe(
+        ids => {
+          for (let i = 0; i < ids.length; i++) {
+            if (ids[i].name == p['id']) {
+              this.identity = ids[i];
+              this.updateAttestation();
+            }
+          }
+        });
+    });
+
+  }
+
+  private updateAttestation() {
+    this.reclaimService.getAttestation(this.identity).subscribe(attestation => 
{
+      this.attestations = [];
+      let i;
+      for (i = 0; i < attestation.length; i++) {
+        this.attestations.push(attestation[i]);
+      }
+    },
+    err => {
+      //this.errorInfos.push("Error retrieving attestation for ``" + 
identity.name + "''");
+      console.log(err);
+    });
+  }
+
+  addAttestation() {
+    this.storeAttestation()
+    .pipe(
+      finalize(() => {
+        this.newAttestation.name = '';
+        this.newAttestation.type = '';
+        this.newAttestation.value = '';
+        this.updateAttestation();
+      }))
+      .subscribe(res => {
+        console.log(res);
+      },
+      err => {
+        console.log(err);
+        //this.errorInfos.push("Failed to update identity ``" +  
this.identityInEdit.name + "''");
+        EMPTY
+      });
+  }
+
+  private storeAttestation() {
+    const promises = [];
+    let i;
+    if (undefined !== this.attestations) {
+      for (i = 0; i < this.attestations.length; i++) {
+        promises.push(
+          from(this.reclaimService.addAttestation(this.identity, 
this.attestations[i])));
+      }
+    }
+    if ((this.newAttestation.value !== '') || (this.newAttestation.type !== 
'')) {
+      promises.push(from(this.reclaimService.addAttestation(this.identity, 
this.newAttestation)));
+    }
+    return forkJoin(promises);
+  }
+
+  canSaveAttestation() {
+    if (this.canAddAttestation(this.newAttestation)) {
+      return true;
+    }
+    return ((this.newAttestation.name === '') &&
+      (this.newAttestation.value === '') &&
+      (this.newAttestation.type === '')) &&
+      !this.isAttestInConflict(this.newAttestation);
+  }
+
+  isAttestInConflict(attestation: Attestation) {
+    let i;
+    if (undefined !== this.attestations) {
+      for (i = 0; i < this.attestations.length; i++) {
+        if (attestation.name === this.attestations[i].name) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  saveAttestation() {
+    this.storeAttestation()
+      .pipe(
+        finalize(() => {
+          this.newAttestation.name = '';
+          this.newAttestation.value = '';
+          this.newAttestation.type = '';
+          this.router.navigate(['/edit-identity', this.identity.name]);
+        }))
+      .subscribe(res => {
+        //FIXME success dialog/banner
+        this.updateAttestation();
+      },
+      err => {
+        console.log(err);
+        //this.errorInfos.push("Failed to update identity ``" +  
this.identityInEdit.name + "''");
+      });
+  }
+
+
+  deleteAttestation(attestation: Attestation) {
+    this.reclaimService.deleteAttestation(this.identity, attestation)
+      .subscribe(res => {
+        //FIXME info dialog
+        this.updateAttestation();
+      },
+      err => {
+        //this.errorInfos.push("Failed to delete attestation ``" + 
attestation.name + "''");
+        console.log(err);
+      });
+  }
+
+  canAddAttestation(attestation: Attestation) {
+    if ((attestation.name === '') || (attestation.value === '') || 
(attestation.type === '')) {
+      return false;
+    }
+    if (attestation.name.indexOf(' ') >= 0) {
+      return false;
+    }
+    return !this.isAttestInConflict(attestation);
+  }
+
+  attestationNameValid(attestation: Attestation) {
+    if (attestation.name === '' && attestation.value === '' && 
attestation.type === '') {
+      return true;
+    }
+    if (attestation.name.indexOf(' ') >= 0) {
+      return false;
+    }
+    if (!/^[a-zA-Z0-9-]+$/.test(attestation.name)) {
+      return false;
+    }
+    return !this.isAttestInConflict(attestation);
+  }
+
+  attestationTypeValid(attestation: Attestation) {
+    if (attestation.type === '') {
+      return attestation.name === '';
+    }
+    return true;
+  }
+
+  attestationValueValid(attestation: Attestation) {
+    return true;
+  }
+
+}
diff --git a/src/app/edit-authorizations/edit-authorizations.component.css 
b/src/app/edit-authorizations/edit-authorizations.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/edit-authorizations/edit-authorizations.component.html 
b/src/app/edit-authorizations/edit-authorizations.component.html
new file mode 100644
index 0000000..ddb918d
--- /dev/null
+++ b/src/app/edit-authorizations/edit-authorizations.component.html
@@ -0,0 +1,64 @@
+<!-- Identity edit screen -->
+<div class="m-2 card">
+  <div class="card-avatar card-img-top">
+    <div class="card-avatar-character text-dark">
+      Manage authorizations for <i>{{ identity.name }}</i>
+    </div>
+  </div>
+  <div class="card-body">
+    <!-- Authorized entities -->
+    <div style="margin-top: 1.5em;">
+      <table class="table pb-1">
+        <thead style="border-top-style: hidden">
+          <tr>
+            <th scope="col">
+              <h6 class="card-subtitle mb-2">
+                Authorized Entity:
+              </h6>
+            </th>
+            <th scope="col">
+              <h6 class="card-subtitle mb-2">
+                Shared attributes:
+              </h6>
+            </th>
+            <th scope="col"></th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr *ngFor="let ticket of tickets">
+            <td>
+              <div style="min-width: 15em">
+                {{getAudienceName(ticket)}}
+              </div>
+            </td>
+            <td>
+              <div style="min-width: 15em">
+                {{ticketAttributeMapper[ticket.audience]}}
+              </div>
+            </td>
+            <td>
+              <button class="btn btn-primary" *ngIf="showConfirmRevoke != 
ticket" (click)="confirmRevoke(ticket)">
+                <span class="fa fa-unlink"></span> Revoke
+              </button>
+              <div class="alert alert-danger fade show" 
*ngIf="showConfirmRevoke == ticket">
+                Do you really want to revoke this authorization?<br/><br/>
+                <button class="btn btn-primary m-2" 
(click)="revokeTicket(ticket)">
+                  <span class="fa fa-check"></span> Yes
+                </button>
+                <button class="btn btn-primary m-2" 
(click)="hideConfirmRevoke()">
+                  <span class="fa fa-close"></span> No
+                </button>
+              </div>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+    <!-- Edit card buttons -->
+    <div>
+      <button class="btn btn-primary" [routerLink]="['/edit-identity', 
identity.name]">
+        <span class="fa fa-back"></span> Back to {{identity.name}}
+      </button>
+    </div>
+  </div>
+</div>
diff --git a/src/app/edit-authorizations/edit-authorizations.component.ts 
b/src/app/edit-authorizations/edit-authorizations.component.ts
new file mode 100644
index 0000000..79b2efe
--- /dev/null
+++ b/src/app/edit-authorizations/edit-authorizations.component.ts
@@ -0,0 +1,144 @@
+import { Component, OnInit } from '@angular/core';
+import { Ticket } from '../ticket';
+import { Identity } from '../identity';
+import { Attribute } from '../attribute';
+import { ReclaimService } from '../reclaim.service';
+import { ActivatedRoute } from '@angular/router';
+import { IdentityService } from '../identity.service';
+import { GnsService } from '../gns.service';
+import { NamestoreService } from '../namestore.service';
+
+@Component({
+  selector: 'app-edit-authorizations',
+  templateUrl: './edit-authorizations.component.html',
+  styleUrls: ['./edit-authorizations.component.css']
+})
+export class EditAuthorizationsComponent implements OnInit {
+
+  tickets: Ticket[];
+  audienceNames: {};
+  identity: Identity;
+  ticketAttributeMapper: {};
+  attributes: Attribute[];
+  showConfirmRevoke: any;
+
+  constructor(private reclaimService: ReclaimService,
+              private activatedRoute: ActivatedRoute,
+              private identityService: IdentityService,
+              private gnsService: GnsService,
+              private namestoreService: NamestoreService) { }
+
+  ngOnInit() {
+    this.tickets = [];
+    this.identity = new Identity('','');
+    this.attributes = [];
+    this.audienceNames = {};
+    this.ticketAttributeMapper = {};
+    this.showConfirmRevoke = false;
+    this.activatedRoute.params.subscribe(p => {
+      if (p['id'] === undefined) {
+        return;
+      }
+      this.identityService.getIdentities().subscribe(
+        ids => {
+          for (let i = 0; i < ids.length; i++) {
+            if (ids[i].name == p['id']) {
+              this.identity = ids[i];
+              this.updateAttributes();
+            }
+          }
+        });
+    });
+
+  }
+
+  getAudienceName(ticket) {
+    if (undefined === this.audienceNames[ticket.audience]) {
+      return 'Unknown';
+    }
+    return this.audienceNames[ticket.audience];
+  }
+
+
+  private updateTickets() {
+    this.reclaimService.getTickets(this.identity).subscribe(tickets => {
+      this.tickets = [];
+      if (tickets === null) {
+        return;
+      }
+      this.tickets = tickets;
+      tickets.forEach(ticket => {
+        this.mapAudience(ticket);
+        this.mapAttributes(ticket);
+      });
+    },
+    err => {
+      //this.errorInfos.push("Unable to retrieve tickets for identity ``" + 
identity.name + "''");
+      console.log(err);
+    });
+  }
+
+  private mapAttributes(ticket) {
+    this.namestoreService.getNames(this.identity).subscribe(names => {
+      this.ticketAttributeMapper[ticket.audience] = [];
+      names = names.filter(name => name.record_name === 
ticket.rnd.toLowerCase());
+      for (let i = 0; i < names.length; i++) {
+        names[i].data.forEach(record => {
+          if (record.record_type === 'RECLAIM_ATTR_REF') {
+            this.attributes
+              .filter(attr => attr.id === record.value)
+              .map(attr => {
+                this.ticketAttributeMapper[ticket.audience].push(attr.name);
+              });
+          }
+        });
+      }
+    });
+  }
+
+  hideConfirmRevoke() { this.showConfirmRevoke = null; }
+
+  confirmRevoke(ticket) { this.showConfirmRevoke = ticket;}
+
+  revokeTicket(ticket) {
+    this.reclaimService.revokeTicket(ticket).subscribe(
+      result => {
+        this.updateAttributes();
+      },
+      err => {
+        //this.errorInfos.push("Unable to revoke ticket.");
+        console.log(err);
+      });
+  }
+
+  private updateAttributes() {
+    this.reclaimService.getAttributes(this.identity).subscribe(attributes => {
+      this.attributes = [];
+      if (attributes === null) {
+        return;
+      }
+      this.attributes = attributes;
+      this.updateTickets();
+    },
+    err => {
+      //this.errorInfos.push("Error retrieving attributes for ``" + 
identity.name + "''");
+      console.log(err);
+    });
+  }
+
+
+
+  private mapAudience(ticket) {
+    this.gnsService.getClientName(ticket.audience).subscribe(records => {
+      for (let i = 0; i < records.data.length; i++) {
+        if (records.data[i].record_type !== 'RECLAIM_OIDC_CLIENT') {
+          continue;
+        }
+        this.audienceNames[ticket.audience] = records.data[i].value;
+        break;
+      }
+    });
+  }
+
+
+}
diff --git a/src/app/edit-identity/edit-identity.component.html 
b/src/app/edit-identity/edit-identity.component.html
index 0820c55..caf82ef 100644
--- a/src/app/edit-identity/edit-identity.component.html
+++ b/src/app/edit-identity/edit-identity.component.html
@@ -9,8 +9,48 @@
   <div class="card-body">
     <div>
       <h6 class="card-subtitle mb-2">Attributes:</h6>
+      <!-- Missing references -->
+      <table class="table pb-1" *ngIf="isReferenceMissing()">
+        <thead>
+          <tr>
+              <th>Attribute Name</th>
+              <th>Attribute Value</th>
+              <th>Attestation ID</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr [class.openid]="inOpenIdFlow()" 
[class.alert-danger]="newReference.name === missing.name" class="text-primary" 
*ngFor="let missing of missingReferences">
+            <td><div style="min-width: 15em">{{missing.name}}</div></td>
+            <td>
+<select *ngIf="missing.ref_id !== ''" 
(change)="missing.ref_value=$event.target.value">
+                <option value="">Select a Claim</option>
+                <option *ngFor="let claim of 
objectKeys(attestation_val[missing.ref_id])" value={{claim}}>
+                  {{claim}}
+                </option>
+                <option 
value="">{{attestation_val[missing.ref_id]['iss']}}</option>
+              </select>
+            </td>
+            <td>
+              <select (change)="missing.ref_id=$event.target.value; ">
+              <option value="">Select an Attestation</option>
+              <option *ngFor="let attest of attestations" value={{attest.id}}>
+                {{attest.name}}
+              </option>
+            </select>
+            </td>
+            <td>
+            </td>
+          </tr>
+        </tbody>
+      </table>
       <!-- Missing attributes -->
       <table class="table pb-1" *ngIf="isAttributeMissing()">
+        <thead>
+          <tr>
+              <th>Attribute Name</th>
+              <th>Attribute Value</th>
+          </tr>
+        </thead>
         <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>
@@ -24,14 +64,20 @@
       </table>
       <!-- Requested attributes -->
       <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" *ngFor="let 
attribute of attributes">
+          <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>
             <td>
               <input placeholder="Value" [(ngModel)]="attribute.value">
             </td>
             <td>
-              <button class="btn btn-primary" 
(click)="deleteAttribute(attribute)">
+              <button class="btn btn-primary" 
(click)="deleteAttribute(attribute)" *ngIf="!isAttestation(attribute)">
                 <span class="fa fa-trash"></span>
               </button>
             </td>
@@ -61,65 +107,107 @@
         <li>Attribute values may not be empty!</li>
       </ul>
     </div>
-    <!-- Authorized entities -->
-    <div style="margin-top: 1.5em;">
-      <table class="table pb-1" *ngIf="showTickets">
-        <thead style="border-top-style: hidden">
+    <div>
+      <button class="btn btn-primary" (click)="toggleShowRef()" 
[style.float]="'right'">
+        <span *ngIf="showReferences"> Hide</span>
+        <span *ngIf="!showReferences"> Show</span> 
+        reference management
+      </button>
+    </div>
+    <div style="margin-top: 1.5em;" *ngIf="showReferences">   
+      <h6 class="card-subtitle mb-2">References:</h6>
+      <!-- Requested references -->
+      <table class="table pb-1" style="">
+        <thead *ngIf="!isReferenceMissing()">
           <tr>
-            <th scope="col">
-              <h6 class="card-subtitle mb-2">
-                Authorized Entities:
-              </h6>
-            </th>
-            <th scope="col">
-              <h6 class="card-subtitle mb-2">
-                Shared attributes:
-              </h6>
-            </th>
-            <th scope="col"></th>
+              <th>Attribute Name</th>
+              <th>Attribute Value</th>
+              <th>Attestation ID</th>
           </tr>
         </thead>
         <tbody>
-          <tr *ngFor="let ticket of tickets">
+          <tr [class.openid]="inOpenIdFlow()" 
[class.text-primary]="isRefRequested(reference)" 
[class.text-secondary]="isoptRefRequested(reference)" 
[class.alert-danger]="newReference.name === reference.name" 
[class.alert-warning]="!isAttestationValid(reference.ref_id)" *ngFor="let 
reference of references">
+            <td><div style="min-width: 15em">{{reference.name}}</div></td>
             <td>
-              <div style="min-width: 15em">
-                {{getAudienceName(ticket)}}
-              </div>
+              <input placeholder="Claim Name Attestation" 
[(ngModel)]="reference.ref_value">
             </td>
             <td>
-              <div style="min-width: 15em">
-                {{ticketAttributeMapper[ticket.audience]}}
-              </div>
+              <input placeholder="Attestation ID" 
[(ngModel)]="reference.ref_id">
             </td>
             <td>
-              <button class="btn btn-primary" *ngIf="showConfirmRevoke != 
ticket" (click)="confirmRevoke(ticket)">
-                <span class="fa fa-unlink"></span> Revoke
+              <button class="btn btn-primary"  
(click)="deleteReference(reference)">
+                <span class="fa fa-trash"></span>
+              </button>
+            </td>
+            <td>
+              <div *ngIf= "isAttestationValid(reference.ref_id)"> Valid 
Reference <span class="fa fa-check"></span> </div>
+              <div *ngIf= "!isAttestationValid(reference.ref_id)"> <span 
style="color:#f00"> Expired Attestation </span><span class="fa 
fa-times"></span> </div>
+            </td>
+
+          </tr>
+          <tr [class.alert-danger]="isRefInConflict(newReference)">
+            <td>
+              <input [class.text-danger]="!referenceNameValid(newReference)" 
placeholder="Reference" [(ngModel)]="newReference.name">
+            </td>
+            <td>
+              <select *ngIf="newReference.ref_id !== ''" 
(change)="newReference.ref_value=$event.target.value">
+                <option value="">Select a Claim</option>
+                <option *ngFor="let claim of 
objectKeys(attestation_val[newReference.ref_id])" value={{claim}}>
+                  {{claim}}
+                </option>
+                <option 
value="">{{attestation_val[newReference.ref_id]['iss']}}</option>
+              </select>
+
+            </td>
+            <td>
+              <select (change)="newReference.ref_id=$event.target.value; ">
+              <option value="">Select an Attestation</option>
+              <option *ngFor="let attest of attestations" value={{attest.id}}>
+                {{attest.name}}
+              </option>
+            </select>
+            </td>
+
+            <td>
+              <button [disabled]="!canAddReference(newReference)" class="btn 
btn-primary"  (click)="addReference(newReference)">
+                <span class="fa fa-plus"></span> 
               </button>
-              <div class="alert alert-danger fade show" 
*ngIf="showConfirmRevoke == ticket">
-                Do you really want to revoke this authorization?<br/><br/>
-                <button class="btn btn-primary m-2" 
(click)="revokeTicket(ticket)">
-                  <span class="fa fa-check"></span> Yes
-                </button>
-                <button class="btn btn-primary m-2" 
(click)="hideConfirmRevoke()">
-                  <span class="fa fa-close"></span> No
-                </button>
-              </div>
             </td>
           </tr>
         </tbody>
       </table>
     </div>
+    <!-- optional attributes information -->
+    <div *ngIf="optionalReferences.length !== 0" class="text-primary">
+      <span class="fa fa-openid"></span> Optionally requested is also:
+      <ul>
+        <li *ngFor="let reference of 
optionalReferences"><strong>{{reference}}</strong>
+      </ul>
+    </div>
+    <!-- Reference creation warning -->
+    <div *ngIf="!referenceNameValid(newReference) || 
!referenceValueValid(newReference) || !referenceIDValid(newReference)" 
class="alert alert-primary alert-dismissible fade show" role="alert">
+      <span class="fa fa-warning"></span> Note:
+      <ul>
+        <li>Only use alphanumeric reference names.</li>
+        <li>You cannot define the same name twice.</li>
+        <li>IDs and values may not be empty!</li>
+      </ul>
+    </div>
+
     <!-- Edit card buttons -->
     <div>
-      <button class="btn btn-primary" (click)="saveIdentityAttributes()" 
[disabled]="!canSaveIdentity()">
+      <button class="btn btn-primary" (click)="saveIdentity()" 
[disabled]="!canSaveIdentity()">
         <span class="fa fa-save"></span> Save and Back
       </button>
-        <button *ngIf="(0 < tickets.length) && !inOpenIdFlow()" class="btn 
btn-primary" (click)="toggleShowTickets()" [style.float]="'right'">
+      <button *ngIf="!inOpenIdFlow()" class="btn btn-primary" 
[routerLink]="['/edit-authorizations', identity.name]" [style.float]="'right'">
         <span class="fa fa-openid"></span>
-        <span *ngIf="showTickets"> Hide</span>
-        <span *ngIf="!showTickets"> Show</span> 
-        authorizations
+        Manage authorizations
       </button>
+      <button *ngIf="!inOpenIdFlow()" class="btn btn-primary" 
[routerLink]="['/edit-attestations', identity.name]" [style.float]="'right'">
+        <span class="fa fa-openid"></span>
+        Manage attestations
+      </button>
+
     </div>
   </div>
 </div>
diff --git a/src/app/edit-identity/edit-identity.component.ts 
b/src/app/edit-identity/edit-identity.component.ts
index 464237f..37f697d 100644
--- a/src/app/edit-identity/edit-identity.component.ts
+++ b/src/app/edit-identity/edit-identity.component.ts
@@ -1,12 +1,13 @@
 import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
 import { ReclaimService } from '../reclaim.service';
-import { Ticket } from '../ticket';
 import { Identity } from '../identity';
 import { GnsService } from '../gns.service';
 import { NamestoreService } from '../namestore.service';
 import { OpenIdService } from '../open-id.service';
 import { Attribute } from '../attribute';
+import { Attestation } from '../attestation';
+import { Reference } from '../reference';
 import { IdentityService } from '../identity.service';
 import { finalize } from 'rxjs/operators';
 import { from, forkJoin, EMPTY } from 'rxjs';
@@ -18,16 +19,19 @@ import { from, forkJoin, EMPTY } from 'rxjs';
 })
 export class EditIdentityComponent implements OnInit {
 
-  tickets: Ticket[];
   identity: Identity;
-  audienceNames: {};
-  showTickets: Boolean;
-  ticketAttributeMapper: {};
+  showReferences: Boolean;
   attributes: Attribute[];
+  attestations: Attestation[];
+  attestationValues: {};
   requestedAttributes: Attribute[];
   missingAttributes: Attribute[];
   newAttribute: Attribute;
-  showConfirmRevoke: any;
+  references: Reference[];
+  newReference: Reference;
+  missingReferences: Reference[];
+  requestedReferences: Reference[];
+  optionalReferences: Reference[];
 
   constructor(private reclaimService: ReclaimService,
               private identityService: IdentityService,
@@ -38,14 +42,14 @@ export class EditIdentityComponent implements OnInit {
               private router: Router) { }
 
   ngOnInit() {
-    this.tickets = [];
     this.attributes = [];
-    this.showConfirmRevoke = null;
-    this.audienceNames = {};
+    this.attestations = [];
+    this.optionalReferences = [];
+    this.attestationValues = {};
     this.identity = new Identity('','');
-    this.newAttribute = new Attribute('', '', '', 'STRING');
-    this.ticketAttributeMapper = {};
-    this.activatedRoute.params.subscribe(p => {
+    this.newAttribute = new Attribute('', '', '', 'STRING', '');
+    this.newReference = new Reference('', '', '', '');
+      this.activatedRoute.params.subscribe(p => {
       if (p['id'] === undefined) {
         return;
       }
@@ -55,79 +59,16 @@ export class EditIdentityComponent implements OnInit {
             if (ids[i].name == p['id']) {
               this.identity = ids[i];
               this.updateAttributes();
+              this.updateReferences();
+              this.updateAttestations();
             }
           }
         });
     });
   }
 
-  confirmRevoke(ticket) { this.showConfirmRevoke = ticket;}
 
-  hideConfirmRevoke() { this.showConfirmRevoke = null; }
 
-  private mapAudience(ticket) {
-    this.gnsService.getClientName(ticket.audience).subscribe(records => {
-      for (let i = 0; i < records.data.length; i++) {
-        if (records.data[i].record_type !== 'RECLAIM_OIDC_CLIENT') {
-          continue;
-        }
-        this.audienceNames[ticket.audience] = records.data[i].value;
-        break;
-      }
-    });
-  }
-
-  private updateTickets() {
-    this.reclaimService.getTickets(this.identity).subscribe(tickets => {
-      this.tickets = [];
-      if (tickets === null) {
-        return;
-      }
-      this.tickets = tickets;
-      tickets.forEach(ticket => {
-        this.mapAudience(ticket);
-        this.mapAttributes(ticket);
-      });
-    },
-    err => {
-      //this.errorInfos.push("Unable to retrieve tickets for identity ``" + 
identity.name + "''");
-      console.log(err);
-    });
-  }
-  
-  private mapAttributes(ticket) {
-    this.namestoreService.getNames(this.identity).subscribe(names => {
-      this.ticketAttributeMapper[ticket.audience] = [];
-      names = names.filter(name => name.record_name === 
ticket.rnd.toLowerCase());
-      for (let i = 0; i < names.length; i++) {
-        names[i].data.forEach(record => {
-          if (record.record_type === 'RECLAIM_ATTR_REF') {
-            this.attributes
-              .filter(attr => attr.id === record.value)
-              .map(attr => {
-                this.ticketAttributeMapper[ticket.audience].push(attr.name);
-              });
-          }
-        });
-      }
-    });
-  }
-
-  toggleShowTickets(identity) {
-    this.showTickets = !this.showTickets;
-  }
-
-  revokeTicket(ticket) {
-    this.reclaimService.revokeTicket(ticket).subscribe(
-      result => {
-        this.updateAttributes();
-      },
-      err => {
-        //this.errorInfos.push("Unable to revoke ticket.");
-        console.log(err);
-      });
-  }
-  
   private updateAttributes() {
     this.reclaimService.getAttributes(this.identity).subscribe(attributes => {
       this.attributes = [];
@@ -144,7 +85,6 @@ export class EditIdentityComponent implements OnInit {
         }
       }
       this.getMissingAttributes();
-      this.updateTickets();
     },
     err => {
       //this.errorInfos.push("Error retrieving attributes for ``" + 
identity.name + "''");
@@ -177,7 +117,7 @@ export class EditIdentityComponent implements OnInit {
     }
     this.missingAttributes = [];
     for (i = 0; i < scopes.length; i++) {
-      const attribute = new Attribute('', '', '', 'STRING');
+      const attribute = new Attribute('', '', '', 'STRING', '');
       attribute.name = scopes[i];
       this.missingAttributes.push(attribute);
     }
@@ -214,6 +154,11 @@ export class EditIdentityComponent implements OnInit {
   }
 
   canSaveIdentity() {
+    return (this.canSaveAttribute() &&
+            this.canSaveReference());
+  }
+
+  canSaveAttribute() {
     if (this.canAddAttribute(this.newAttribute)) {
       return true;
     }
@@ -222,6 +167,43 @@ export class EditIdentityComponent implements OnInit {
       !this.isInConflict(this.newAttribute);
   }
 
+  canSaveReference() {
+    if (this.canAddReference(this.newReference)) {
+      return true;
+    }
+    return ((this.newReference.name === '') &&
+      (this.newReference.ref_value === '') &&
+      (this.newReference.ref_id === '')) &&
+      !this.isRefInConflict(this.newReference);
+  }
+
+
+  isRefInConflict(reference) {
+    let i;
+    if (undefined !== this.missingReferences) {
+      for (i = 0; i < this.missingReferences.length; i++) {
+        if (reference.name ===
+          this.missingReferences[i].name) {
+          return true;
+        }
+      }
+    }
+    if (undefined !== this.references) {
+      for (i = 0; i < this.references.length; i++) {
+        if (reference.name === this.references[i].name) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+
+  saveIdentity() {
+    this.saveIdentityAttributes();
+    this.saveIdentityReferences();
+  }
+
   saveIdentityAttributes() {
     this.storeAttributes()
       .pipe(
@@ -268,6 +250,9 @@ export class EditIdentityComponent implements OnInit {
     }
     if (undefined !== this.attributes) {
       for (i = 0; i < this.attributes.length; i++) {
+        if (this.attributes[i].flag === '1') {
+          continue; //Is an attestation
+        }
         promises.push(
           from(this.reclaimService.addAttribute(this.identity, 
this.attributes[i])));
       }
@@ -329,11 +314,303 @@ export class EditIdentityComponent implements OnInit {
       this.requestedAttributes.length;
   }
 
-  getAudienceName(ticket) {
-    if (undefined === this.audienceNames[ticket.audience]) {
-      return 'Unknown';
+  private saveIdentityReferences() {
+    this.storeReferences()
+      .pipe(
+        finalize(() => {
+          this.newReference.name = '';
+          this.newReference.ref_value = '';
+          this.newReference.ref_id = '';
+        }))
+      .subscribe(res => {
+        //FIXME success dialog/banner
+        this.updateReferences();
+      },
+      err => {
+        console.log(err);
+        //this.errorInfos.push("Failed to update identity ``" +  
this.identityInEdit.name + "''");
+      });
+  }
+
+  deleteReference(reference) {
+    this.reclaimService.deleteReference(this.identity, reference)
+      .subscribe(res => {
+        //FIXME info dialog
+        this.updateReferences();
+        this.updateAttributes();
+      },
+      err => {
+        //this.errorInfos.push("Failed to delete reference ``" + 
reference.name + "''");
+        console.log(err);
+      });
+  }
+
+
+  getMissingReferences() {
+    const refscopes = this.oidcService.getRefScope();
+    let i;
+    for (i = 0; i < this.requestedReferences.length; i++) {
+      for (var j = 0; j < refscopes.length; j++) {
+        if (this.requestedReferences[i].name === refscopes[j][0] ) {
+          refscopes.splice(j,1);
+        }
+      }
+    }
+    this.missingReferences = [];
+    this.optionalReferences = [];
+    for (i = 0; i < refscopes.length; i++) {
+      const reference = new Reference('', '', '', '');
+      if (refscopes[i][1] === true)
+      {
+        reference.name = refscopes[i][0];
+        this.missingReferences.push(reference);
+      }
+      if (refscopes[i][1] === false)
+      {
+        reference.name = refscopes[i][0];
+        this.optionalReferences.push(reference);
+      }
     }
-    return this.audienceNames[ticket.audience];
+  }
+
+  toggleShowRef() {
+    this.showReferences = !this.showReferences;
+  }
+
+  private updateAttestations() {
+    this.reclaimService.getAttestation(this.identity).subscribe(attestations 
=> {
+      this.attestations = attestations;
+      //FIXME this is not how this API should work
+      //The API should already return attributes which can be used...
+      let i;
+      for (i = 0; i < this.attestations.length; i++) {
+        this.reclaimService.parseAttest(this.attestations[i]).subscribe(values 
=>{
+          this.attestationValues[this.attestations[i].id]=values;
+        },
+        err => {
+          //this.errorInfos.push("Error parsing attestation ``" + 
attestation.name + "''");
+          console.log(err);
+        });
+
+      }
+    },
+    err => {
+      //this.errorInfos.push("Error retrieving attestation for ``" + 
identity.name + "''");
+      console.log(err);
+    });
+  }
+
+  private updateReferences() {
+    this.reclaimService.getReferences(this.identity).subscribe(references => {
+      this.references = [];
+      this.requestedReferences = [];
+      if (references === null) {
+        this.getMissingReferences();
+        return;
+      }
+      const scope = this.oidcService.getRefScope();
+      let i;
+      for (i = 0; i < references.length; i++) {
+        this.references.push(references[i]);
+        let j;
+        for (j = 0; j < scope.length; j++) {
+          if (references[i].name === scope[j][0] ) {
+            this.requestedReferences.push(references[i]);
+          }
+        }
+      }
+      this.getMissingReferences();
+    },
+    err => {
+      //this.errorInfos.push("Error retrieving references for ``" + 
identity.name + "''");
+      console.log(err);
+    });
+  }
+
+  private storeReferences() {
+    const promises = [];
+    let i;
+    if (undefined !== this.missingReferences) {
+      for (i = 0; i < this.missingReferences.length; i++) {
+        if ((this.missingReferences[i].ref_value === '') || 
(this.missingReferences[i].ref_id === '')) {
+          console.log("EmptyReferences: " + this.missingReferences[i]);
+          continue;
+        }
+        console.log("MissingReferences: " + this.missingReferences[i]);
+        promises.push(from(this.reclaimService.addReference(
+          this.identity, this.missingReferences[i])));
+      }
+    }
+    if (undefined !== this.references) {
+      for (i = 0; i < this.references.length; i++) {
+        promises.push(
+          from(this.reclaimService.addReference(this.identity, 
this.references[i])));
+      }
+    }
+    if ((this.newReference.ref_value !== '') || (this.newReference.ref_id !== 
'')) {
+      promises.push(from(this.reclaimService.addReference(this.identity, 
this.newReference)));
+    }
+
+    return forkJoin(promises);
+  }
+
+  addReference() {
+    this.storeReferences()
+    .pipe(
+      finalize(() => {
+        this.newReference.name = '';
+        this.newReference.ref_value= '';
+        this.newReference.ref_id = '';
+        this.updateReferences();
+        this.updateAttributes();
+      }))
+      .subscribe(res => {
+        console.log(res);
+      },
+      err => {
+        console.log(err);
+        //this.errorInfos.push("Failed to update identity ``" +  
this.identityInEdit.name + "''");
+        EMPTY
+      });
+  }
+
+
+  isAttestation(attribute) {
+    if (attribute.flag ==='1') {
+      return true;
+    }
+    return false;
+  }
+
+  canAddReference(reference) {
+    if ((reference.name === '') || (reference.ref_value === '') || 
(reference.ref_id === '')) {
+      return false;
+    }
+    if (reference.name.indexOf(' ') >= 0) {
+      return false;
+    }
+    return !this.isRefInConflict(reference);
+  }
+
+  referenceNameValid(reference) {
+    if (reference.name === '' && reference.ref_value === '' && 
reference.ref_id === '') {
+      return true;
+    }
+    if (reference.name.indexOf(' ') >= 0) {
+      return false;
+    }
+    if (!/^[a-zA-Z0-9-_]+$/.test(reference.name)) {
+      return false;
+    }
+    return !this.isRefInConflict(reference);
+  }
+
+  referenceValueValid(reference: Reference) {
+    if (reference.ref_value === '') {
+      return reference.name === '';
+    }
+    return true;
+  }
+
+  referenceIDValid(reference: Reference) {
+    if (reference.ref_id === '') {
+      return reference.name === '';
+    }
+    return true;
+  }
+
+
+  isRefRequested(reference: Reference) {
+    if (undefined === this.requestedReferences) {
+      return false;
+    } else {
+      return -1 !==
+        this.requestedReferences.indexOf(reference);
+    }
+  }
+
+  isAttrRefRequested(attribute: Attribute) {
+    if (undefined === this.requestedReferences) {
+      return false;
+    } else {
+      for (var j = 0; j < this.requestedReferences.length; j++) {
+        if (attribute.name === this.requestedReferences[j].name) {
+          return true;
+        }
+      }
+      return false;
+    }
+  }
+
+  isoptRefRequested(reference: Reference) {
+    if (undefined === this.optionalReferences) {
+      return false;
+    } else {
+      return -1 !==
+        this.optionalReferences.indexOf(reference);
+    }
+  }
+
+  isReferenceMissing() {
+    if (!this.inOpenIdFlow()) {
+      return false;
+    }
+    if (undefined === this.requestedReferences) {
+      return false;
+    }
+    for (var i = 0; i < this.oidcService.getRefScope().length; i++) {
+      if (this.oidcService.getRefScope()[i][1] === true) {
+        var j;
+        for (j = 0; j < this.requestedReferences.length; j++) {
+          if (this.oidcService.getRefScope()[i][0] === 
this.requestedReferences[j].name){
+            break;
+          }
+        }
+        if (j === this.requestedReferences.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);
+  });
+  }*/
+
+
+  isAttestationValid(attestation: Attestation) {
+    //FIXME JWT specific
+    //FIXME the expiration of the JWT should be a property of the attestation
+    //Not part of the values
+    const now = Date.now().valueOf() / 1000;
+    if (this.attestationValues[attestation.id]['exp'] === 'undefined') {
+      return true;
+    }
+    return this.attestationValues[attestation.id]['exp'] > now;
+  }
+
+
+  isAnyAttestationInvalid() {
+    if (!this.inOpenIdFlow()) {
+      return false;
+    }
+    if (undefined === this.requestedReferences) {
+      return false;
+    }
+    for (var j = 0; j < this.attestations.length; j++) {
+      if (!this.isAttestationValid(this.attestations[j])) {
+        return true;
+      }
+    }
+    return false;
   }
 
 }
diff --git a/src/app/identity-list/identity-list.component.html 
b/src/app/identity-list/identity-list.component.html
index 40e95ba..18c9ffb 100644
--- a/src/app/identity-list/identity-list.component.html
+++ b/src/app/identity-list/identity-list.component.html
@@ -76,15 +76,25 @@
     <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)" 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 attributes: 
+    <div *ngIf="isAttributeMissing(identity) || isReferenceMissing(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:
       <ul>
         <li *ngFor="let attr of getMissing(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 references are invalid.
+      <button class="btn btn-primary" (click)="editIdentity(identity)">
+        <span class="fa fa-pencil-square-o"></span> Edit
+      </button>
+    </div>
+    <div *ngIf="(0 < requestedReferences[identity.pubkey]?.length) && 
inOpenIdFlow()" class="alert alert-danger alert-dismissible fade show" 
role="alert">
+      <span class="fa fa-openid"></span> Please note that sharing references 
might disclose additional information stored in the attestation.
+    </div>
     <div class="card-body">
       <!-- Attribute table --> 
       <div>
@@ -93,10 +103,11 @@
         </h6>
         <table class="table pb-1">
           <tbody>
-            <tr [class.openid]="inOpenIdFlow()" 
[class.text-primary]="isRequested(identity, attribute)"
+            <tr [class.openid]="inOpenIdFlow()" 
[class.text-primary]="isRequested(identity, attribute) || 
isAttrRefRequested(identity, attribute)"
+               [class.text-secondary]="!isAttrRefRequested(identity, 
attribute) && isAttestation(attribute)"
                *ngFor="let attribute of attributes[identity.pubkey]">
               <td>
-                <div style="min-width: 15em"><span 
*ngIf="isRequested(identity, attribute)" class="fa fa-openid"></span> 
{{attribute.name}}</div>
+                <div style="min-width: 15em"><span 
*ngIf="isRequested(identity, attribute) || isAttrRefRequested(identity, 
attribute)" class="fa fa-openid"></span> {{attribute.name}}</div>
               </td>
               <td>
                 <div *ngIf="attribute.value.length <= 20" style="min-width: 
15em">{{attribute.value}}</div>
@@ -112,7 +123,7 @@
       <div>
 
         <div>
-          <button *ngIf="canAuthorize(identity)" [disabled]="!inOpenIdFlow() 
|| isAttributeMissing(identity) || !isClientVerified()" 
(click)="loginIdentity(identity)" class="btn btn-primary mr-1 openid-login">
+          <button *ngIf="canAuthorize(identity)" [disabled]="!inOpenIdFlow() 
|| isAttributeMissing(identity) || isReferenceMissing(identity) || 
!isClientVerified() || isReqReferenceInvalid(identity)" 
(click)="loginIdentity(identity)" class="btn btn-primary mr-1 openid-login">
             <span *ngIf="isClientVerified()"><i class="fa fa-openid"></i> 
Share 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 e0c0201..ee28486 100644
--- a/src/app/identity-list/identity-list.component.ts
+++ b/src/app/identity-list/identity-list.component.ts
@@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute, Router } from '@angular/router';
 
 import { Attribute } from '../attribute';
+import { Reference } from '../reference';
 import { GnsService } from '../gns.service';
 import { Identity } from '../identity';
 import { IdentityService } from '../identity.service';
@@ -21,8 +22,12 @@ import { from, forkJoin, EMPTY } from 'rxjs';
 export class IdentityListComponent implements OnInit {
 
   requestedAttributes: any;
+  requestedReferences: any;
   missingAttributes: any;
+  missingReferences: any;
+  optionalReferences: any;
   attributes: any;
+  references: any;
   identities: Identity[];
   showConfirmDelete: any;
   connected: any;
@@ -41,10 +46,14 @@ export class IdentityListComponent implements OnInit {
 
   ngOnInit() {
     this.attributes = {};
+    this.references = {};
     this.identities = [];
     this.showConfirmDelete = null;
     this.requestedAttributes = {};
     this.missingAttributes = {};
+    this.requestedReferences = {};
+    this.missingReferences = {};
+    this.optionalReferences = {};
     this.connected = false;
     this.modalOpened = false;
     if (!this.oidcService.inOpenIdFlow()) {
@@ -81,12 +90,66 @@ export class IdentityListComponent implements OnInit {
     }
     this.missingAttributes[identity.pubkey] = [];
     for (i = 0; i < scopes.length; i++) {
-      const attribute = new Attribute('', '', '', 'STRING');
+      const attribute = new Attribute('', '', '', 'STRING', '');
       attribute.name = scopes[i];
       this.missingAttributes[identity.pubkey].push(attribute);
     }
   }
 
+  getMissingReferences(identity) {
+    const refscopes = this.oidcService.getRefScope();
+    let i;
+    for (i = 0; i < this.requestedReferences[identity.pubkey].length; i++) {
+      for (var j = 0; j < refscopes.length; j++) {
+        if (this.requestedReferences[identity.pubkey][i].name === 
refscopes[j][0] ) {
+          refscopes.splice(j,1);
+        }
+      }
+    }
+    this.missingReferences[identity.pubkey] = [];
+    this.optionalReferences[identity.pubkey] = [];
+    for (i = 0; i < refscopes.length; i++) {
+      const reference = new Reference('', '', '', '');
+      if (refscopes[i][1] === true)
+      {
+        reference.name = refscopes[i][0];
+        this.missingReferences[identity.pubkey].push(reference);
+      }
+      if (refscopes[i][1] === false)
+      {
+        reference.name = refscopes[i][0];
+        this.optionalReferences[identity.pubkey].push(reference);
+      }
+    }
+  }
+
+  private updateReferences(identity) {
+    this.reclaimService.getReferences(identity).subscribe(references => {
+      this.references[identity.pubkey] = [];
+      this.requestedReferences[identity.pubkey] = [];
+      if (references === null) {
+        this.getMissingReferences(identity);
+        return;
+      }
+     const scope = this.oidcService.getRefScope();
+      let i;
+      for (i = 0; i < references.length; i++) {
+        this.references[identity.pubkey].push(references[i]);
+        let j;
+        for (j = 0; j < scope.length; j++) {
+          if (references[i].name === scope[j][0] ) {
+            this.requestedReferences[identity.pubkey].push(references[i]);
+          }
+        }
+      }
+      this.getMissingReferences(identity);
+    },
+    err => {
+      this.errorInfos.push("Error retrieving references for ``" + 
identity.name + "''");
+      console.log(err);
+    });
+  }
+
   private updateAttributes(identity) {
     this.reclaimService.getAttributes(identity).subscribe(attributes => {
       this.attributes[identity.pubkey] = [];
@@ -200,6 +263,7 @@ export class IdentityListComponent implements OnInit {
 
       identities.forEach(identity => {
         this.updateAttributes(identity);
+        this.updateReferences(identity);
       });
       if (!this.modalOpened) {
         this.closeModal('GnunetInfo');
@@ -220,4 +284,52 @@ export class IdentityListComponent implements OnInit {
   canSearch() {
     return this.isConnected() && 0 != this.identities.length;
   }
+
+  isReferenceMissing(identity) {
+    if (!this.inOpenIdFlow()) {
+      return false;
+    }
+    if (undefined === this.requestedReferences) {
+      return false;
+    }
+    for (var i = 0; i < this.oidcService.getRefScope().length; i++) {
+      if (this.oidcService.getRefScope()[i][1] === true) {
+        var j;
+        for (j = 0; j < this.requestedReferences.length; j++) {
+          if (this.oidcService.getRefScope()[i][0] === 
this.requestedReferences[j].name){
+            break;
+          }
+        }
+        if (j === this.requestedReferences.length){
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  isAttrRefRequested(identity: Identity, attribute: Attribute) {
+    if (undefined === this.requestedReferences[identity.pubkey]) {
+      return false;
+    } else {
+      for (var j = 0; j < this.requestedReferences[identity.pubkey].length; 
j++) {
+        if (attribute.name === 
this.requestedReferences[identity.pubkey][j].name) {
+          return true;
+        }
+      }
+      return false;
+    }
+  }
+
+  isAttestation(attribute: Attribute) {
+    if (attribute.flag ==='1') {
+      return true;
+    }
+    return false;
+  }
+
+  isReqReferenceInvalid(identity: any) {
+    return false; //FIXME actually handle this 
https://gitlab.com/voggenre/ui/commit/dd9b6656dee7dbf59809dcc9bc2508ee70d8afe6
+  }
+
 }
diff --git a/src/app/open-id.service.ts b/src/app/open-id.service.ts
index a090c32..1151915 100644
--- a/src/app/open-id.service.ts
+++ b/src/app/open-id.service.ts
@@ -5,6 +5,7 @@ import { Identity } from './identity';
 import { ConfigService } from './config.service';
 import { Router } from '@angular/router';
 import { GnsService } from './gns.service';
+import { Reference } from './reference';
 
 @Injectable()
 export class OpenIdService {
@@ -12,6 +13,7 @@ export class OpenIdService {
   inOidcFlow: Boolean;
   clientNameVerified: Boolean;
   clientName: String;
+  referenceString: String;
 
   constructor(private http: HttpClient,
     private config: ConfigService,
@@ -19,8 +21,9 @@ export class OpenIdService {
     private router: Router) {
     this.params = {};
     this.inOidcFlow = false;
+    this.referenceString = "";
   }
-  
+
   getClientName() {
     this.clientNameVerified = undefined;
     if (!this.inOpenIdFlow()) {
@@ -66,12 +69,19 @@ export class OpenIdService {
     window.location.href = this.config.get().apiUrl + 
'/openid/authorize?client_id=' + this.params['client_id'] +
     '&redirect_uri=' + this.params['redirect_uri'] +
     '&response_type=' + this.params['response_type'] +
-    '&scope=' + this.params['scope'] +
+    '&scope=' + this.params['scope'] + " " + this.referenceString +
     '&state=' + this.params['state'] +
     '&code_challenge=' + this.params['code_challenge'] +
     '&nonce=' + this.params['nonce'];
   }
 
+  setReferences(references: Reference[]) {
+    this.referenceString = "";
+    for(var i = 0; i < references.length; i++) {
+      this.referenceString = this.referenceString + references[i].name + " ";
+    }
+  }
+
   cancelAuthorization(): any {
     const httpOptions = {
       withCredentials: true
@@ -101,4 +111,21 @@ export class OpenIdService {
     scopes.splice(i, 1);
     return scopes;
   }
+
+  getRefScope(): any {
+    if (!this.inOpenIdFlow()) {
+      return [];
+    }
+    var scope = [];
+    var json = JSON.parse(this.params.get('claims'))['userinfo'];
+    for(var key in json)
+    {
+      if (json[key]['attestation'] === true)
+        {
+          scope.push([key, json[key]['essential'], json[key]['attestation'], 
json[key]['format']]);
+        }
+    }
+    return scope;
+  }
+
 }
diff --git a/src/app/reclaim.service.ts b/src/app/reclaim.service.ts
index ca8374e..94bb3b3 100644
--- a/src/app/reclaim.service.ts
+++ b/src/app/reclaim.service.ts
@@ -1,8 +1,10 @@
-import { HttpClient } from '@angular/common/http';
+import { HttpClient, HttpHeaders } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 import { Observable } from 'rxjs';
 
 import { Attribute } from './attribute';
+import { Reference } from './reference';
+import { Attestation } from './attestation';
 import { ConfigService } from './config.service';
 import { GnuNetResponse } from './gnu-net-response';
 import { Identity } from './identity';
@@ -38,4 +40,46 @@ export class ReclaimService {
     return this.http.post<Ticket>(this.config.get().apiUrl + '/reclaim/revoke',
       ticket);
   }
+  getReferences(identity: Identity): Observable<Reference[]> {
+    return this.http.get<Reference[]>(this.config.get().apiUrl +
+      '/reclaim/attestation/reference/' + identity.name);
+  }
+
+  addReference(identity: Identity, reference: Reference) {
+    return this.http.post<Reference>(this.config.get().apiUrl +
+      '/reclaim/attestation/reference/' + identity.name,
+      reference);
+  }
+
+  deleteReference(identity: Identity, reference: Reference) {
+    const options = {headers: new HttpHeaders({'Content-Type': 
'application/json',}),
+    body: reference,};
+    return this.http.delete(this.config.get().apiUrl + 
'/reclaim/attestation/reference/' +
+      identity.name + '/' + reference.ref_id, options);
+  }
+
+  getAttestation(identity: Identity): Observable<Attestation[]> {
+    return this.http.get<Attestation[]>(this.config.get().apiUrl +
+      '/reclaim/attestation/' + identity.name);
+  }
+
+  addAttestation(identity: Identity, attestation: Attestation) {
+    return this.http.post<Attestation>(this.config.get().apiUrl +
+      '/reclaim/attestation/' + identity.name,
+      attestation);
+  }
+
+  deleteAttestation(identity: Identity, attestation: Attestation) {
+    return this.http.delete(this.config.get().apiUrl + '/reclaim/attestation/' 
+
+      identity.name + '/' + attestation.id);
+  }
+
+  //FIXME this should be replaced by a data model that ties attributes
+  //and references to attestations together.
+  parseAttest(attestation: Attestation) {
+    var json = JSON.parse('{"value":"'+ attestation.value + '", "type":"'+ 
attestation.type + '"}')
+    return this.http.post(this.config.get().apiUrl +
+      '/reclaim/attestation/parse',json
+      );
+  }
 }
diff --git a/src/app/reference.ts b/src/app/reference.ts
new file mode 100644
index 0000000..d135891
--- /dev/null
+++ b/src/app/reference.ts
@@ -0,0 +1,6 @@
+export class Reference {
+  constructor(public name: string,
+              public ref_value: string,
+              public id: string,
+              public ref_id: string) {}
+}

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]