[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.
- [reclaim-ui] 360/459: clean up, (continued)
- [reclaim-ui] 360/459: clean up, gnunet, 2021/06/11
- [reclaim-ui] 372/459: fix defaults loading, gnunet, 2021/06/11
- [reclaim-ui] 338/459: update en, gnunet, 2021/06/11
- [reclaim-ui] 366/459: fix, gnunet, 2021/06/11
- [reclaim-ui] 333/459: Added translation using Weblate (German), gnunet, 2021/06/11
- [reclaim-ui] 305/459: renamed attestations to credentials, gnunet, 2021/06/11
- [reclaim-ui] 365/459: preserve history on authz redirect, gnunet, 2021/06/11
- [reclaim-ui] 344/459: translate claims and scopedescription, gnunet, 2021/06/11
- [reclaim-ui] 348/459: fix, gnunet, 2021/06/11
- [reclaim-ui] 374/459: logout before login, gnunet, 2021/06/11
- [reclaim-ui] 367/459: towards better attribute import,
gnunet <=
- [reclaim-ui] 351/459: make parser happy, gnunet, 2021/06/11
- [reclaim-ui] 321/459: I18n, gnunet, 2021/06/11
- [reclaim-ui] 354/459: redirect at api request, gnunet, 2021/06/11
- [reclaim-ui] 350/459: do not escape, gnunet, 2021/06/11
- [reclaim-ui] 377/459: ignore some profile claims, gnunet, 2021/06/11
- [reclaim-ui] 384/459: move info for import, gnunet, 2021/06/11
- [reclaim-ui] 385/459: fix bugs with requested attributes, gnunet, 2021/06/11
- [reclaim-ui] 355/459: fix, gnunet, 2021/06/11
- [reclaim-ui] 375/459: blacklist some claims, gnunet, 2021/06/11
- [reclaim-ui] 378/459: update, gnunet, 2021/06/11