-
Notifications
You must be signed in to change notification settings - Fork 52
implement time-bound account lock with auto-unlock after 24h #394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -222,14 +222,34 @@ | |
|
|
||
| private void checkUserAccountStatus(User user) throws IEMRException { | ||
| if (user.getDeleted()) { | ||
| throw new IEMRException("Your account is locked or de-activated. Please contact administrator"); | ||
| // check if account was locked due to failed attempts and lock has expired | ||
| if (user.getLockTimestamp() != null) { | ||
| java.time.LocalDate lockDate = user.getLockTimestamp().toLocalDateTime().toLocalDate(); | ||
| java.time.LocalDate today = java.time.LocalDate.now(); | ||
|
|
||
| if (today.isAfter(lockDate)) { | ||
| // auto-unlock: it's a new day after the lock date | ||
| user.setDeleted(false); | ||
| user.setFailedAttempt(0); | ||
| user.setLockTimestamp(null); | ||
| iEMRUserRepositoryCustom.save(user); | ||
| logger.info("Account auto-unlocked for user {} (locked on {}, unlocked on {})", | ||
| user.getUserName(), lockDate, today); | ||
| return; | ||
| } else { | ||
| throw new IEMRException( | ||
| "Your account has been locked. You can try tomorrow or connect to the administrator."); | ||
| } | ||
| } | ||
| throw new IEMRException( | ||
| "Your account is locked or de-activated. Please contact administrator"); | ||
| } else if (user.getStatusID() > 2) { | ||
| throw new IEMRException("Your account is not active. Please contact administrator"); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public List<User> userAuthenticate(String userName, String password) throws Exception { | ||
|
Check failure on line 252 in src/main/java/com/iemr/common/service/users/IEMRAdminUserServiceImpl.java
|
||
| List<User> users = iEMRUserRepositoryCustom.findByUserNameNew(userName); | ||
| if (users.size() != 1) { | ||
| throw new IEMRException("Invalid username or password"); | ||
|
|
@@ -265,6 +285,10 @@ | |
| checkUserAccountStatus(user); | ||
| iEMRUserRepositoryCustom.save(user); | ||
| } else if (validatePassword == 0) { | ||
| // if already locked/deleted, don't overwrite lock timestamp | ||
| if (Boolean.TRUE.equals(user.getDeleted())) { | ||
| checkUserAccountStatus(user); | ||
| } | ||
| if (user.getFailedAttempt() + 1 < failedAttempt) { | ||
| user.setFailedAttempt(user.getFailedAttempt() + 1); | ||
| user = iEMRUserRepositoryCustom.save(user); | ||
|
|
@@ -273,24 +297,26 @@ | |
| } else if (user.getFailedAttempt() + 1 >= failedAttempt) { | ||
| user.setFailedAttempt(user.getFailedAttempt() + 1); | ||
| user.setDeleted(true); | ||
| user.setLockTimestamp(new java.sql.Timestamp(System.currentTimeMillis())); | ||
| user = iEMRUserRepositoryCustom.save(user); | ||
| logger.warn("User Account has been locked after reaching the limit of {} failed login attempts.", | ||
| ConfigProperties.getInteger("failedLoginAttempt")); | ||
|
|
||
| throw new IEMRException( | ||
| "Invalid username or password. Please contact administrator."); | ||
| "Your account has been locked. You can try tomorrow or connect to the administrator."); | ||
| } else { | ||
| user.setFailedAttempt(user.getFailedAttempt() + 1); | ||
| user = iEMRUserRepositoryCustom.save(user); | ||
| logger.warn("Failed login attempt {} of {} for a user account.", | ||
| user.getFailedAttempt(), ConfigProperties.getInteger("failedLoginAttempt")); | ||
| throw new IEMRException( | ||
| "Invalid username or password. Please contact administrator."); | ||
| "Invalid username or password."); | ||
| } | ||
| } else { | ||
| checkUserAccountStatus(user); | ||
| if (user.getFailedAttempt() != 0) { | ||
| user.setFailedAttempt(0); | ||
| user.setLockTimestamp(null); | ||
| user = iEMRUserRepositoryCustom.save(user); | ||
|
Comment on lines
+319
to
320
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== SecurePassword.validatePassword contract =="
fd 'SecurePassword.java' src --exec sed -n '1,260p' {}
echo
echo "== validatePassword call sites in IEMRAdminUserServiceImpl =="
rg -n -C3 '\bvalidatePassword\s*\(' --type=java src/main/java/com/iemr/common/service/users/IEMRAdminUserServiceImpl.javaRepository: PSMRI/Common-API Length of output: 6138 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "== Full code around first reset (lines 250-330) =="
sed -n '250,330p' src/main/java/com/iemr/common/service/users/IEMRAdminUserServiceImpl.java
echo
echo "== Full code around second reset (lines 340-420) =="
sed -n '340,420p' src/main/java/com/iemr/common/service/users/IEMRAdminUserServiceImpl.javaRepository: PSMRI/Common-API Length of output: 6292 Reset the failure state in every successful-auth branch.
Move the reset into all three success branches ( This applies to both methods around the locations indicated. 🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
|
|
@@ -317,8 +343,8 @@ | |
| * Super Admin login | ||
| */ | ||
| @Override | ||
| public User superUserAuthenticate(String userName, String password) throws Exception { | ||
|
Check failure on line 346 in src/main/java/com/iemr/common/service/users/IEMRAdminUserServiceImpl.java
|
||
| List<User> users = iEMRUserRepositoryCustom.findByUserName(userName); | ||
| List<User> users = iEMRUserRepositoryCustom.findByUserNameNew(userName); | ||
|
|
||
| if (users.size() != 1) { | ||
| throw new IEMRException("Invalid username or password"); | ||
|
|
@@ -351,6 +377,10 @@ | |
| iEMRUserRepositoryCustom.save(user); | ||
|
|
||
| } else if (validatePassword == 0) { | ||
| // if already locked/deleted, don't overwrite lock timestamp | ||
| if (Boolean.TRUE.equals(user.getDeleted())) { | ||
| checkUserAccountStatus(user); | ||
| } | ||
| if (user.getFailedAttempt() + 1 < failedAttempt) { | ||
| user.setFailedAttempt(user.getFailedAttempt() + 1); | ||
| user = iEMRUserRepositoryCustom.save(user); | ||
|
|
@@ -359,24 +389,26 @@ | |
| } else if (user.getFailedAttempt() + 1 >= failedAttempt) { | ||
| user.setFailedAttempt(user.getFailedAttempt() + 1); | ||
| user.setDeleted(true); | ||
| user.setLockTimestamp(new java.sql.Timestamp(System.currentTimeMillis())); | ||
| user = iEMRUserRepositoryCustom.save(user); | ||
| logger.warn("User Account has been locked after reaching the limit of {} failed login attempts.", | ||
| ConfigProperties.getInteger("failedLoginAttempt")); | ||
|
|
||
| throw new IEMRException( | ||
| "Invalid username or password. Please contact administrator."); | ||
| "Your account has been locked. You can try tomorrow or connect to the administrator."); | ||
| } else { | ||
| user.setFailedAttempt(user.getFailedAttempt() + 1); | ||
| user = iEMRUserRepositoryCustom.save(user); | ||
| logger.warn("Failed login attempt {} of {} for a user account.", | ||
| user.getFailedAttempt(), ConfigProperties.getInteger("failedLoginAttempt")); | ||
| throw new IEMRException( | ||
| "Invalid username or password. Please contact administrator."); | ||
| "Invalid username or password."); | ||
| } | ||
| } else { | ||
| checkUserAccountStatus(user); | ||
| if (user.getFailedAttempt() != 0) { | ||
| user.setFailedAttempt(0); | ||
| user.setLockTimestamp(null); | ||
| user = iEMRUserRepositoryCustom.save(user); | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| -- Add lock_timestamp column to m_user table for time-bound account locking | ||
| -- When a user gets locked after 5 failed login attempts, this stores when | ||
| -- the lock happened. After 24 hours, the account auto-unlocks on next login. | ||
|
|
||
| ALTER TABLE m_user ADD COLUMN lock_timestamp DATETIME NULL DEFAULT NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't auto-unlock past the inactive-status guard.
If an account is deactivated (
statusID > 2) while it still has a historicallockTimestamp, this branch clearsdeletedandreturns before Line 246 runs. The rest of the login flow then proceeds with an inactive account. Let the method continue into the status check after clearing the temporary lock, instead of returning here.Suggested fix
if (user.getDeleted()) { if (user.getLockTimestamp() != null) { java.time.LocalDate lockDate = user.getLockTimestamp().toLocalDateTime().toLocalDate(); java.time.LocalDate today = java.time.LocalDate.now(); if (today.isAfter(lockDate)) { user.setDeleted(false); user.setFailedAttempt(0); user.setLockTimestamp(null); iEMRUserRepositoryCustom.save(user); logger.info("Account auto-unlocked for user {} (locked on {}, unlocked on {})", user.getUserName(), lockDate, today); - return; } else { throw new IEMRException( "Your account has been locked. You can try tomorrow or connect to the administrator."); } } - throw new IEMRException( - "Your account is locked or de-activated. Please contact administrator"); - } else if (user.getStatusID() > 2) { + if (user.getDeleted()) { + throw new IEMRException( + "Your account is locked or de-activated. Please contact administrator"); + } + } + if (user.getStatusID() > 2) { throw new IEMRException("Your account is not active. Please contact administrator"); }🤖 Prompt for AI Agents