From 654c887f0e3ce9264c8fb36f27bb4b5bc0db6b55 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Thu, 7 Dec 2023 12:52:13 +0200 Subject: [PATCH 1/7] UI: Add max password length with validation --- .../admin/security-settings.component.html | 59 ++++++++++++------- .../admin/security-settings.component.ts | 27 ++++++++- .../pages/security/security.component.html | 7 +++ .../home/pages/security/security.component.ts | 5 ++ .../src/app/shared/models/settings.models.ts | 2 + .../assets/locale/locale.constant-en_US.json | 9 ++- 6 files changed, 83 insertions(+), 26 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html index ef29248767..f49b2b25e8 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html @@ -51,24 +51,36 @@
admin.password-policy
- - admin.minimum-password-length - - - {{ 'admin.minimum-password-length-required' | translate }} - - - {{ 'admin.minimum-password-length-range' | translate }} - - - {{ 'admin.minimum-password-length-range' | translate }} - - +
+ + admin.minimum-password-length + + + {{ 'admin.minimum-password-length-required' | translate }} + + + {{ 'admin.minimum-password-length-range' | translate }} + + + {{ 'admin.minimum-password-length-range' | translate }} + + + + admin.maximum-password-length + + + {{ 'admin.maximum-password-length-min' | translate }} + + + {{ 'admin.maximum-password-length-less-min' | translate }} + + +
admin.minimum-uppercase-letters @@ -140,9 +152,14 @@
- - admin.allow-whitespace - +
+ + admin.allow-whitespace + + + admin.force-reset-password-if-no-valid + +
diff --git a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts index cdaf0747af..9e969bf8e0 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts @@ -19,7 +19,14 @@ import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { PageComponent } from '@shared/components/page.component'; import { Router } from '@angular/router'; -import { UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; +import { + AbstractControl, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, ValidationErrors, + ValidatorFn, + Validators +} from '@angular/forms'; import { JwtSettings, SecuritySettings } from '@shared/models/settings.models'; import { AdminService } from '@core/http/admin.service'; import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; @@ -67,14 +74,16 @@ export class SecuritySettingsComponent extends PageComponent implements HasConfi userLockoutNotificationEmail: ['', []], passwordPolicy: this.fb.group( { - minimumLength: [null, [Validators.required, Validators.min(5), Validators.max(50)]], + minimumLength: [null, [Validators.required, Validators.min(6), Validators.max(50)]], + maximumLength: [null, [Validators.min(6), this.maxPasswordValidation()]], minimumUppercaseLetters: [null, Validators.min(0)], minimumLowercaseLetters: [null, Validators.min(0)], minimumDigits: [null, Validators.min(0)], minimumSpecialCharacters: [null, Validators.min(0)], passwordExpirationPeriodDays: [null, Validators.min(0)], passwordReuseFrequencyDays: [null, Validators.min(0)], - allowWhitespaces: [true] + allowWhitespaces: [true], + forceUserToResetPasswordIfNotValid: [false] } ) }); @@ -113,6 +122,18 @@ export class SecuritySettingsComponent extends PageComponent implements HasConfi })).subscribe(() => {}); } + private maxPasswordValidation(): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + const value: string = control.value; + if (value) { + if (value < this.securitySettingsFormGroup.get('passwordPolicy.minimumLength').value) { + return {lessMin: true}; + } + } + return null; + }; + } + discardSetting() { this.securitySettingsFormGroup.reset(this.securitySettings); } diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.html b/ui-ngx/src/app/modules/home/pages/security/security.component.html index 45460e086d..9d40e0786b 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.html @@ -105,6 +105,13 @@ {{ 'security.password-requirement.character' | translate : {count: passwordPolicy.minimumLength} }}

+
+

security.password-requirement.at-most

+

+ + {{ 'security.password-requirement.character' | translate : {count: passwordPolicy.maximumLength} }} +

+
diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.ts b/ui-ngx/src/app/modules/home/pages/security/security.component.ts index 9a99e7d6d4..d79a6c59b4 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.ts +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.ts @@ -213,6 +213,11 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro errors.minLength = true; } + if (this.passwordPolicy?.maximumLength > 0 && + (value.length > this.passwordPolicy?.maximumLength || value.length < this.passwordPolicy.minimumLength)) { + errors.maxLength = true; + } + return isEqual(errors, {}) ? null : errors; }; } diff --git a/ui-ngx/src/app/shared/models/settings.models.ts b/ui-ngx/src/app/shared/models/settings.models.ts index 9238dae197..9dd599d49b 100644 --- a/ui-ngx/src/app/shared/models/settings.models.ts +++ b/ui-ngx/src/app/shared/models/settings.models.ts @@ -99,12 +99,14 @@ export type DeviceConnectivitySettings = Record Date: Thu, 7 Dec 2023 16:45:37 +0200 Subject: [PATCH 2/7] UI: Refactoring validation func --- .../app/modules/home/pages/admin/security-settings.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts index 9e969bf8e0..5c64d78049 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts @@ -126,7 +126,7 @@ export class SecuritySettingsComponent extends PageComponent implements HasConfi return (control: AbstractControl): ValidationErrors | null => { const value: string = control.value; if (value) { - if (value < this.securitySettingsFormGroup.get('passwordPolicy.minimumLength').value) { + if (value < control.parent.value?.minimumLength) { return {lessMin: true}; } } From 793639a87798203499f5cdc68da70cf9f66cd3a7 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 8 Dec 2023 15:09:36 +0200 Subject: [PATCH 3/7] UI: Refactoring --- .../home/pages/admin/security-settings.component.html | 9 ++++++++- .../home/pages/admin/security-settings.component.scss | 6 ++++++ .../home/pages/admin/security-settings.component.ts | 7 ++++++- .../app/modules/login/pages/login/login.component.html | 3 ++- .../src/app/modules/login/pages/login/login.component.ts | 4 ++++ ui-ngx/src/app/shared/models/constants.ts | 3 ++- ui-ngx/src/assets/locale/locale.constant-en_US.json | 1 + 7 files changed, 29 insertions(+), 4 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html index f49b2b25e8..189d75d56d 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html @@ -157,7 +157,14 @@ admin.allow-whitespace - admin.force-reset-password-if-no-valid + + {{'admin.force-reset-password-if-no-valid' | translate}} + + +
diff --git a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.scss index d9300a5504..fcafcb9c95 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.scss +++ b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.scss @@ -32,4 +32,10 @@ color: rgba(0, 0, 0, .7); } } + + .hint-icon { + width: 15px; + height: 15px; + vertical-align: top; + } } diff --git a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts index 5c64d78049..4ca662763a 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts @@ -35,7 +35,10 @@ import { randomAlphanumeric } from '@core/utils'; import { AuthService } from '@core/auth/auth.service'; import { DialogService } from '@core/services/dialog.service'; import { TranslateService } from '@ngx-translate/core'; -import { Observable, of } from 'rxjs'; +import { forkJoin, Observable, of } from 'rxjs'; +import { MatCheckboxChange } from '@angular/material/checkbox'; +import { AlarmInfo } from '@shared/models/alarm.models'; +import { QueueProcessingStrategyTypes, QueueProcessingStrategyTypesMap } from '@shared/models/queue.models'; @Component({ selector: 'tb-security-settings', @@ -211,4 +214,6 @@ export class SecuritySettingsComponent extends PageComponent implements HasConfi return this.securitySettingsFormGroup.dirty ? this.securitySettingsFormGroup : this.jwtSecuritySettingsFormGroup; } + protected readonly queueProcessingStrategyTypes = QueueProcessingStrategyTypes; + protected readonly queueProcessingStrategyTypesMap = QueueProcessingStrategyTypesMap; } diff --git a/ui-ngx/src/app/modules/login/pages/login/login.component.html b/ui-ngx/src/app/modules/login/pages/login/login.component.html index f5797ae574..8748917ee7 100644 --- a/ui-ngx/src/app/modules/login/pages/login/login.component.html +++ b/ui-ngx/src/app/modules/login/pages/login/login.component.html @@ -56,7 +56,8 @@ lock
-
diff --git a/ui-ngx/src/app/modules/login/pages/login/login.component.ts b/ui-ngx/src/app/modules/login/pages/login/login.component.ts index 2fc1c7c50d..e61322b653 100644 --- a/ui-ngx/src/app/modules/login/pages/login/login.component.ts +++ b/ui-ngx/src/app/modules/login/pages/login/login.component.ts @@ -32,6 +32,8 @@ import { OAuth2ClientInfo } from '@shared/models/oauth2.models'; }) export class LoginComponent extends PageComponent implements OnInit { + passwordViolation = false; + loginFormGroup = this.fb.group({ username: '', password: '' @@ -57,6 +59,8 @@ export class LoginComponent extends PageComponent implements OnInit { if (error && error.error && error.error.errorCode) { if (error.error.errorCode === Constants.serverErrorCode.credentialsExpired) { this.router.navigateByUrl(`login/resetExpiredPassword?resetToken=${error.error.resetToken}`); + } else if (error.error.errorCode === Constants.serverErrorCode.passwordViolation) { + this.passwordViolation = true; } } } diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 4f904a4bf3..c119ae327f 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -30,7 +30,8 @@ export const Constants = { badRequestParams: 31, itemNotFound: 32, tooManyRequests: 33, - tooManyUpdates: 34 + tooManyUpdates: 34, + passwordViolation: 45 }, entryPoints: { login: '/api/auth/login', diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 8e9433e6d2..93ec444fea 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -200,6 +200,7 @@ "password-reuse-frequency-days-range": "Password reuse frequency in days can't be negative", "allow-whitespace": "Allow whitespace", "force-reset-password-if-no-valid": "Force to reset password if not valid", + "force-reset-password-if-no-valid-hint": "Please, take into it can affect all users with passwords that violate current policy.", "general-policy": "General policy", "max-failed-login-attempts": "Maximum number of failed login attempts, before account is locked", "minimum-max-failed-login-attempts-range": "Maximum number of failed login attempts can't be negative", From 515c7e1b07b28d32ab76ac09ba627f228150f217 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 8 Dec 2023 16:06:46 +0200 Subject: [PATCH 4/7] UI: Refactoring --- .../home/pages/admin/security-settings.component.html | 8 ++------ .../home/pages/admin/security-settings.component.ts | 3 --- .../modules/home/pages/security/security.component.html | 5 +++-- .../app/modules/home/pages/security/security.component.ts | 4 ++-- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html index 189d75d56d..886dd79ef6 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html @@ -73,6 +73,7 @@ admin.maximum-password-length + {{ 'admin.maximum-password-length-min' | translate }} @@ -157,13 +158,8 @@ admin.allow-whitespace - + {{'admin.force-reset-password-if-no-valid' | translate}} - -
diff --git a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts index 4ca662763a..a2d43e1a8f 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts @@ -213,7 +213,4 @@ export class SecuritySettingsComponent extends PageComponent implements HasConfi confirmForm(): UntypedFormGroup { return this.securitySettingsFormGroup.dirty ? this.securitySettingsFormGroup : this.jwtSecuritySettingsFormGroup; } - - protected readonly queueProcessingStrategyTypes = QueueProcessingStrategyTypes; - protected readonly queueProcessingStrategyTypesMap = QueueProcessingStrategyTypesMap; } diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.html b/ui-ngx/src/app/modules/home/pages/security/security.component.html index 9d40e0786b..b87507f957 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.html +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.html @@ -44,10 +44,11 @@ {{ 'security.password-requirement.incorrect-password-try-again' | translate }} - + login.new-password + {{ 'security.password-requirement.character' | translate : {count: passwordPolicy.minimumLength} }}

-
+

security.password-requirement.at-most

diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.ts b/ui-ngx/src/app/modules/home/pages/security/security.component.ts index d79a6c59b4..0f9b9cef2c 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.ts +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.ts @@ -171,6 +171,7 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro private loadPasswordPolicy() { this.authService.getUserPasswordPolicy().subscribe(policy => { this.passwordPolicy = policy; + this.passwordPolicy.maximumLength = 7; this.changePassword.get('newPassword').setValidators([ this.passwordStrengthValidator(), this.samePasswordValidation(true, 'currentPassword'), @@ -213,8 +214,7 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro errors.minLength = true; } - if (this.passwordPolicy?.maximumLength > 0 && - (value.length > this.passwordPolicy?.maximumLength || value.length < this.passwordPolicy.minimumLength)) { + if (!value.length || this.passwordPolicy.maximumLength > 0 && value.length > this.passwordPolicy.maximumLength) { errors.maxLength = true; } From e83fb9e58803569a1228d9a70deefd320873f6d6 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 8 Dec 2023 16:07:38 +0200 Subject: [PATCH 5/7] UI: Clear --- ui-ngx/src/app/modules/home/pages/security/security.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/pages/security/security.component.ts b/ui-ngx/src/app/modules/home/pages/security/security.component.ts index 0f9b9cef2c..c49ba464d4 100644 --- a/ui-ngx/src/app/modules/home/pages/security/security.component.ts +++ b/ui-ngx/src/app/modules/home/pages/security/security.component.ts @@ -171,7 +171,6 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro private loadPasswordPolicy() { this.authService.getUserPasswordPolicy().subscribe(policy => { this.passwordPolicy = policy; - this.passwordPolicy.maximumLength = 7; this.changePassword.get('newPassword').setValidators([ this.passwordStrengthValidator(), this.samePasswordValidation(true, 'currentPassword'), From 81403e872bf70db16a27681efee3572bbb981ceb Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 8 Dec 2023 16:08:50 +0200 Subject: [PATCH 6/7] UI: Clean css style --- .../home/pages/admin/security-settings.component.scss | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.scss index fcafcb9c95..d9300a5504 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.scss +++ b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.scss @@ -32,10 +32,4 @@ color: rgba(0, 0, 0, .7); } } - - .hint-icon { - width: 15px; - height: 15px; - vertical-align: top; - } } From 3c41eb765cf3e69688fc148830ad428c4b919bb5 Mon Sep 17 00:00:00 2001 From: Artem Dzhereleiko Date: Fri, 8 Dec 2023 16:49:48 +0200 Subject: [PATCH 7/7] UI: Update hint translate --- ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 93ec444fea..cb09328d45 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -200,7 +200,7 @@ "password-reuse-frequency-days-range": "Password reuse frequency in days can't be negative", "allow-whitespace": "Allow whitespace", "force-reset-password-if-no-valid": "Force to reset password if not valid", - "force-reset-password-if-no-valid-hint": "Please, take into it can affect all users with passwords that violate current policy.", + "force-reset-password-if-no-valid-hint": "Please be careful when enabling this feature: it will require users with not valid password to reset their password via email.", "general-policy": "General policy", "max-failed-login-attempts": "Maximum number of failed login attempts, before account is locked", "minimum-max-failed-login-attempts-range": "Maximum number of failed login attempts can't be negative",