From 54c1d47e55b9ee2d008a999ee24007219fc32a99 Mon Sep 17 00:00:00 2001 From: RIHTARSIC Joze <joze.rihtarsic@ext.ec.europa.eu> Date: Mon, 2 Sep 2024 15:43:27 +0200 Subject: [PATCH] [EDELIVERY-13839] Reset password policy validation fix --- smp-angular/src/app/common/global-lookups.ts | 43 ++++++++++--------- .../reset-credential.component.ts | 31 +++++++++++-- .../smp/services/CredentialService.java | 7 ++- 3 files changed, 55 insertions(+), 26 deletions(-) diff --git a/smp-angular/src/app/common/global-lookups.ts b/smp-angular/src/app/common/global-lookups.ts index fc8f8e5e8..177ed421a 100644 --- a/smp-angular/src/app/common/global-lookups.ts +++ b/smp-angular/src/app/common/global-lookups.ts @@ -13,6 +13,7 @@ import {SecurityEventService} from "../security/security-event.service"; import {DateAdapter} from "@angular/material/core"; import {NgxMatDateAdapter} from "@angular-material-components/datetime-picker"; import {DomainRo} from "./model/domain-ro.model"; +import {Subject} from "rxjs"; /** * Purpose of object is to fetch lookups as domains and users @@ -20,6 +21,9 @@ import {DomainRo} from "./model/domain-ro.model"; @Injectable() export class GlobalLookups { + // global data observers. The components will subscribe to these Subject to get + // data updates. + private smpInfoUpdateSubject: Subject<SmpInfo> = new Subject<SmpInfo>(); domainObserver: Observable<SearchTableResult> userObserver: Observable<SearchTableResult> @@ -71,15 +75,6 @@ export class GlobalLookups { this.refreshDomainLookup(domainUrl); } - public refreshDomainLookupForLoggedUser() { - let domainUrl = SmpConstants.REST_PUBLIC_DOMAIN; - // for authenticated admin use internal url which returns more data! - if (this.securityService.isCurrentUserSystemAdmin()) { - domainUrl = SmpConstants.REST_INTERNAL_DOMAIN_MANAGE_DEPRECATED; - } - this.refreshDomainLookup(domainUrl); - } - public refreshDomainLookup(domainUrl: string) { let params: HttpParams = new HttpParams() .set('page', '-1') @@ -96,16 +91,18 @@ export class GlobalLookups { }); } - public refreshApplicationInfo() { this.http.get<SmpInfo>(SmpConstants.REST_PUBLIC_APPLICATION_INFO) - .subscribe((res: SmpInfo) => { + .subscribe({ + next: (res: SmpInfo): void => { this.cachedApplicationInfo = res; - }, error => { - console.log("getSmpInfo:" + error); + this.smpInfoUpdateSubject.next(res); + }, + error: (err: any): void => { + console.log("getSmpInfo:" + err); } - ); + }); } @@ -116,12 +113,13 @@ export class GlobalLookups { console.log("Refresh application configuration is authenticated " + isAuthenticated) if (isAuthenticated) { this.http.get<SmpConfig>(SmpConstants.REST_PUBLIC_APPLICATION_CONFIG) - .subscribe((res: SmpConfig) => { - this.cachedApplicationConfig = res; - }, error => { - console.log("getSmpConfig:" + error); - } - ); + .subscribe({next: (res :SmpConfig):void => { + this.cachedApplicationConfig = res; + }, + error: (err: any)=> { + console.log("getSmpConfig:" + err); + } + }); } }); } @@ -143,7 +141,6 @@ export class GlobalLookups { let sub: Subscription = this.userObserver.subscribe((users: SearchTableResult) => { this.cachedServiceGroupOwnerList = users.serviceEntities.map(serviceEntity => { return {...serviceEntity} - }); sub.unsubscribe(); }, (error: any) => { @@ -160,4 +157,8 @@ export class GlobalLookups { this.cachedApplicationConfig = null; this.cachedDomainList = []; } + + public onSmpInfoUpdateEvent(): Observable<SmpInfo> { + return this.smpInfoUpdateSubject.asObservable(); + } } diff --git a/smp-angular/src/app/security/reset-credential/reset-credential.component.ts b/smp-angular/src/app/security/reset-credential/reset-credential.component.ts index 0cc719943..0956a149b 100644 --- a/smp-angular/src/app/security/reset-credential/reset-credential.component.ts +++ b/smp-angular/src/app/security/reset-credential/reset-credential.component.ts @@ -2,8 +2,11 @@ import {ActivatedRoute, Router} from '@angular/router'; import {FormGroup, UntypedFormControl, Validators} from "@angular/forms"; import {GlobalLookups} from "../../common/global-lookups"; -import {equal} from "../../common/dialogs/password-change-dialog/password-change-dialog.component"; +import { + equal +} from "../../common/dialogs/password-change-dialog/password-change-dialog.component"; import {SecurityService} from "../security.service"; +import {SmpInfo} from "../../app-info/smp-info.model"; @Component({ templateUrl: './reset-credential.component.html', @@ -26,18 +29,38 @@ export class ResetCredentialComponent implements OnInit { ngOnInit() { this.initForm(); + this.lookups.onSmpInfoUpdateEvent().subscribe((data: SmpInfo): void => { + this.resetPasswordValidator(data); + }) this.resetToken = this.activatedRoute.snapshot.params['resetToken']; } + /** + * Reset password validator from the application info data object + * @param data - SmpInfo + */ + private resetPasswordValidator(data: SmpInfo): void { + this.resetForm.controls['new-password'].setValidators([Validators.required, Validators.pattern(data.passwordValidationRegExp)]); + } + private initForm() { - let newPasswdFormControl: UntypedFormControl = new UntypedFormControl({value: null, readonly: false}, + let newPasswdFormControl: UntypedFormControl = new UntypedFormControl({ + value: null, + readonly: false + }, [Validators.required, Validators.pattern(this.passwordValidationRegExp)]); - let confirmNewPasswdFormControl: UntypedFormControl = new UntypedFormControl({value: null, readonly: false}, + let confirmNewPasswdFormControl: UntypedFormControl = new UntypedFormControl({ + value: null, + readonly: false + }, [Validators.required, equal(newPasswdFormControl, true)]); this.resetForm = new FormGroup({ - 'resetUsername': new UntypedFormControl({value: null, readonly: true}, null), + 'resetUsername': new UntypedFormControl({ + value: null, + readonly: true + }, null), 'new-password': newPasswdFormControl, 'confirm-new-password': confirmNewPasswdFormControl }); diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java index bc7908249..d3c85c44d 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java @@ -56,6 +56,7 @@ import java.text.SimpleDateFormat; import java.time.OffsetDateTime; import java.time.temporal.ChronoUnit; import java.util.*; +import java.util.regex.Pattern; import static java.util.Locale.US; @@ -354,12 +355,16 @@ public class CredentialService { return; } DBCredential dbCredential = optCredential.get(); - if (!resetToken.equals(dbCredential.getResetToken())) { LOG.warn("User [{}] reset token does not match the active reset token! The request is ignored", username); return; } + Pattern pattern = configurationService.getPasswordPolicyRexExp(); + if (pattern != null && !pattern.matcher(newPassword).matches()) { + throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "PasswordChange", configurationService.getPasswordPolicyValidationMessage()); + } + OffsetDateTime now = OffsetDateTime.now(); dbCredential.setValue(BCrypt.hashpw(newPassword, BCrypt.gensalt())); dbCredential.setResetToken(null); -- GitLab