diff --git a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.css b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.css index ebf113a3ed1552b77dbb8aefea0474d409ef0768..4530b75dc618e3480080071e5d93fceb1abbe79a 100644 --- a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.css +++ b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.css @@ -14,3 +14,13 @@ .certificate-valid-from, .certificate-valid-until { width: 40%; } + +.required-fields { + text-align: right; + font-size: 70% +} + +.has-error { + color:red; + font-size: 70%; +} diff --git a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.html b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.html index 8d8aef5aff4702d287b01ca200cdb5d575c87e27..adbba76a35d12a2512951ae682e41777aac0c582 100644 --- a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.html +++ b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.html @@ -2,26 +2,28 @@ <mat-dialog-content> <mat-card> <mat-card-content> - <mat-slide-toggle mat-no-ink class="mat-primary" [checked]="userForm.get('userName')?.value" (change)="onUserToggleChanged($event)"> + <mat-slide-toggle mat-no-ink class="mat-primary" [formControl]="userForm.controls['userToggle']" (change)="onUserToggleChanged($event)"> User Details </mat-slide-toggle> + <div *ngIf="userForm.errors?.userDetailsOrCertificateRequired && (userForm.get('userToggle').dirty || userForm.get('certificateToggle').dirty)" class="has-error">You need to enter at least the details or the certificate for this user</div> + <div class="panel"> <mat-form-field class="username"> <input matInput placeholder="Username" [formControl]="userForm.controls['userName']" maxlength="255" required> - <div *ngIf="userForm.controls['userName'].hasError('required') && userForm.controls['userName'].touched" style="color:red; font-size: 70%">You should type an username</div> + <div *ngIf="userForm.controls['userName'].hasError('required') && userForm.controls['userName'].touched" class="has-error">You should type an username</div> </mat-form-field> <mat-form-field class="role"> <mat-select matInput placeholder="Role" class="role" [formControl]="userForm.controls['role']" required> <mat-option *ngFor="let item of existingRoles" [value]="item">{{item}}</mat-option> </mat-select> - <div *ngIf="userForm.controls['role'].hasError('required') && userForm.controls['role'].touched" style="color:red; font-size: 70%">You need to choose at least one role for this user</div> + <div *ngIf="userForm.controls['role'].hasError('required') && userForm.controls['role'].touched" class="has-error">You need to choose at least one role for this user</div> </mat-form-field> <mat-form-field class="password"> <input matInput placeholder="Password" type="password" [formControl]="userForm.controls['password']" [pattern]="passwordPattern" [required]="!editMode"> - <div *ngIf="!editMode && userForm.controls['password'].hasError('required') && userForm.controls['password'].touched" style="color:red; font-size: 70%">You should type a password</div> - <div *ngIf="userForm.controls['password'].dirty && userForm.controls['password'].hasError('pattern') && userForm.controls['password'].touched" style="color:red; font-size: 70%"> + <div *ngIf="!editMode && userForm.controls['password'].hasError('required') && userForm.controls['password'].touched" class="has-error">You should type a password</div> + <div *ngIf="userForm.controls['password'].dirty && userForm.controls['password'].hasError('pattern') && userForm.controls['password'].touched" class="has-error"> Password should follow all of these rules:<br> - Minimum length: 8 characters<br> - Maximum length: 32 characters<br> @@ -34,14 +36,17 @@ <mat-form-field class="password-confirmation"> <input matInput placeholder="Confirmation" type="password" [formControl]="userForm.controls['confirmation']" [required]="!editMode"> - <div *ngIf="!editMode && userForm.controls['confirmation'].hasError('required') && userForm.controls['confirmation'].touched" style="color:red; font-size: 70%">You should type a password</div> - <div *ngIf="userForm.errors?.confirmation && userForm.controls['confirmation'].touched" style="color:red; font-size: 70%">Passwords do not match</div> + <div *ngIf="!editMode && userForm.controls['confirmation'].hasError('required') && userForm.controls['confirmation'].touched" class="has-error">You should type a password</div> + <div *ngIf="userForm.errors?.confirmationMatch && userForm.controls['confirmation'].touched" class="has-error">Passwords do not match</div> </mat-form-field> </div> - <mat-slide-toggle #certificateToggle mat-no-ink class="mat-primary" [checked]="userForm.get('certificate')?.value"> + <mat-slide-toggle mat-no-ink class="mat-primary" [formControl]="userForm.controls['certificateToggle']"> Certificate </mat-slide-toggle> + <div *ngIf="userForm.errors?.userDetailsOrCertificateRequired && (userForm.get('userToggle').dirty || userForm.get('certificateToggle').dirty)" class="has-error">You need to enter at least the details or the certificate for this user</div> + <div *ngIf="userForm.errors?.certificateDetailsRequired && userForm.get('certificateToggle').touched" class="has-error">All the certificate fields are required so please upload a new certificate</div> + <div class="panel"> <mat-form-field class="certificate-subject"> <input matInput placeholder="Subject Name" [formControl]="userForm.controls['subject']"> @@ -60,8 +65,8 @@ </mat-form-field> <label class="custom-file-upload"> - <input #fileInput type="file" id="custom-file-upload" accept=".cer" (change)="uploadCertificate()" [disabled]="!certificateToggle.checked"> - <button mat-flat-button color="primary" (click)="fileInput.click()" [disabled]="!certificateToggle.checked">Import</button> + <input #fileInput type="file" id="custom-file-upload" accept=".cer" (change)="uploadCertificate()" [disabled]="!userForm.controls['certificateToggle']?.value"> + <button mat-flat-button color="primary" (click)="fileInput.click()" [disabled]="!userForm.controls['certificateToggle']?.value">Import</button> </label> </div> </mat-card-content> @@ -82,5 +87,5 @@ </td> </tr> </table> -<div style="text-align: right; font-size: 70%">* required fields</div> +<div class="required-fields">* required fields</div> diff --git a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.ts b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.ts index f9a6b750402248ac8f6b023c8e801489adc78eba..3136f268b00a48c18ef721b53202bbdd30f91425 100644 --- a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.ts +++ b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.ts @@ -1,5 +1,5 @@ import {Component, Inject, ViewChild} from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialogRef, MatSlideToggleChange} from '@angular/material'; +import {MAT_DIALOG_DATA, MatDialogRef, MatSlideToggle, MatSlideToggleChange} from '@angular/material'; import {FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators} from '@angular/forms'; import {UserService} from '../user.service'; import {Role} from '../../security/role.model'; @@ -30,16 +30,31 @@ export class UserDetailsDialogComponent { existingRoles = []; userForm: FormGroup; - userSwitch: boolean; - certificateSwitch: boolean; - @ViewChild('fileInput') private fileInput; private passwordConfirmationValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => { + const userToggle = control.get('userToggle'); const password = control.get('password'); const confirmation = control.get('confirmation'); - return password && confirmation && password.value !== confirmation.value ? { confirmation: true } : null; + return userToggle && password && confirmation && userToggle.value && password.value !== confirmation.value ? { confirmationMatch: true } : null; + }; + + private atLeastOneToggleCheckedValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => { + const userToggle = control.get('userToggle'); + const certificateToggle = control.get('certificateToggle'); + return userToggle && certificateToggle && !userToggle.value && !certificateToggle.value ? { userDetailsOrCertificateRequired: true} : null; + }; + + private certificateValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => { + const certificateToggle = control.get('certificateToggle'); + const subject = control.get('subject'); + const validFrom = control.get('validFrom'); + const validUntil = control.get('validUntil'); + const issuer = control.get('issuer'); + const fingerprints = control.get('fingerprints'); + return certificateToggle && subject && validFrom && validUntil && issuer && fingerprints + && certificateToggle.value && !(subject.value && validFrom.value && validUntil.value && issuer.value && fingerprints.value) ? { certificateDetailsRequired: true} : null; }; constructor(private dialogRef: MatDialogRef<UserDetailsDialogComponent>, @@ -57,9 +72,14 @@ export class UserDetailsDialogComponent { ...data.row, password: '', // ensures the user password is cleared before editing confirmation: '', - certificate: data.row.certificate || {}, - } - : { + certificate: { + subject: data.row.subject, + validFrom: data.row.validFrom, + validUntil: data.row.validUntil, + issuer: data.row.issuer, + fingerprints: data.row.fingerprints, + } + }: { userName: '', email: '', password: '', @@ -69,19 +89,23 @@ export class UserDetailsDialogComponent { certificate: {}, }; + const userDetailsToggled: boolean = user && !!user.userName; + this.userForm = fb.group({ - 'userName': new FormControl({value: user.userName, disabled: this.editMode}, this.editMode ? Validators.nullValidator : null), - 'role': new FormControl(user.role, Validators.required), - 'password': new FormControl(user.password, [Validators.required, Validators.pattern(this.passwordPattern)]), - 'confirmation': new FormControl(user.password, Validators.pattern(this.passwordPattern)), - - 'subject': new FormControl({ value: user.certificate.subject, disabled: true }), - 'validFrom': new FormControl({ value: user.certificate.validFrom, disabled: true }), - 'validUntil': new FormControl({ value: user.certificate.validUntil, disabled: true }), - 'issuer': new FormControl({ value: user.certificate.issuer, disabled: true }), - 'fingerprints': new FormControl({ value: user.certificate.fingerprints, disabled: true }), + 'userToggle': new FormControl(userDetailsToggled), + 'userName': new FormControl({ value: user.userName, disabled: this.editMode || !userDetailsToggled }, this.editMode ? Validators.nullValidator : null), + 'role': new FormControl({ value: user.role, disabled: !userDetailsToggled }, Validators.required), + 'password': new FormControl({ value: user.password, disabled: !userDetailsToggled }, [Validators.required, Validators.pattern(this.passwordPattern)]), + 'confirmation': new FormControl({ value: user.password, disabled: !userDetailsToggled }, Validators.pattern(this.passwordPattern)), + + 'certificateToggle': new FormControl(user && user.certificate && !!user.certificate.subject), + 'subject': new FormControl({ value: user.certificate.subject, disabled: true }, Validators.required), + 'validFrom': new FormControl({ value: user.certificate.validFrom, disabled: true }, Validators.required), + 'validUntil': new FormControl({ value: user.certificate.validUntil, disabled: true }, Validators.required), + 'issuer': new FormControl({ value: user.certificate.issuer, disabled: true }, Validators.required), + 'fingerprints': new FormControl({ value: user.certificate.fingerprints, disabled: true }, Validators.required), }, { - validator: this.passwordConfirmationValidator + validator: [this.passwordConfirmationValidator, this.atLeastOneToggleCheckedValidator, this.certificateValidator] }); this.userService.getUserRoles$().subscribe(userRoles => { @@ -115,7 +139,7 @@ export class UserDetailsDialogComponent { 'validFrom': this.datePipe.transform(new Date().toString(), this.dateFormat), 'validUntil': this.datePipe.transform(new Date().toString(), this.dateFormat), 'issuer': 'issues', - 'fingerprints': 'fingerprints', + 'fingerprints': 'fingerprints' }); // }, // err => {