Code development platform for open source projects from the European Union institutions

Skip to content
Snippets Groups Projects
user-details-dialog.component.ts 17.4 KiB
Newer Older
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
import {Component, Inject, ViewChild} from '@angular/core';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
import {
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
  AbstractControl,
  AsyncValidatorFn,
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
  FormBuilder,
  FormControl,
  FormGroup,
  ValidationErrors,
  ValidatorFn,
  Validators
} from '@angular/forms';
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";
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
import {GlobalLookups} from "../../common/global-lookups";
import {Observable, of} from "rxjs";
import {catchError, map} from "rxjs/operators";
import {UserDetailsService} from "./user-details.service";
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
import {MatSlideToggleChange} from "@angular/material/slide-toggle";
import {AccessTokenRo} from "../../common/access-token-generation-dialog/access-token-ro.model";

@Component({
  selector: 'user-details-dialog',
  templateUrl: './user-details-dialog.component.html',
  styleUrls: ['user-details-dialog.component.css']
export class UserDetailsDialogComponent {
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
  @ViewChild('fileInput') private fileInput;
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed

  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';
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
  readonly usernamePattern = '^[a-zA-Z0-9]{4,32}$';
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed

  editMode: boolean;
  userRoles = [];
  certificateValidationMessage: string = null;
  isCertificateInvalid: boolean = true;
  existingRoles = [];
  userForm: FormGroup;
  current: UserRo;
  tempStoreForCertificate: CertificateRo = this.newCertificateRo();
  tempStoreForUser: UserRo = this.newUserRo();
  newCertFile:File=null;
  private passwordConfirmationValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
    const userToggle = control.get('userToggle');
    const password = control.get('password');
    const confirmation = control.get('confirmation');
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    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');
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    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;
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
  private certificateExistValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
    const certificateToggle = control.get('certificateToggle');
    const certificateId = control.get('certificateId');
    // get all persisted
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    const listIds = this.lookups.cachedServiceGroupOwnerList.map(a => a.certificate ? a.certificate.certificateId : "NoId");
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed

    return certificateToggle && certificateId && certificateId.value
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    && listIds.includes(certificateId.value) && this.current.certificate && certificateId.value !== this.current.certificate.certificateId ? {certificateIdExists: true} : null;
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
  };

Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
  notInList(list: string[]) {
    return (c: AbstractControl): { [key: string]: any } => {
      if (c.value && list.includes(c.value.toString().toLowerCase())) {
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
        return {'notInList': {valid: false}};
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      return null;
    }
  }

  constructor(private dialogRef: MatDialogRef<UserDetailsDialogComponent>,
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
              private lookups: GlobalLookups,
              private certificateService: CertificateService,
              private userDetailsService: UserDetailsService,
              private alertService: AlertService,
              private datePipe: DatePipe,
              @Inject(MAT_DIALOG_DATA) public data: any,
              private fb: FormBuilder) {
    this.userId = data.row && data.row.userId;
    this.editMode = this.mode !== UserDetailsDialogMode.NEW_MODE;
    this.current = this.editMode
        password: '', // ensures the user password is cleared before editing
        confirmation: '',
        certificate: data.row.certificate ? {...data.row.certificate} : this.newCertificateRo()
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      } : {
        active: true,
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
        username: '',
        emailAddress: '',
        password: '',
        confirmation: '',
        role: '',
        encodedValue: '',
        crlUrl: '',
        status: SearchTableEntityStatus.NEW,
        statusPassword: SearchTableEntityStatus.NEW,
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
        certificate: this.newCertificateRo(),
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    // The password authentication is if username exists
    // if it's off on clear then clear the username!
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    const bUserPasswordAuthentication: boolean = !!this.current.username;
    const bSetPassword: boolean = false;
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    // calculate allowed roles
    this.existingRoles = this.getAllowedRoles(this.current.role);

    // set empty form ! do not bind it to current object !
    this.userForm = fb.group({
      // common values
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      'active': new FormControl({value: ''}, []),
      'emailAddress': new FormControl({value: ''}, [Validators.pattern(this.emailPattern), Validators.maxLength(255)]),
      'role': new FormControl({
        value: '',
        disabled: this.mode === UserDetailsDialogMode.PREFERENCES_MODE
      }, Validators.required),
      // username/password authentication
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      'userToggle': new FormControl(bUserPasswordAuthentication),
      'passwordToggle': new FormControl({value: bSetPassword, disabled: !bUserPasswordAuthentication}),
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      'username': new FormControl({value: '', disabled: this.editMode || !bUserPasswordAuthentication},
        !this.editMode || !this.current.username
          ? [Validators.nullValidator, Validators.pattern(this.usernamePattern), this.notInList(this.lookups.cachedServiceGroupOwnerList.map(a => a.username ? a.username.toLowerCase() : null))]
          : null),
      // improve notInList validator
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      'password': new FormControl({value: '', disabled: !bUserPasswordAuthentication || !bSetPassword},
        [Validators.required, Validators.pattern(this.passwordPattern)]),
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      'confirmation': new FormControl({value: ''}),
      // certificate authentication
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      'certificateToggle': new FormControl(this.current && this.current.certificate && !!this.current.certificate.certificateId),
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      'subject': new FormControl({value: '', disabled: true}, Validators.required),
      'validFrom': new FormControl({value: '', disabled: true}, Validators.required),
      'validTo': new FormControl({value: '', disabled: true}, Validators.required),
      'issuer': new FormControl({value: '', disabled: true}, Validators.required),
      'serialNumber': new FormControl({value: '', disabled: true}, Validators.required),
      'crlUrl': new FormControl({value: '', disabled: true}),
      'encodedValue': new FormControl({value: '', disabled: true}),
      'certificateId': new FormControl({value: '', disabled: true,}, [Validators.required]),
      'isCertificateValid': new FormControl({value: 'true', disabled: true,}, [Validators.requiredTrue]
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      ),
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      validator: [this.passwordConfirmationValidator,
        this.atLeastOneToggleCheckedValidator,
        this.certificateValidator,
        this.certificateExistValidator,
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    // bind values to form! not property
    this.userForm.controls['active'].setValue(this.current.active);
    this.userForm.controls['emailAddress'].setValue(this.current.emailAddress);
    this.userForm.controls['role'].setValue(this.current.role);
    // username/password authentication
    this.userForm.controls['username'].setValue(this.current.username);
    this.userForm.controls['password'].setValue(this.current.password);
    // certificate authentication
    this.userForm.controls['subject'].setValue(this.current.certificate.subject);
    this.userForm.controls['validFrom'].setValue(this.current.certificate.validFrom);
    this.userForm.controls['validTo'].setValue(this.current.certificate.validTo);
    this.userForm.controls['issuer'].setValue(this.current.certificate.issuer);
    this.userForm.controls['serialNumber'].setValue(this.current.certificate.serialNumber);
    this.userForm.controls['certificateId'].setValue(this.current.certificate.certificateId);
    this.userForm.controls['crlUrl'].setValue(this.current.certificate.crlUrl);
    this.userForm.controls['encodedValue'].setValue(this.current.certificate.encodedValue);
    this.userForm.controls['isCertificateValid'].setValue(!this.current.certificate.invalid);
    this.certificateValidationMessage = this.current.certificate.invalidReason;
    this.isCertificateInvalid = this.current.certificate.invalid;
    // if edit mode and user is given - toggle is disabled
    // username should not be changed.!
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    if (this.editMode && bUserPasswordAuthentication) {
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      this.userForm.controls['userToggle'].disable();
    }
  }

  submitForm() {
    this.dialogRef.close(true);
  uploadCertificate(event) {
    this.newCertFile=null;
    const file = event.target.files[0];
    this.certificateService.validateCertificate(file).subscribe((res: CertificateRo) => {
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
        if (res && res.certificateId) {
          this.userForm.patchValue({
            'subject': res.subject,
            'validFrom': res.validFrom,
            'validTo': res.validTo,
            'issuer': res.issuer,
            'serialNumber': res.serialNumber,
            'certificateId': res.certificateId,
            'crlUrl': res.crlUrl,
            'encodedValue': res.encodedValue,
            'isCertificateValid': !res.invalid
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
          });
          this.certificateValidationMessage = res.invalidReason;
          this.isCertificateInvalid = res.invalid;
          this.newCertFile=file;
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
        } else {
          this.alertService.exception("Error occurred while reading certificate.", "Check if uploaded file has valid certificate type.", false);
        }
      },
      err => {
        this.alertService.exception('Error uploading certificate file ' + file.name, err);
      }
    );
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
  onCertificateToggleChanged({checked}: MatSlideToggleChange) {
    if (checked) {
      // fill from temp
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      this.userForm.controls['certificateId'].setValue(this.tempStoreForCertificate.certificateId);
      this.userForm.controls['subject'].setValue(this.tempStoreForCertificate.subject);
      this.userForm.controls['issuer'].setValue(this.tempStoreForCertificate.issuer);
      this.userForm.controls['serialNumber'].setValue(this.tempStoreForCertificate.serialNumber);
      this.userForm.controls['validFrom'].setValue(this.tempStoreForCertificate.validFrom);
      this.userForm.controls['validFrom'].setValue(this.tempStoreForCertificate.validFrom);
      this.userForm.controls['validTo'].setValue(this.tempStoreForCertificate.validTo);
      this.userForm.controls['encodedValue'].setValue(this.tempStoreForCertificate.encodedValue);
      this.userForm.controls['crlUrl'].setValue(this.tempStoreForCertificate.crlUrl);
      this.certificateValidationMessage = this.tempStoreForCertificate.invalidReason;
      this.isCertificateInvalid = this.tempStoreForCertificate.invalid;
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    } else {
      // store data to temp, set values to null
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      this.tempStoreForCertificate.certificateId = this.userForm.controls['certificateId'].value;
      this.tempStoreForCertificate.subject = this.userForm.controls['subject'].value;
      this.tempStoreForCertificate.issuer = this.userForm.controls['issuer'].value;
      this.tempStoreForCertificate.serialNumber = this.userForm.controls['serialNumber'].value;
      this.tempStoreForCertificate.validFrom = this.userForm.controls['validFrom'].value;
      this.tempStoreForCertificate.validTo = this.userForm.controls['validTo'].value;
      this.tempStoreForCertificate.encodedValue = this.userForm.controls['encodedValue'].value;
      this.tempStoreForCertificate.crlUrl = this.userForm.controls['crlUrl'].value;

      this.tempStoreForCertificate.invalidReason = this.certificateValidationMessage;
      this.tempStoreForCertificate.invalid = this.isCertificateInvalid;
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed

      this.userForm.controls['certificateId'].setValue("");
      this.userForm.controls['subject'].setValue("");
      this.userForm.controls['issuer'].setValue("");
      this.userForm.controls['serialNumber'].setValue("");
      this.userForm.controls['validFrom'].setValue("");
      this.userForm.controls['validTo'].setValue("");
      this.userForm.controls['crlUrl'].setValue("");
      this.userForm.controls['encodedValue'].setValue("");
      this.userForm.controls['isCertificateValid'].setValue("true");

      this.certificateValidationMessage = null;
      this.isCertificateInvalid = false;
  onUserToggleChanged({checked}: MatSlideToggleChange) {
    const action = checked ? 'enable' : 'disable';
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    this.userForm.get('username')[action]();
    this.userForm.get('password')[action]();
    this.userForm.get('confirmation')[action]();

Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    if (checked) {
      this.userForm.controls['username'].setValue(this.tempStoreForUser.username);
      this.userForm.controls['password'].setValue(this.tempStoreForUser.password);
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    } else {
      // store data to temp, set values to null
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      this.tempStoreForUser.username = this.userForm.controls['username'].value;
      this.tempStoreForUser.password = this.userForm.controls['password'].value;
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed

      this.userForm.controls['username'].setValue("");
      this.userForm.controls['password'].setValue("");
    }
    this.userForm.controls['passwordToggle'].setValue(checked || !this.editMode);
  }

  onPasswordToggleChanged({checked}: MatSlideToggleChange) {
    const action = checked ? 'enable' : 'disable';
    this.userForm.get('password')[action]();
    this.userForm.get('confirmation')[action]();
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    if (!checked) {
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      this.userForm.get('password').setValue('');
      this.userForm.get('confirmation').setValue('');
    }
  isPreferencesMode() {
    return this.mode === UserDetailsDialogMode.PREFERENCES_MODE;
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
  public getCurrent(): UserRo {
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    this.current.active = this.userForm.get('active').value;
    this.current.emailAddress = this.userForm.get('emailAddress').value;
    this.current.role = this.userForm.get('role').value;
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    // certificate data
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    if (this.userForm.get('certificateToggle')) {
      this.current.certificate.certificateId = this.userForm.controls['certificateId'].value;
      this.current.certificate.subject = this.userForm.controls['subject'].value;
      this.current.certificate.issuer = this.userForm.controls['issuer'].value;
      this.current.certificate.serialNumber = this.userForm.controls['serialNumber'].value;
      this.current.certificate.validFrom = this.userForm.controls['validFrom'].value;
      this.current.certificate.validTo = this.userForm.controls['validTo'].value;
      this.current.certificate.crlUrl = this.userForm.controls['crlUrl'].value;
      this.current.certificate.encodedValue = this.userForm.controls['encodedValue'].value;
      this.current.certificate.invalid = this.isCertificateInvalid;
      this.current.certificate.invalidReason = this.certificateValidationMessage;
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    } else {
      this.current.certificate = null;
    }
    // set username and password for new
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    if (this.userForm.get('userToggle')) {
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      if (!this.editMode || !this.current.username) {
        this.current.username = this.userForm.controls['username'].value;
        this.current.password = this.userForm.controls['password'].value;
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      }
      // if edit mode and password on  - set password
      else if (this.editMode && this.userForm.get('passwordToggle')) {
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
        this.current.password = this.userForm.controls['password'].value;
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      }
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    } else {
      this.current.username = '';
      this.current.password = '';
    // update data
    return this.current;
  }

  // filters out roles so that the user cannot change from system administrator to the other roles or vice-versa
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
  private getAllowedRoles(userRole) {
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    if (!this.editMode) {
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      return Object.keys(Role);
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    } else if (userRole === Role.SYSTEM_ADMIN) {
      return [Role.SYSTEM_ADMIN];
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      return Object.keys(Role).filter(role => role !== Role.SYSTEM_ADMIN);
    }
  }

  private newCertificateRo(): CertificateRo {
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    return {
      subject: '',
      validFrom: null,
      validTo: null,
      issuer: '',
      serialNumber: '',
      certificateId: '',
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
      fingerprints: '',
      crlUrl: '',
      encodedValue: '',
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
  private newUserRo(): UserRo {
Joze RIHTARSIC's avatar
Joze RIHTARSIC committed
    return {
      id: null,
      index: null,
      username: '',
      emailAddress: '',
      role: '',
      active: true,
      status: SearchTableEntityStatus.NEW,
      statusPassword: SearchTableEntityStatus.NEW

export enum UserDetailsDialogMode {
  NEW_MODE = 'New User',
  EDIT_MODE = 'User Edit',
  PREFERENCES_MODE = 'Edit',
}