From 01727094e07ecb6afa1ae740990a9b2ed0aa42d7 Mon Sep 17 00:00:00 2001 From: Varun Deep Saini Date: Wed, 7 Jan 2026 22:28:20 +0530 Subject: [PATCH] AMM-118: Add time-based account lockout with auto-unlock Signed-off-by: Varun Deep Saini --- .../employee-master-new.component.html | 18 ++++ .../employee-master-new.component.ts | 94 +++++++++++++++++++ .../employee-master-new-services.service.ts | 12 +++ src/app/user-login/login/login.component.ts | 4 + src/environments/environment.local.ts | 5 + src/environments/environment.prod.ts | 5 + src/environments/environment.test.ts | 5 + 7 files changed, 143 insertions(+) diff --git a/src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.html b/src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.html index 96c45fd8..e6e5f519 100644 --- a/src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.html +++ b/src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.html @@ -88,18 +88,36 @@

Employee Master

color="accent" class="mat_green" (click)="activateDeactivate(element.userID, false)" + style="margin-right: 5px;" > Activate + + diff --git a/src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts b/src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts index 6623e28b..ad2f20de 100644 --- a/src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts +++ b/src/app/app-provider-admin/provider-admin/activities/employee-master-new/employee-master-new.component.ts @@ -1534,6 +1534,100 @@ export class EmployeeMasterNewComponent implements OnInit { }, ); } + + unlockUserAccount(userID: number, userName: string) { + this.dialogService + .confirm( + 'Confirm Unlock', + `Are you sure you want to unlock the account for user "${userName}"? This will reset their failed login attempts and allow them to log in again.`, + ) + .subscribe( + (res) => { + if (res) { + this.employeeMasterNewService.unlockUserAccount(userID).subscribe( + (response: any) => { + if (response && response.statusCode === 200) { + this.dialogService.alert( + 'User account unlocked successfully. The user can now log in.', + 'success', + ); + this.getAllUserDetails(); + this.searchTerm = null; + } else { + this.dialogService.alert( + response?.errorMessage || 'Failed to unlock user account', + 'error', + ); + } + }, + (err) => { + console.log('error', err); + this.dialogService.alert( + 'Error unlocking user account. Please try again.', + 'error', + ); + }, + ); + } + }, + (err) => { + console.log(err); + }, + ); + } + + checkUserLockStatus(userID: number) { + this.employeeMasterNewService.getUserLockStatus(userID).subscribe( + (response: any) => { + if (response && response.statusCode === 200) { + let data; + try { + data = JSON.parse(response.data); + } catch (e) { + console.error('Failed to parse lock status data:', e); + this.dialogService.alert( + 'Invalid lock status data received. Please try again.', + 'error', + ); + return; + } + let message = ''; + if (data.isLockedDueToFailedAttempts) { + message = `Account is LOCKED due to failed login attempts.\n\n` + + `Locked at: ${data.lockTimestamp}\n` + + `Failed attempts: ${data.failedAttempts}\n` + + `Time remaining: ${data.remainingTime}\n` + + (data.unlockTime ? `Auto-unlock at: ${data.unlockTime}\n` : '') + + `\nYou can unlock this account manually using the Unlock button.`; + } else if (data.lockExpired) { + message = `Account lock has EXPIRED.\n\n` + + `Original lock time: ${data.lockTimestamp}\n` + + `The account will automatically unlock when the user tries to log in.`; + } else if (data.isLocked) { + message = `Account is DEACTIVATED by administrator.\n\n` + + `Use the Activate button to reactivate this account.`; + } else { + message = `Account is ACTIVE.\n\n` + + `Failed login attempts: ${data.failedAttempts}`; + } + this.dialogService.alert(message, 'info'); + } else { + this.dialogService.alert( + response?.errorMessage || 'Failed to get lock status', + 'error', + ); + } + }, + (err) => { + console.log('error', err); + this.dialogService.alert( + 'Error getting lock status. Please try again.', + 'error', + ); + }, + ); + } + filterComponentList(searchTerm?: string) { if (!searchTerm) { this.filteredsearchResult.data = this.searchResult; diff --git a/src/app/app-provider-admin/provider-admin/activities/services/employee-master-new-services.service.ts b/src/app/app-provider-admin/provider-admin/activities/services/employee-master-new-services.service.ts index 618c7fb8..d8a2adcf 100644 --- a/src/app/app-provider-admin/provider-admin/activities/services/employee-master-new-services.service.ts +++ b/src/app/app-provider-admin/provider-admin/activities/services/employee-master-new-services.service.ts @@ -188,4 +188,16 @@ export class EmployeeMasterNewServices { // .map(this.extractData) // .catch(this.handleError) } + + getUserLockStatus(userId: number): Observable { + return this.http.post(environment.getUserLockStatusUrl, { + userId: userId, + }); + } + + unlockUserAccount(userId: number): Observable { + return this.http.post(environment.unlockUserAccountUrl, { + userId: userId, + }); + } } diff --git a/src/app/user-login/login/login.component.ts b/src/app/user-login/login/login.component.ts index d37ae886..acb4cfe9 100644 --- a/src/app/user-login/login/login.component.ts +++ b/src/app/user-login/login/login.component.ts @@ -212,6 +212,8 @@ export class loginContentClassComponent implements OnInit, OnDestroy { this.loginservice.dologoutUsrFromPreSession(true); } }); + } else { + this.alertMessage.alert(response.errorMessage, 'error'); } } }, @@ -282,6 +284,8 @@ export class loginContentClassComponent implements OnInit, OnDestroy { this.loginservice.dologoutUsrFromPreSession(true); } }); + } else { + this.alertMessage.alert(response.errorMessage, 'error'); } } }, diff --git a/src/environments/environment.local.ts b/src/environments/environment.local.ts index 0bd86903..37e5431c 100644 --- a/src/environments/environment.local.ts +++ b/src/environments/environment.local.ts @@ -227,6 +227,11 @@ export const environment = { editUserDetailsUrl: `${adminBaseUrl}editUserDetails`, userActivationDeactivationUrl: `${adminBaseUrl}deletedUserDetails`, checkEmpIdAvailabilityUrl: `${adminBaseUrl}m/FindEmployeeDetails`, + + // User Account Lock Management APIs (Common-API) + getUserLockStatusUrl: `${commonBaseURL}user/getUserLockStatus`, + unlockUserAccountUrl: `${commonBaseURL}user/unlockUserAccount`, + getStates_url: `${adminBaseUrl}m/role/state`, getDistricts_url: `${adminBaseUrl}m/location/findDistrict`, getServiceLines_url: `${adminBaseUrl}m/role/service`, diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index e1090e6e..7e301c44 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -226,6 +226,11 @@ export const environment = { editUserDetailsUrl: `${adminBaseUrl}editUserDetails`, userActivationDeactivationUrl: `${adminBaseUrl}deletedUserDetails`, checkEmpIdAvailabilityUrl: `${adminBaseUrl}m/FindEmployeeDetails`, + + // User Account Lock Management APIs (Common-API) + getUserLockStatusUrl: `${commonBaseURL}user/getUserLockStatus`, + unlockUserAccountUrl: `${commonBaseURL}user/unlockUserAccount`, + getStates_url: `${adminBaseUrl}m/role/state`, getDistricts_url: `${adminBaseUrl}m/location/findDistrict`, getServiceLines_url: `${adminBaseUrl}m/role/service`, diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 44d123c6..e7df9af2 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -222,6 +222,11 @@ export const environment = { editUserDetailsUrl: `${adminBaseUrl}editUserDetails`, userActivationDeactivationUrl: `${adminBaseUrl}deletedUserDetails`, checkEmpIdAvailabilityUrl: `${adminBaseUrl}m/FindEmployeeDetails`, + + // User Account Lock Management APIs (Common-API) + getUserLockStatusUrl: `${commonBaseURL}user/getUserLockStatus`, + unlockUserAccountUrl: `${commonBaseURL}user/unlockUserAccount`, + getStates_url: `${adminBaseUrl}m/role/state`, getDistricts_url: `${adminBaseUrl}m/location/findDistrict`, getServiceLines_url: `${adminBaseUrl}m/role/service`,