gnunet-svn
[Top][All Lists]
Advanced

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

[reclaim-ui] 367/459: towards better attribute import


From: gnunet
Subject: [reclaim-ui] 367/459: towards better attribute import
Date: Fri, 11 Jun 2021 23:27:39 +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 878064c437f30b6683e6414be18b19252f51184c
Author: Martin Schanzenbach <schanzen@gnunet.org>
AuthorDate: Wed Dec 23 23:48:59 2020 +0900

    towards better attribute import
---
 src/app/config/config.component.css                |   0
 src/app/config/config.component.html               |  15 ++
 src/app/config/config.component.spec.ts            |  25 ++
 src/app/config/config.component.ts                 |  41 ++++
 .../import-attributes.component.css                |  25 ++
 .../import-attributes.component.html               |  47 ++++
 .../import-attributes.component.spec.ts            |  25 ++
 .../import-attributes.component.ts                 | 251 +++++++++++++++++++++
 8 files changed, 429 insertions(+)

diff --git a/src/app/config/config.component.css 
b/src/app/config/config.component.css
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/config/config.component.html 
b/src/app/config/config.component.html
new file mode 100644
index 0000000..d5f4c6c
--- /dev/null
+++ b/src/app/config/config.component.html
@@ -0,0 +1,15 @@
+<div class="m-2 card">
+  <div class="card-avatar card-img-top">
+    <div class="card-avatar-character text-dark" >
+      Configuration
+    </div>
+  </div>
+  <!-- Credential management -->
+  <div class="card-body">
+    <div style="margin: 1em" (click)="toggleExperimental()">
+      <i [className]="isExperimental() ? 'fa fa-toggle-on mr-2' : 'fa 
fa-toggle-off mr-2'"></i>
+      <b 
*ngIf="isExperimental()">{{getMessage("app_html@experimentalEnabled")}}</b><span
 
*ngIf="!isExperimental()">{{getMessage("app_html@experimentalDisabled")}}</span>
+    </div>
+  </div>
+</div>
+
diff --git a/src/app/config/config.component.spec.ts 
b/src/app/config/config.component.spec.ts
new file mode 100644
index 0000000..ec5d3be
--- /dev/null
+++ b/src/app/config/config.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ConfigComponent } from './config.component';
+
+describe('ConfigComponent', () => {
+  let component: ConfigComponent;
+  let fixture: ComponentFixture<ConfigComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ ConfigComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ConfigComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/config/config.component.ts 
b/src/app/config/config.component.ts
new file mode 100644
index 0000000..52e9af9
--- /dev/null
+++ b/src/app/config/config.component.ts
@@ -0,0 +1,41 @@
+import { Component, OnInit } from '@angular/core';
+import { ConfigService } from '../config.service';
+import { LanguageService } from '../language.service';
+
+
+@Component({
+  selector: 'app-config',
+  templateUrl: './config.component.html',
+  styleUrls: ['./config.component.css']
+})
+export class ConfigComponent implements OnInit {
+
+  configService: ConfigService;
+
+  constructor(private _configService: ConfigService,
+              private languageService: LanguageService) {
+    this.configService = _configService;
+  }
+
+
+  ngOnInit(): void {
+  }
+
+  isExperimental() {
+    return this.configService.get().experiments;
+  }
+
+  toggleExperimental() {
+    var config = this.configService.get();
+    console.log("Config is: "+config);
+    config.experiments = !config.experiments;
+    this.configService.save(config);
+  }
+
+  //Internationalization
+  getMessage(key, sub?){
+    return this.languageService.getMessage(key, sub);
+  }
+
+
+}
diff --git a/src/app/import-attributes/import-attributes.component.css 
b/src/app/import-attributes/import-attributes.component.css
new file mode 100644
index 0000000..ca84834
--- /dev/null
+++ b/src/app/import-attributes/import-attributes.component.css
@@ -0,0 +1,25 @@
+.email-status-wrapper {
+  display:inline-block;
+  position: relative;
+}
+
+.email-status-wrapper input {
+  width: 30em;
+}
+
+.invalid.email-status-wrapper:after {
+  font-family: 'FontAwesome';
+  content: '\f071';
+  position: absolute;
+  right: 1.25em;
+  top: 0.5em;
+}
+
+.valid.email-status-wrapper:after {
+  font-family: 'FontAwesome';
+  content: '\f00c';
+  position: absolute;
+  right: 1.25em;
+  top: 0.5em;
+}
+
diff --git a/src/app/import-attributes/import-attributes.component.html 
b/src/app/import-attributes/import-attributes.component.html
new file mode 100644
index 0000000..d468721
--- /dev/null
+++ b/src/app/import-attributes/import-attributes.component.html
@@ -0,0 +1,47 @@
+<!-- Identity edit screen -->
+<div class="m-2 card">
+  <div class="card-avatar card-img-top">
+    <div class="card-avatar-character text-dark" >
+      <h2 class="fa-2x">
+      <span class="fa fa-download"></span>
+      {{getMessage("import_attributes_html@importFor")}} <i>{{ identity.name 
}}</i>
+      </h2>
+    </div>
+  </div>
+  <!-- Credential management -->
+  <div class="card-body">
+    <!--IdProvider-Discovery-->
+    <div>
+      <!--Email not found Warning-->
+      <div *ngIf="errorMessage != ''"
+        class="alert alert-danger alert-dismissible fade show my-2" 
role="alert">
+        <span class="fa fa-warning"></span> {{errorMessage}}
+      </div>
+      <!-- Instructions -->
+      <div
+        class="alert alert-primary alert-dismissible fade show my-2" 
role="alert" >
+        <span class="fa fa-info"> </span>  <b 
class="ml-2">{{getMessage("edit_credentials_html@info")}}</b><br/>
+        {{getMessage("edit_credentials_html@linkAccountInfo1")}}<br/>
+        
<i>{{getMessage("Note")}}</i>{{getMessage("edit_credentials_html@linkAccountInfo2")}}
+      </div>
+      <div class="my-2 col-lg-4">
+        <div class="email-status-wrapper" [class.valid]="validEmail" 
[class.text-primary]="validEmail"
+                                          [class.invalid]="errorMessage != ''" 
[class.text-danger]="errorMessage != ''">
+          <input placeholder="user@example.com" [(ngModel)]="webfingerEmail" 
(keyup)="validateEmail()">
+        </div>
+        <button class="btn btn-primary fhg-link" (click)="import()" 
[disabled]="!validEmail">
+          <span class="fa fa-download"></span> 
{{getMessage("import_attributes_html@import")}}
+        </button>
+        <i *ngIf="discoveringIdProvider" class="ml-1 fa fa-spinner 
fa-spin"></i>
+      </div>
+    </div>
+    <hr />
+
+    <!-- Edit card buttons -->
+    <div>
+      <button class="btn btn-primary" [routerLink]="['/edit-identity', 
identity.name ]">
+        <span class="fa fa-save"></span> {{ getMessage("Back") }}
+      </button>
+    </div>
+  </div>
+</div>
diff --git a/src/app/import-attributes/import-attributes.component.spec.ts 
b/src/app/import-attributes/import-attributes.component.spec.ts
new file mode 100644
index 0000000..c864ff3
--- /dev/null
+++ b/src/app/import-attributes/import-attributes.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ImportAttributesComponent } from './import-attributes.component';
+
+describe('ImportAttributesComponent', () => {
+  let component: ImportAttributesComponent;
+  let fixture: ComponentFixture<ImportAttributesComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ ImportAttributesComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ImportAttributesComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/import-attributes/import-attributes.component.ts 
b/src/app/import-attributes/import-attributes.component.ts
new file mode 100644
index 0000000..4f610cb
--- /dev/null
+++ b/src/app/import-attributes/import-attributes.component.ts
@@ -0,0 +1,251 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { ReclaimService } from '../reclaim.service';
+import { Identity } from '../identity';
+import { Credential } from '../credential';
+import { IdentityService } from '../identity.service';
+import { CredentialService } from '../credential.service';
+import { OAuthService } from 'angular-oauth2-oidc';
+import { IdProvider } from '../idProvider';
+import { LoginOptions } from 'angular-oauth2-oidc';
+import { Scope } from '../scope';
+import { LanguageService } from '../language.service';
+
+
+@Component({
+  selector: 'app-import-attributes',
+  templateUrl: './import-attributes.component.html',
+  styleUrls: ['./import-attributes.component.css']
+})
+export class ImportAttributesComponent implements OnInit {
+
+  identity: Identity;
+  newCredential: Credential;
+  newIdProvider: IdProvider;
+  credentials: Credential[];
+  webfingerEmail: string;
+  emailNotFoundAlertClosed: boolean;
+  errorMessage: string;
+  scopes: Scope[];
+  timer: any;
+  validEmail: boolean;
+  discoveringIdProvider: boolean;
+
+  constructor(private reclaimService: ReclaimService,
+              private identityService: IdentityService,
+              private activatedRoute: ActivatedRoute,
+              private router: Router,
+              private credentialService: CredentialService,
+              private oauthService: OAuthService,
+              private languageService: LanguageService) { }
+
+
+  ngOnInit(): void {
+    this.newCredential = new Credential('', '', '', 'JWT', '', 0, []);
+    this.identity = new Identity('','');
+    this.newIdProvider = new IdProvider ('', '');
+    this.webfingerEmail = '';
+    this.emailNotFoundAlertClosed = true;
+    this.errorMessage = '';
+    this.loadScopesFromLocalStorage()
+    this.loadIdProviderFromLocalStorage();
+    this.credentials = [];
+    if (this.newIdProvider.url !== ''){
+      const loginOptions: LoginOptions = {
+        customHashFragment: "?code="+localStorage.getItem("credentialCode") + 
"&state=" + localStorage.getItem("credentialState") + "&session_state="+ 
localStorage.getItem("credentialSession_State"),
+      }
+      this.configureOauthService();
+      if (!localStorage.getItem("credentialCode")){
+        this.oauthService.loadDiscoveryDocumentAndTryLogin();
+      }
+      else{
+        this.oauthService.loadDiscoveryDocumentAndTryLogin(loginOptions);
+      }
+    }
+    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];
+            }
+          }
+        });
+    });
+  }
+
+  loadIdProviderFromLocalStorage(){
+    this.newIdProvider.url = localStorage.getItem("newIdProviderURL") || '';
+    this.newIdProvider.name = 
this.getNewIdProviderName(this.newIdProvider.url);
+  }
+
+  getNewIdProviderName(url: string){
+    return url.split('//')[1];
+  }
+
+  getNewCredentialExpiration(){
+    var exp = new Date(0);
+    exp.setMilliseconds(this.oauthService.getIdTokenExpiration());
+    return exp.toLocaleString();
+  }
+
+  resetNewIdProvider(){
+    this.newIdProvider.url = '';
+    this.newIdProvider.name = '';
+    localStorage.removeItem('newIdProviderURL');
+  }
+
+  logOutFromOauthService(){
+    if (!this.oauthService.hasValidAccessToken()){
+      return;
+    }
+    this.oauthService.logOut();
+  }
+
+  loggedIn(){
+    return this.oauthService.hasValidAccessToken();
+  }
+
+  cancelAdding(){
+    this.logOutFromOauthService();
+    this.resetNewIdProvider();
+    this.resetScopes();
+    this.newCredential.value = '';
+    this.newCredential.name = '';
+  }
+
+
+  //Webfinger
+
+  discoverIdProvider() {
+    this.discoveringIdProvider = true;
+    localStorage.setItem('userForCredential', this.identity.name);
+    let account = this.webfingerEmail;
+    if (this.webfingerEmail.substr(this.webfingerEmail.indexOf('@')+1) === 
'aisec.fraunhofer.de') {
+      account = this.webfingerEmail.substr(0, 
this.webfingerEmail.indexOf('@')+1) + 'as.aisec.fraunhofer.de';
+    }
+
+    this.credentialService.getLink(account).subscribe (idProvider => {
+      this.discoveringIdProvider = false;
+      this.newIdProvider.url = (idProvider.links [0]).href; 
+      localStorage.setItem('newIdProviderURL', this.newIdProvider.url);
+      this.newIdProvider.name = 
this.getNewIdProviderName(this.newIdProvider.url);
+      console.log(this.newIdProvider.url);
+      this.getScopes();
+      this.errorMessage = '';
+      this.validEmail = true;
+    },
+    error => {
+      this.discoveringIdProvider = false;
+      this.validEmail = false;
+      if (error.status == 404){
+        this.errorMessage = this.getMessage("edit_credentials_ts@noAccount");
+      }
+      else{
+        this.errorMessage = 
this.getMessage("edit_credentials_ts@errorWrongAddress");
+      }
+      this.emailNotFoundAlertClosed = false;
+        setTimeout(() => this.emailNotFoundAlertClosed = true, 20000);
+      console.log (error);
+    });
+  }
+
+  getScopes(){
+    this.configureOauthService();
+    
this.credentialService.getDiscoveryDocument(this.oauthService.issuer).subscribe(openidConfig
 => {
+      this.scopes = [];
+      openidConfig["scopes_supported"].forEach(scope => {
+        const scopeInterface: Scope = {
+          scope: scope,
+          chosen: true,
+        }
+        this.scopes.push(scopeInterface)
+      });
+      localStorage.setItem("scopes", JSON.stringify(this.scopes));
+      });
+  }
+
+  loadScopesFromLocalStorage(){
+    this.scopes = [];
+    var loadedScopes = localStorage.getItem("scopes");
+    if (loadedScopes==null){
+      return
+    }
+    loadedScopes.split(',{').forEach(scopeObject => {
+      var scopeName = scopeObject.split(',')[0];
+      var scopeChosen = scopeObject.split(',')[1].slice(0, -1);
+      const scopeInterface: Scope = {
+        scope: scopeName.split(':')[1].slice(1,-1),
+        chosen: (/true/i).test(scopeChosen.split(':')[1]),
+      }
+      this.scopes.push(scopeInterface)
+    }
+      );
+  }
+
+  newIdProviderDiscovered(){
+    if (this.newIdProvider.url == ''){
+      return false;
+    }
+    return true;
+  }
+
+  validateEmail() {
+    if (!this.webfingerEmail.includes('@')){
+      this.validEmail = false;
+      return;
+    }
+    if (this.webfingerEmail.length - this.webfingerEmail.indexOf('@') < 4) {
+      this.validEmail = false;
+      return;
+    }
+    clearTimeout(this.timer);
+
+    this.timer = setTimeout(() => {
+      this.discoverIdProvider();
+    }, 400);
+  }
+
+  isValidEmailforDiscovery(){
+    if (!this.webfingerEmail.includes('@')){
+      return false;
+    }
+    return true;
+  }
+
+  import(){
+    this.configureOauthService();
+    this.oauthService.loadDiscoveryDocumentAndLogin();
+  }
+
+  configureOauthService(){
+    var authCodeFlowConfig = 
this.credentialService.getOauthConfig(this.newIdProvider, this.scopes);
+    this.oauthService.configure(authCodeFlowConfig);
+  }
+
+  cancelLinking(){
+    this.resetNewIdProvider();
+    this.resetScopes();
+    this.webfingerEmail = '';
+  }
+
+  necessaryScope(scope){
+    if (scope=="openid" || scope=="profile") {
+      return true;
+    }
+    return false;
+  }
+
+  resetScopes(){
+    localStorage.removeItem("scopes");
+    this.scopes = [];
+  }
+
+  //Internationalization
+  getMessage(key, sub?){
+    return this.languageService.getMessage(key, sub);
+  }
+}

-- 
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]