diff --git a/src/app/components/preference/dialogs/dialog-users/dialog-users.component.html b/src/app/components/preference/dialogs/dialog-users/dialog-users.component.html index 94465cd5..1cddf272 100644 --- a/src/app/components/preference/dialogs/dialog-users/dialog-users.component.html +++ b/src/app/components/preference/dialogs/dialog-users/dialog-users.component.html @@ -77,6 +77,7 @@ [placeholder]="'preference.users.dialog.password' | translate" [type]="hidePass1 ? 'password' : 'text'" [formControl]="password" + (change)="validate()" (blur)="validate()" (keyup)="validate()" required /> The password {{'validators.required' | translate }} @@ -85,7 +86,9 @@ {{ password?.errors?.['maxlength']?.requiredLength }} {{'validators.maxChars' | translate }} - + + {{'validators.pattern' | translate }} [aA-Zz], [0-9] and "~!@#$%^&*()_"] + {{ password2?.errors?.['maxlength']?.requiredLength }} {{'validators.maxChars' | translate }} + + {{'validators.pattern' | translate }} [aA-Zz], [0-9] and "~!@#$%^&*()_"] + {{ 'preference.users.dialog.personalInfo' | translate }} @@ -237,7 +243,7 @@ class="setting-btn-ok" (click)="onSubmit()" cdkFocusInitial - [disabled]="isCopy && isNotChanged"> + [disabled]="(isCopy && isNotChanged) || isDisabled"> {{'LINK.buttons.save' | translate}} diff --git a/src/app/components/preference/dialogs/dialog-users/dialog-users.component.ts b/src/app/components/preference/dialogs/dialog-users/dialog-users.component.ts index bb9ecb50..5b030a04 100644 --- a/src/app/components/preference/dialogs/dialog-users/dialog-users.component.ts +++ b/src/app/components/preference/dialogs/dialog-users/dialog-users.component.ts @@ -2,244 +2,317 @@ import { Component, Inject, ViewChild, ChangeDetectionStrategy, OnInit, ChangeDe import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { Functions } from '@app/helpers/functions'; import { AuthenticationService, AlertService, PreferenceUserService, PreferenceAdvancedService } from '@app/services'; -import { Validators, FormControl, AbstractControl } from '@angular/forms'; +import { Validators, FormControl, AbstractControl, ValidatorFn, ValidationErrors } from '@angular/forms'; import { emailValidator } from '@app/helpers/email-validator.directive'; import { TranslateService } from '@ngx-translate/core' -import moment from 'moment'; +import moment from 'moment'; import { lastValueFrom } from 'rxjs'; @Component({ - selector: 'app-dialog-users', - templateUrl: './dialog-users.component.html', - styleUrls: ['./dialog-users.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush + selector: 'app-dialog-users', + templateUrl: './dialog-users.component.html', + styleUrls: ['./dialog-users.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush }) export class DialogUsersComponent implements OnInit { - isValidForm = false; - isAdmin = false; - pass2: string; - hidePass1 = true; - regString = /^[a-zA-Z0-9\-\_\.]+$/; - regNum = /^[0-9]+$/; - regDept = /^[a-zA-Z0-9\-\_\.\s]+$/; - originalUser = ''; - isCopy = false; - isNotChanged = true; - bufferName: string; - timeout: any; - lastPasswordChange: string; - lastLogin: string; - username = new FormControl( - {value:'', disabled: true},[ - Validators.required, - Validators.minLength(3), - Validators.maxLength(100), - Validators.pattern(this.regString)], - this.usernameValidator.bind(this) - ); - /* usergroup = new FormControl('', [ - Validators.required, - Validators.minLength(3), - Validators.maxLength(100), - Validators.pattern(this.regString) - ]); */ - usergroup = new FormControl(''); - partid = new FormControl( - {value:'', disabled: true}, [ - Validators.required, - Validators.minLength(1), - Validators.maxLength(100), - Validators.min(1), - Validators.max(99), - Validators.pattern(this.regNum) - ]); - password = new FormControl('', [ - Validators.required, - Validators.minLength(6), - Validators.maxLength(100) - - ]); - password2 = new FormControl('', [ - Validators.required, - Validators.minLength(6), - Validators.maxLength(100) - - ]); - - firstname = new FormControl('', [ - Validators.required, - Validators.minLength(1), - Validators.maxLength(100), - Validators.pattern(this.regString) - ]); - - email = new FormControl('', [ - Validators.required, - emailValidator()]); - lastname = new FormControl('', [ - Validators.maxLength(100), - Validators.pattern(this.regString) - ]); - - department = new FormControl('', [ - Validators.minLength(1), - Validators.maxLength(100), - Validators.pattern(this.regDept) - ]); - - groupList: Array; - bufferGroupList: any; - dateFormat: string; - hasStatistics = false; - constructor( - private authenticationService: AuthenticationService, - public dialogRef: MatDialogRef, - private alertService: AlertService, - private userService: PreferenceUserService, - public translateService: TranslateService, - private _pas: PreferenceAdvancedService, - private cdr: ChangeDetectorRef, - @Inject(MAT_DIALOG_DATA) public data: any) { - translateService.addLangs(['en']) - translateService.setDefaultLang('en') - if (data.isnew) { - data.data = { - username: '', - usergroup: 'user', - partid: 10, - password: '', - firstname: '', - email: '', - lastname: '', - department: '', - guid: Functions.newGuid(), - - }; - - } - - const userData = this.authenticationService.currentUserValue; - this.isAdmin = userData?.user?.admin === true; - if (this.isAdmin) { - this.username.enable(); - this.partid.enable(); - } - this.isCopy = data.isCopy; - if (this.isCopy) { - this.bufferName = data.data.username; - this.isNotChanged = false; - } - /* be sure that this is string */ - data.data.password = String(data.data.password); - (d => { - this.originalUser = d.username; - this.username.setValue(this.isCopy ? d.username + 'COPY' : d.username); - this.usergroup.setValue(d.usergroup.toLowerCase()); - this.partid.setValue(d.partid); - this.firstname.setValue(d.firstname); - this.email.setValue(d.email); - this.lastname.setValue(d.lastname); - this.department.setValue(d.department); - this.lastPasswordChange = d.params?.timestamp_change_password; - this.lastLogin = d.params?.last_loogin; - })(data.data); - if (data.data.params?.last_login || data.data.params?.timestamp_change_password) { - this.hasStatistics = true; - } - this.isValidForm = true; + isValidForm = false; + isAdmin = false; + pass2: string; + hidePass1 = true; + regString = /^[a-zA-Z0-9\-\_\.]+$/; + regNum = /^[0-9]+$/; + regDept = /^[a-zA-Z0-9\-\_\.\s]+$/; + originalUser = ''; + isCopy = false; + isNotChanged = true; + bufferName: string; + timeout: any; + lastPasswordChange: string; + lastLogin: string; + isDisabled: boolean = true; + regPassword: RegExp; + characterRequirements: CharacterRequirements = { + numbers: true, + lowercase: true, + uppercase: true, + special: false, + }; + username = new FormControl( + { value: '', disabled: true }, [ + Validators.required, + Validators.minLength(3), + Validators.maxLength(100), + Validators.pattern(this.regString)], + this.usernameValidator.bind(this) + ); + /* usergroup = new FormControl('', [ + Validators.required, + Validators.minLength(3), + Validators.maxLength(100), + Validators.pattern(this.regString) + ]); */ + usergroup = new FormControl(''); + partid = new FormControl( + { value: '', disabled: true }, [ + Validators.required, + Validators.minLength(1), + Validators.maxLength(100), + Validators.min(1), + Validators.max(99), + Validators.pattern(this.regNum) + ]); + password = new FormControl('', [ + Validators.required, + Validators.minLength(6), + Validators.maxLength(100) + ]); + password2 = new FormControl('', [ + Validators.required, + Validators.minLength(6), + Validators.maxLength(100) + + ]); + + firstname = new FormControl('', [ + Validators.required, + Validators.minLength(1), + Validators.maxLength(100), + Validators.pattern(this.regString) + ]); + + email = new FormControl('', [ + Validators.required, + emailValidator()]); + lastname = new FormControl('', [ + Validators.maxLength(100), + Validators.pattern(this.regString) + ]); + + department = new FormControl('', [ + Validators.minLength(1), + Validators.maxLength(100), + Validators.pattern(this.regDept) + ]); + + groupList: Array; + bufferGroupList: any; + dateFormat: string; + hasStatistics = false; + constructor( + private authenticationService: AuthenticationService, + public dialogRef: MatDialogRef, + private alertService: AlertService, + private userService: PreferenceUserService, + public translateService: TranslateService, + private _pas: PreferenceAdvancedService, + private cdr: ChangeDetectorRef, + @Inject(MAT_DIALOG_DATA) public data: any) { + translateService.addLangs(['en']) + translateService.setDefaultLang('en') + if (data.isnew) { + data.data = { + username: '', + usergroup: 'user', + partid: 10, + password: '', + firstname: '', + email: '', + lastname: '', + department: '', + guid: Functions.newGuid(), + + }; } - async ngOnInit() { - await lastValueFrom(this.userService - .getAllGroups()) - .then((groups: any) => { - this.groupList = groups.data; - }); - await this.getFormat(); - } - onNoClick(): void { - this.dialogRef.close(); + + const userData = this.authenticationService.currentUserValue; + this.isAdmin = userData?.user?.admin === true; + if (this.isAdmin) { + this.username.enable(); + this.partid.enable(); } - onSubmit() { - if (!this.username?.invalid && - !this.usergroup?.invalid && - !this.partid?.invalid && - (this.data?.isNew || this.isCopy ? !this.password?.invalid : true) && - !this.firstname?.invalid && - !this.email?.invalid && - !this.lastname?.invalid && - !this.department?.invalid - ) { - (d => { - d.username = this.username?.value; - d.usergroup = this.usergroup?.value; - d.partid = this.partid?.value; - d.password = this.password?.value; - d.firstname = this.firstname?.value; - d.email = this.email?.value; - d.lastname = this.lastname?.value; - d.department = this.department?.value; - })(this.data.data); - - this.dialogRef.close(this.data); - } else { - this.username.markAsTouched(); - this.usergroup.markAsTouched(); - this.partid.markAsTouched(); - this.password.markAsTouched(); - this.password2.markAsTouched(); - this.firstname.markAsTouched(); - this.email.markAsTouched(); - this.lastname.markAsTouched(); - this.department.markAsTouched(); - } + this.isCopy = data.isCopy; + if (this.isCopy) { + this.bufferName = data.data.username; + this.isNotChanged = false; } - getErrorMessage() { - return this.email.hasError('required') ? 'You must enter a value' : 'Not a valid email'; + /* be sure that this is string */ + data.data.password = String(data.data.password); + (d => { + this.originalUser = d.username; + this.username.setValue(this.isCopy ? d.username + 'COPY' : d.username); + this.usergroup.setValue(d.usergroup.toLowerCase()); + this.partid.setValue(d.partid); + this.firstname.setValue(d.firstname); + this.email.setValue(d.email); + this.lastname.setValue(d.lastname); + this.department.setValue(d.department); + this.lastPasswordChange = d.params?.timestamp_change_password; + this.lastLogin = d.params?.last_loogin; + })(data.data); + if (data.data.params?.last_login || data.data.params?.timestamp_change_password) { + this.hasStatistics = true; } - async getFormat() { - let advancedFormat; - - await this._pas.getSetting('dateTimeFormat', 'system').then((data) => advancedFormat = data[0]?.format); - - if (typeof advancedFormat === 'undefined' || advancedFormat.replace(/\s/, '') === '') { - const locale = navigator.languages[0]; - const localeData = moment.localeData(locale); - const dateFormat = localeData.longDateFormat('L'); - const timeFormat = 'HH:mm:ss'; - this.dateFormat = `${dateFormat} ${timeFormat}`; - } else { - this.dateFormat = advancedFormat; - } - this.lastLogin = moment(this.lastLogin).format(advancedFormat); - this.lastPasswordChange = moment(this.lastPasswordChange).format(advancedFormat); - this.cdr.detectChanges(); + this.isValidForm = true; + + + } + async ngOnInit() { + await lastValueFrom(this.userService + .getAllGroups()) + .then((groups: any) => { + this.groupList = groups.data; + }); + await this.getFormat(); + await this.getPasswordPolicy(); + } + async getPasswordPolicy() { + + this.password.addValidators(passwordValidator(this.characterRequirements)); + this.password2.addValidators(passwordValidator(this.characterRequirements)); + this.password.updateValueAndValidity(); + this.password2.updateValueAndValidity(); + this.cdr.detectChanges(); + } + onNoClick(): void { + this.dialogRef.close(); + } + onSubmit() { + if (!this.username?.invalid && + !this.usergroup?.invalid && + !this.partid?.invalid && + (this.data?.isNew || this.isCopy ? !this.password?.invalid : true) && + !this.firstname?.invalid && + !this.email?.invalid && + !this.lastname?.invalid && + !this.department?.invalid + ) { + (d => { + d.username = this.username?.value; + d.usergroup = this.usergroup?.value; + d.partid = this.partid?.value; + d.password = this.password?.value; + d.firstname = this.firstname?.value; + d.email = this.email?.value; + d.lastname = this.lastname?.value; + d.department = this.department?.value; + })(this.data.data); + + this.dialogRef.close(this.data); + } else { + this.username.markAsTouched(); + this.usergroup.markAsTouched(); + this.partid.markAsTouched(); + this.password.markAsTouched(); + this.password2.markAsTouched(); + this.firstname.markAsTouched(); + this.email.markAsTouched(); + this.lastname.markAsTouched(); + this.department.markAsTouched(); } + } + getErrorMessage() { + return this.email.hasError('required') ? 'You must enter a value' : 'Not a valid email'; + } + async getFormat() { + let advancedFormat; - usernameValidator(userControl: AbstractControl) { - return new Promise(resolve => { - if (typeof this.timeout !== 'undefined') { - clearTimeout(this.timeout); - } - this.timeout = setTimeout(() => { - let validated = false; - this.isTaken(userControl.value).then(data => { - validated = data; - resolve(validated ? { userNameNotAvailable: true } : null); - }); - }, 500); - }); + await this._pas.getSetting('dateTimeFormat', 'system').then((data) => advancedFormat = data[0]?.format); + + if (typeof advancedFormat === 'undefined' || advancedFormat.replace(/\s/, '') === '') { + const locale = navigator.languages[0]; + const localeData = moment.localeData(locale); + const dateFormat = localeData.longDateFormat('L'); + const timeFormat = 'HH:mm:ss'; + this.dateFormat = `${dateFormat} ${timeFormat}`; + } else { + this.dateFormat = advancedFormat; + } + this.lastLogin = moment(this.lastLogin).format(advancedFormat); + this.lastPasswordChange = moment(this.lastPasswordChange).format(advancedFormat); + this.cdr.detectChanges(); + } + validate() { + if ( + this.username?.invalid || + this.usergroup?.invalid || + this.partid?.invalid || + this.firstname?.invalid || + this.email?.invalid || + this.lastname?.invalid || + (this.data?.isNew || this.isCopy || this.password.value !== '' + ? this.password?.invalid + : false) || + this.department?.invalid + ) { + this.isDisabled = true; + } else { + this.isDisabled = false; } - async isTaken(user) { - return this.userService.getAll().toPromise().then((users: any) => { - return ([].concat(users?.data?.map?.(m => m?.username) || [])).includes(user) && user !== this.originalUser; + this.cdr.detectChanges(); + } + usernameValidator(userControl: AbstractControl) { + return new Promise(resolve => { + if (typeof this.timeout !== 'undefined') { + clearTimeout(this.timeout); + } + this.timeout = setTimeout(() => { + let validated = false; + this.isTaken(userControl.value).then(data => { + validated = data; + resolve(validated ? { userNameNotAvailable: true } : null); }); + }, 500); + }); + } + async isTaken(user) { + return this.userService.getAll().toPromise().then((users: any) => { + return ([].concat(users?.data?.map?.(m => m?.username) || [])).includes(user) && user !== this.originalUser; + }); + } + disableClose(e) { + this.dialogRef.disableClose = e; + } + import(text) { + this.data.data.setting = text; + } +} +export interface CharacterRequirements { + special: boolean; + lowercase: boolean; + uppercase: boolean; + numbers: boolean; +} + +export function passwordValidator( + characterRequirements: CharacterRequirements +): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const specialCharaterRegex = /[\*\.!@#$%^&(){}[\]:;<>,.?\/~_+-=|\\]/; + const hasSpecial = !characterRequirements.special || specialCharaterRegex.test(control.value); + const lowercaseRegex = /[a-z]/; + const hasLowercase = !characterRequirements.lowercase || lowercaseRegex.test(control.value); + const uppercaseRegex = /[A-Z]/; + const hasUppercase = !characterRequirements.uppercase || uppercaseRegex.test(control.value); + const numberRegex = /\d/; + const hasNumbers = !characterRequirements.numbers || numberRegex.test(control.value); + const forbidden = + hasNumbers && hasUppercase && hasLowercase && hasSpecial; + const missing = []; + if (!hasNumbers) { + missing.push('numbers'); + } + if (!hasLowercase) { + missing.push('lowercase letters'); } - disableClose(e) { - this.dialogRef.disableClose = e; + if (!hasUppercase) { + missing.push('uppercase letters'); } - import(text) { - this.data.data.setting = text; + if (!hasSpecial) { + missing.push('special characters'); } + const textMissing = missing.join(', ') + return !forbidden ? { missingCharacters: { value: textMissing } } : null; + }; }