Select Git revision
user-details-dialog.component.ts
Sebastian-Ion TINCU authored
Rename roles to match those defined in the back-end. Fix wrong role binding when adding or editing users. Fix missing id and status properties. Improve user resource annotations.
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
user-details-dialog.component.ts 7.60 KiB
import {Component, Inject, ViewChild} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef, 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';
import {UserRo} from '../user-ro.model';
import {SearchTableEntityStatus} from '../../common/search-table/search-table-entity-status.model';
import {AlertService} from '../../alert/alert.service';
import {CertificateService} from '../certificate.service';
import {CertificateRo} from "../certificate-ro.model";
import {DatePipe} from "../../custom-date/date.pipe";
@Component({
selector: 'user-details-dialog',
templateUrl: './user-details-dialog.component.html',
styleUrls: ['user-details-dialog.component.css']
})
export class UserDetailsDialogComponent {
static readonly NEW_MODE = 'New User';
static readonly EDIT_MODE = 'User Edit';
// readonly emailPattern = '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}';
readonly passwordPattern = '^(?=.*[A-Z])(?=.*[ !#$%&\'()*+,-./:;<=>?@\\[^_`{|}~\\\]"])(?=.*[0-9])(?=.*[a-z]).{8,32}$';
readonly dateFormat: string = 'yyyy-MM-dd HH:mm:ssZ';
id: number;
editMode: boolean;
formTitle: string;
userRoles = [];
existingRoles = [];
userForm: FormGroup;
@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 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 validTo = control.get('validTo');
const issuer = control.get('issuer');
const serialNumber = control.get('serialNumber');
return certificateToggle && subject && validFrom && validTo && issuer && serialNumber
&& certificateToggle.value && !(subject.value && validFrom.value && validTo.value && issuer.value && serialNumber.value) ? { certificateDetailsRequired: true} : null;
};
constructor(private dialogRef: MatDialogRef<UserDetailsDialogComponent>,
private userService: UserService,
private certificateService: CertificateService,
private alertService: AlertService,
private datePipe: DatePipe,
@Inject(MAT_DIALOG_DATA) public data: any,
private fb: FormBuilder) {
this.editMode = data.edit;
this.id = data.row && data.row.id;
this.formTitle = this.editMode ? UserDetailsDialogComponent.EDIT_MODE: UserDetailsDialogComponent.NEW_MODE;
const user: UserRo & { confirmation?: string } = this.editMode
? {
...data.row,
password: '', // ensures the user password is cleared before editing
confirmation: '',
certificate: {
subject: data.row.subject,
validFrom: data.row.validFrom,
validTo: data.row.validTo,
issuer: data.row.issuer,
serialNumber: data.row.serialNumber,
},
}: {
username: '',
email: '',
password: '',
confirmation: '',
role: '',
certificate: {},
};
const userDetailsToggled: boolean = user && !!user.username;
this.userForm = fb.group({
'userToggle': new FormControl(userDetailsToggled),
'username': new FormControl({ value: user.username, disabled: this.editMode || !userDetailsToggled }, this.editMode ? Validators.nullValidator : null),
'role': new FormControl({ value: Role[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),
'validTo': new FormControl({ value: user.certificate.validTo, disabled: true }, Validators.required),
'issuer': new FormControl({ value: user.certificate.issuer, disabled: true }, Validators.required),
'serialNumber': new FormControl({ value: user.certificate.serialNumber, disabled: true }, Validators.required),
}, {
validator: [this.passwordConfirmationValidator, this.atLeastOneToggleCheckedValidator, this.certificateValidator]
});
this.userService.getUserRoles$().subscribe(userRoles => {
this.userRoles = userRoles.json();
this.existingRoles = this.editMode
? this.getAllowedRoles(this.userRoles, user.role)
: this.userRoles;
console.log(this.userRoles, this.existingRoles);
});
}
submitForm() {
this.dialogRef.close(true);
}
uploadCertificate(event) {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = (e) => {
this.certificateService.uploadCertificate$(reader.result).subscribe((res: CertificateRo) => {
this.userForm.patchValue({
'subject': res.subject,
'validFrom': this.datePipe.transform(res.validFrom.toString(), this.dateFormat),
'validTo': this.datePipe.transform(res.validTo.toString(), this.dateFormat),
'issuer': res.issuer,
'serialNumber': res.serialNumber
});
},
err => {
this.alertService.exception('Error uploading certificate file ' + file.name, err);
}
);
};
reader.onerror = (err) => {
this.alertService.exception('Error reading certificate file ' + file.name, err);
};
reader.readAsBinaryString(file);
}
onUserToggleChanged({checked}: MatSlideToggleChange) {
const action = checked ? 'enable' : 'disable';
this.userForm.get('username')[action]();
this.userForm.get('role')[action]();
this.userForm.get('password')[action]();
this.userForm.get('confirmation')[action]();
}
get current(): UserRo {
const rawValue = this.userForm.getRawValue();
return {
...rawValue,
id: this.id,
role: Object.keys(Role).find(k => Role[k] === rawValue.role), // ugly hack to find the corresponding enum key as a string
status: this.id ? SearchTableEntityStatus.UPDATED: SearchTableEntityStatus.NEW,
};
}
// filters out roles so that the user cannot change from system administrator to the other roles or vice-versa
private getAllowedRoles(allRoles, userRole) {
if (userRole === Role.SYSTEM_ADMIN) {
return [Role.SYSTEM_ADMIN];
} else {
return allRoles.filter(role => role !== Role.SYSTEM_ADMIN);
}
}
}