From ab33cba9a064d86f73db6d9d7f20225a2cb2acf3 Mon Sep 17 00:00:00 2001 From: Alex ORLUC <alex.orluc@maarch.org> Date: Fri, 7 May 2021 17:59:05 +0200 Subject: [PATCH] FEAT #16982 TIME 4 WIP otp connectors admin --- lang/fr.json | 8 ++- .../app/administration/otp/otp.component.html | 57 ++++++++++++++--- .../app/administration/otp/otp.component.ts | 63 +++++++++++++++---- .../otps/otp-create.component.html | 12 ++-- .../otps/otp-create.component.scss | 5 +- .../otps/yousign/otp-yousign.component.html | 7 +-- .../visa-workflow/visa-workflow.component.ts | 2 +- .../app/service/notification.service.ts | 10 +-- 8 files changed, 125 insertions(+), 39 deletions(-) diff --git a/lang/fr.json b/lang/fr.json index 1a14a820bd..069aaa9222 100755 --- a/lang/fr.json +++ b/lang/fr.json @@ -486,9 +486,15 @@ "otpMsg": "L'utilisateur sera notifié par <b>mail</b> et recevra un <b>code de sécurité</b> par <b>{{security}}</b> au moment de son tour dans le circuit.", "manage_otp_connectorsAdmin": "Administrer les connecteurs OTP", "manage_otp_connectors": "Connecteurs OTP", + "manage_otp_connectorsDesc": "Administrer les différents connecteurs de parapheurs externes pour des utilisateurs OTP.", "newConnector": "Nouveau connecteur", "connectors": "connecteur(s)", "type": "Type", - "otpConnectorCreation": "Création d'un connecteur OTP" + "otpConnectorCreation": "Création d'un connecteur OTP", + "apiKey": "Clé API", + "apiUri": "Adresse de l'API", + "connectorAdded": "Connecteur ajouté", + "connectorUpdated": "Connecteur modifié", + "connectorDeleted": "Connecteur supprimé" } } diff --git a/src/frontend/app/administration/otp/otp.component.html b/src/frontend/app/administration/otp/otp.component.html index f2d47e1fa5..3042baca6f 100644 --- a/src/frontend/app/administration/otp/otp.component.html +++ b/src/frontend/app/administration/otp/otp.component.html @@ -9,17 +9,60 @@ </ion-header> <form style="display: contents;" id="adminForm" (ngSubmit)="onSubmit()" #adminForm="ngForm"> <ion-content> - <ion-item> - <ion-label color="secondary">{{'lang.source' | translate}}</ion-label> - <ion-select name="type" [(ngModel)]="connector.type" [value]="connector.type" cancelText="{{'lang.cancel' | translate}}"> - <ion-select-option *ngFor="let connector of otpService.getConnectorTypes()" [value]="connector"> - {{connector | translate}}</ion-select-option> - </ion-select> - </ion-item> <ion-item> <ion-label color="secondary" position="floating">{{'lang.label' | translate}} *</ion-label> <ion-input name="label" [maxlength]="128" [(ngModel)]="connector.label" required> </ion-input> </ion-item> + <ion-list> + <ion-radio-group name="type" [(ngModel)]="connector.type" [value]="connector.type"> + <ion-list-header> + <ion-label color="secondary">{{'lang.source' | translate}} *</ion-label> + </ion-list-header> + <ion-item *ngFor="let connector of connectorTypes"> + <ion-radio slot="start" [value]="connector.id"></ion-radio> + <ion-avatar slot="start"> + <img [src]="connector.logo"> + </ion-avatar> + <ion-label> + {{connector.id | translate}} + </ion-label> + </ion-item> + </ion-radio-group> + </ion-list> + <ion-item> + <ion-label color="secondary" position="floating">{{'lang.apiUri' | translate}} *</ion-label> + <ion-input name="apiUri" [maxlength]="128" [(ngModel)]="connector.apiUri" required> + </ion-input> + </ion-item> + <ion-item> + <ion-label color="secondary" position="floating">{{'lang.apiKey' | translate}} *</ion-label> + <ion-input name="apiKey" [maxlength]="128" [(ngModel)]="connector.apiKey" required> + </ion-input> + </ion-item> + <ion-list> + <ion-list-header> + <ion-label color="secondary"> + {{'lang.securityCodeSendMode' | translate}} * + </ion-label> + </ion-list-header> + <ion-item *ngFor="let mode of securityModes"> + <ion-label>{{mode}}</ion-label> + <ion-checkbox slot="start" [checked]="isCheckedSecurityMode(mode)" + (ionChange)="toggleSecurityMode($event.detail)" [value]="mode"></ion-checkbox> + </ion-item> + </ion-list> + <ion-item text-center lines="none" style="position: sticky;bottom:0px;z-index:1;"> + <div style="display: flex;align-items: center;justify-content: center;width: 100%;background: white;"> + <ion-button type="submit" shape="round" size="large" fill="outline" color="primary" + [disabled]="!adminForm.form.valid || connector.securityModes.length === 0"> + <ion-label style="font-size: 13px;">{{'lang.validate' | translate}}</ion-label> + </ion-button> + <ion-button *ngIf="!creationMode" type="button" shape="round" size="large" fill="outline" color="danger" + (click)="deleteconnector()"> + <ion-label style="font-size: 13px;">{{'lang.delete' | translate}}</ion-label> + </ion-button> + </div> + </ion-item> </ion-content> </form> \ No newline at end of file diff --git a/src/frontend/app/administration/otp/otp.component.ts b/src/frontend/app/administration/otp/otp.component.ts index 604a431379..f4e9822f71 100644 --- a/src/frontend/app/administration/otp/otp.component.ts +++ b/src/frontend/app/administration/otp/otp.component.ts @@ -17,6 +17,7 @@ export interface Connector { label: string; apiUri: string; apiKey: string; + securityModes: string[]; } @Component({ @@ -33,6 +34,16 @@ export class OtpComponent implements OnInit { connectorClone: Connector; title: string = ''; + apiDefaultUri = { + yousign : 'https://api.yousign.com/' + }; + + securityModes: string[] = [ + 'sms', + 'email' + ]; + + connectorTypes: any[] = []; constructor( public http: HttpClient, @@ -48,14 +59,15 @@ export class OtpComponent implements OnInit { public alertController: AlertController, public otpService: OtpService, ) { + this.getConnectorTypes(); this.connector = { id: '', - type: this.otpService.getConnectorTypes()[0], + type: this.connectorTypes[0].id, label: '', - apiUri: '', - apiKey: '' + apiUri: this.apiDefaultUri[this.connectorTypes[0]], + apiKey: '', + securityModes: ['sms'] }; - this.connectorClone = JSON.parse(JSON.stringify(this.connector)); } ngOnInit(): void { @@ -68,7 +80,8 @@ export class OtpComponent implements OnInit { } else { this.creationMode = false; - this.http.get('../rest/otps/' + params['id']) + // WAIT BACK + /* this.http.get('../rest/otps/' + params['id']) .pipe( map((data: any) => data.otp), finalize(() => { @@ -84,11 +97,22 @@ export class OtpComponent implements OnInit { return of(false); }) ) - .subscribe(); + .subscribe();*/ } }); } + getConnectorTypes() { + this.connectorTypes = this.otpService.getConnectorTypes().map((item: any) => ({ + id: item, + logo: null + })); + + this.connectorTypes.forEach(async element => { + element.logo = await this.otpService.getUserOtpIcon(element.id); + }); + } + canValidate() { if (this.connector.label === this.connectorClone.label) { return false; @@ -107,7 +131,8 @@ export class OtpComponent implements OnInit { modifyconnector() { this.loading = true; - this.http.put('../rest/connectors/' + this.connector.id, this.connector) + // WAIT BACK + /* this.http.put('../rest/connectors/' + this.connector.id, this.connector) .pipe( tap(() => { this.router.navigate(['/administration/connectors']); @@ -118,12 +143,12 @@ export class OtpComponent implements OnInit { return of(false); }) ) - .subscribe(); + .subscribe();*/ } createconnector() { this.loading = true; - this.http.post('../rest/connectors', this.connector) + /* this.http.post('../rest/connectors', this.connector) .pipe( tap((data: any) => { this.router.navigate(['/administration/connectors/' + data.id]); @@ -134,7 +159,7 @@ export class OtpComponent implements OnInit { return of(false); }) ) - .subscribe(); + .subscribe(); */ } async deleteconnector() { @@ -151,7 +176,8 @@ export class OtpComponent implements OnInit { { text: this.translate.instant('lang.yes'), handler: () => { - this.http.delete('../rest/connectors/' + this.connector.id) + // WAIT BACK + /* this.http.delete('../rest/connectors/' + this.connector.id) .pipe( tap(() => { this.router.navigate(['/administration/connectors']); @@ -162,7 +188,7 @@ export class OtpComponent implements OnInit { return of(false); }) ) - .subscribe(); + .subscribe(); */ } } ] @@ -171,6 +197,19 @@ export class OtpComponent implements OnInit { await alert.present(); } + toggleSecurityMode(ev: any) { + if (ev.checked) { + this.connector.securityModes.push(ev.value); + } else { + const index = this.connector.securityModes.indexOf(ev.value); + this.connector.securityModes.splice(index, 1); + } + } + + isCheckedSecurityMode(value: string) { + return this.connector.securityModes.indexOf(value) > -1; + } + cancel() { this.router.navigate(['/administration/connectors']); } diff --git a/src/frontend/app/document/visa-workflow/otps/otp-create.component.html b/src/frontend/app/document/visa-workflow/otps/otp-create.component.html index 3d7f1e33c5..07f330525b 100644 --- a/src/frontend/app/document/visa-workflow/otps/otp-create.component.html +++ b/src/frontend/app/document/visa-workflow/otps/otp-create.component.html @@ -9,12 +9,7 @@ </ion-toolbar> </ion-header> <ion-content> - <ion-card> - <ion-item color="primary"> - <ion-label class="info" [innerHTML]="'lang.otpMsg' | translate : { security : appOtpYousign?.getSecurityMode()}"></ion-label> - </ion-item> - </ion-card> - <ion-card> + <ion-card *ngIf="sources.length > 1"> <ion-item> <ion-label color="secondary">{{'lang.source' | translate}}</ion-label> <ion-select [value]="currentSource.id" cancelText="{{'lang.cancel' | translate}}"> @@ -24,6 +19,11 @@ </ion-item> </ion-card> <app-otp-yousign #appOtpYousign *ngIf="currentSource.type === 'yousign'"></app-otp-yousign> + <ion-card> + <ion-item color="primary"> + <ion-label class="info" [innerHTML]="'lang.otpMsg' | translate : { security : appOtpYousign?.getSecurityMode()}"></ion-label> + </ion-item> + </ion-card> </ion-content> <ion-footer class="ion-no-border"> <ion-toolbar> diff --git a/src/frontend/app/document/visa-workflow/otps/otp-create.component.scss b/src/frontend/app/document/visa-workflow/otps/otp-create.component.scss index 806b07f733..99a474645b 100644 --- a/src/frontend/app/document/visa-workflow/otps/otp-create.component.scss +++ b/src/frontend/app/document/visa-workflow/otps/otp-create.component.scss @@ -1,5 +1,6 @@ -.my-custom-class { - --min-width: 400px; +::ng-deep .custom-modal .modal-wrapper { + height: 650px; + max-height: 100%; } .info { diff --git a/src/frontend/app/document/visa-workflow/otps/yousign/otp-yousign.component.html b/src/frontend/app/document/visa-workflow/otps/yousign/otp-yousign.component.html index 979fb5fb5c..2d7b206e04 100644 --- a/src/frontend/app/document/visa-workflow/otps/yousign/otp-yousign.component.html +++ b/src/frontend/app/document/visa-workflow/otps/yousign/otp-yousign.component.html @@ -37,13 +37,8 @@ </ion-item> </div> </ion-radio-group> - <!--<ion-label color="secondary" position="floating">{{'lang.role' | translate}}</ion-label> - <ion-select name="mode" [(ngModel)]="otp.role" [value]="otp.role" cancelText="{{'lang.cancel' | translate}}"> - <ion-select-option *ngFor="let role of roles" [value]="role"> - {{'lang.' + role | translate}}</ion-select-option> - </ion-select>--> </ion-list> - <ion-list> + <ion-list *ngIf="securityModes.length > 1"> <ion-item> <ion-label color="secondary" position="floating">{{'lang.securityCodeSendMode' | translate}} *</ion-label> <ion-select name="mode" [(ngModel)]="otp.security" [value]="otp.security" diff --git a/src/frontend/app/document/visa-workflow/visa-workflow.component.ts b/src/frontend/app/document/visa-workflow/visa-workflow.component.ts index 45299a66c2..2ab89ef5c3 100644 --- a/src/frontend/app/document/visa-workflow/visa-workflow.component.ts +++ b/src/frontend/app/document/visa-workflow/visa-workflow.component.ts @@ -151,7 +151,7 @@ export class VisaWorkflowComponent implements OnInit { async openOtpModal(ev: any) { const modal = await this.modalController.create({ - cssClass: 'my-custom-class', + cssClass: 'custom-modal', component: OtpCreateComponent, backdropDismiss: false, }); diff --git a/src/frontend/app/service/notification.service.ts b/src/frontend/app/service/notification.service.ts index 0c91ed3ee2..54d66710bb 100644 --- a/src/frontend/app/service/notification.service.ts +++ b/src/frontend/app/service/notification.service.ts @@ -50,10 +50,12 @@ export class NotificationService { } } else if (err.error.exception !== undefined) { this.error(err.error.exception[0].message); - } else if (err.error.error !== undefined && err.error.error.message !== undefined) { - this.error(err.error.error.message); - } else if (err.error.error[0] !== undefined) { - this.error(err.error.error[0].message); + } else if (err.error.error !== undefined) { + if (err.error.error.message !== undefined) { + this.error(err.error.error.message); + } else if (err.error.error[0].message !== undefined) { + this.error(err.error.error[0].message); + } } else { this.error(err.message); } -- GitLab