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..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
@@ -51,24 +51,37 @@
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..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
@@ -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';
@@ -28,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',
@@ -67,14 +77,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 +125,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 < control.parent.value?.minimumLength) {
+ return {lessMin: true};
+ }
+ }
+ return null;
+ };
+ }
+
discardSetting() {
this.securitySettingsFormGroup.reset(this.securitySettings);
}
@@ -189,5 +213,4 @@ export class SecuritySettingsComponent extends PageComponent implements HasConfi
confirmForm(): UntypedFormGroup {
return this.securitySettingsFormGroup.dirty ? this.securitySettingsFormGroup : this.jwtSecuritySettingsFormGroup;
}
-
}
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 22f46843a3..ba4b78c34a 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} }}
+ 0">
+
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..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
@@ -213,6 +213,10 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro
errors.minLength = true;
}
+ if (!value.length || this.passwordPolicy.maximumLength > 0 && value.length > this.passwordPolicy.maximumLength) {
+ errors.maxLength = true;
+ }
+
return isEqual(errors, {}) ? null : errors;
};
}
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/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