@@ -129,6 +129,8 @@ public class IEMRAdminUserServiceImpl implements IEMRAdminUserService {
129129 private SessionObject sessionObject ;
130130 @ Value ("${failedLoginAttempt}" )
131131 private String failedLoginAttempt ;
132+ @ Value ("${account.lock.duration.hours:24}" )
133+ private int accountLockDurationHours ;
132134 // @Autowired
133135 // private ServiceRoleScreenMappingRepository ;
134136
@@ -222,7 +224,25 @@ public void setValidator(Validator validator) {
222224
223225 private void checkUserAccountStatus (User user ) throws IEMRException {
224226 if (user .getDeleted ()) {
225- throw new IEMRException ("Your account is locked or de-activated. Please contact administrator" );
227+ if (user .getLockTimestamp () != null ) {
228+ long lockTimeMillis = user .getLockTimestamp ().getTime ();
229+ long currentTimeMillis = System .currentTimeMillis ();
230+ long lockDurationMillis = (long ) accountLockDurationHours * 60 * 60 * 1000 ;
231+
232+ if (currentTimeMillis - lockTimeMillis >= lockDurationMillis ) {
233+ user .setDeleted (false );
234+ user .setFailedAttempt (0 );
235+ user .setLockTimestamp (null );
236+ iEMRUserRepositoryCustom .save (user );
237+ logger .info ("User account auto-unlocked after {} hours lock period for user: {}" ,
238+ accountLockDurationHours , user .getUserName ());
239+ return ;
240+ } else {
241+ throw new IEMRException (generateLockoutErrorMessage (user .getLockTimestamp ()));
242+ }
243+ } else {
244+ throw new IEMRException ("Your account is locked or de-activated. Please contact administrator" );
245+ }
226246 } else if (user .getStatusID () > 2 ) {
227247 throw new IEMRException ("Your account is not active. Please contact administrator" );
228248 }
@@ -265,32 +285,27 @@ public List<User> userAuthenticate(String userName, String password) throws Exce
265285 checkUserAccountStatus (user );
266286 iEMRUserRepositoryCustom .save (user );
267287 } else if (validatePassword == 0 ) {
268- if (user .getFailedAttempt () + 1 < failedAttempt ) {
269- user .setFailedAttempt (user .getFailedAttempt () + 1 );
288+ int currentAttempts = (user .getFailedAttempt () != null ) ? user .getFailedAttempt () : 0 ;
289+ if (currentAttempts + 1 < failedAttempt ) {
290+ user .setFailedAttempt (currentAttempts + 1 );
270291 user = iEMRUserRepositoryCustom .save (user );
271292 logger .warn ("User Password Wrong" );
272293 throw new IEMRException ("Invalid username or password" );
273- } else if (user .getFailedAttempt () + 1 >= failedAttempt ) {
274- user .setFailedAttempt (user .getFailedAttempt () + 1 );
294+ } else {
295+ java .sql .Timestamp lockTime = new java .sql .Timestamp (System .currentTimeMillis ());
296+ user .setFailedAttempt (currentAttempts + 1 );
275297 user .setDeleted (true );
298+ user .setLockTimestamp (lockTime );
276299 user = iEMRUserRepositoryCustom .save (user );
277300 logger .warn ("User Account has been locked after reaching the limit of {} failed login attempts." ,
278301 ConfigProperties .getInteger ("failedLoginAttempt" ));
279-
280- throw new IEMRException (
281- "Invalid username or password. Please contact administrator." );
282- } else {
283- user .setFailedAttempt (user .getFailedAttempt () + 1 );
284- user = iEMRUserRepositoryCustom .save (user );
285- logger .warn ("Failed login attempt {} of {} for a user account." ,
286- user .getFailedAttempt (), ConfigProperties .getInteger ("failedLoginAttempt" ));
287- throw new IEMRException (
288- "Invalid username or password. Please contact administrator." );
302+ throw new IEMRException (generateLockoutErrorMessage (lockTime ));
289303 }
290304 } else {
291305 checkUserAccountStatus (user );
292- if (user .getFailedAttempt () != 0 ) {
306+ if (user .getFailedAttempt () != null && user . getFailedAttempt () != 0 ) {
293307 user .setFailedAttempt (0 );
308+ user .setLockTimestamp (null );
294309 user = iEMRUserRepositoryCustom .save (user );
295310 }
296311 }
@@ -313,6 +328,37 @@ private void resetUserLoginFailedAttempt(User user) throws IEMRException {
313328
314329 }
315330
331+ private String generateLockoutErrorMessage (java .sql .Timestamp lockTimestamp ) {
332+ if (lockTimestamp == null ) {
333+ return "Your account has been locked. Please contact the administrator." ;
334+ }
335+
336+ long lockTimeMillis = lockTimestamp .getTime ();
337+ long currentTimeMillis = System .currentTimeMillis ();
338+ long lockDurationMillis = (long ) accountLockDurationHours * 60 * 60 * 1000 ;
339+ long unlockTimeMillis = lockTimeMillis + lockDurationMillis ;
340+ long remainingMillis = unlockTimeMillis - currentTimeMillis ;
341+
342+ if (remainingMillis <= 0 ) {
343+ return "Your account lock has expired. Please try logging in again." ;
344+ }
345+
346+ long remainingHours = remainingMillis / (60 * 60 * 1000 );
347+ long remainingMinutes = (remainingMillis % (60 * 60 * 1000 )) / (60 * 1000 );
348+
349+ String timeMessage ;
350+ if (remainingHours > 0 && remainingMinutes > 0 ) {
351+ timeMessage = String .format ("%d hours %d minutes" , remainingHours , remainingMinutes );
352+ } else if (remainingHours > 0 ) {
353+ timeMessage = String .format ("%d hours" , remainingHours );
354+ } else {
355+ timeMessage = String .format ("%d minutes" , remainingMinutes );
356+ }
357+
358+ return String .format ("Your account has been locked. You can try again in %s, or contact the administrator." , timeMessage );
359+
360+ }
361+
316362 /**
317363 * Super Admin login
318364 */
@@ -351,32 +397,27 @@ public User superUserAuthenticate(String userName, String password) throws Excep
351397 iEMRUserRepositoryCustom .save (user );
352398
353399 } else if (validatePassword == 0 ) {
354- if (user .getFailedAttempt () + 1 < failedAttempt ) {
355- user .setFailedAttempt (user .getFailedAttempt () + 1 );
400+ int currentAttempts = (user .getFailedAttempt () != null ) ? user .getFailedAttempt () : 0 ;
401+ if (currentAttempts + 1 < failedAttempt ) {
402+ user .setFailedAttempt (currentAttempts + 1 );
356403 user = iEMRUserRepositoryCustom .save (user );
357404 logger .warn ("User Password Wrong" );
358405 throw new IEMRException ("Invalid username or password" );
359- } else if (user .getFailedAttempt () + 1 >= failedAttempt ) {
360- user .setFailedAttempt (user .getFailedAttempt () + 1 );
406+ } else {
407+ java .sql .Timestamp lockTime = new java .sql .Timestamp (System .currentTimeMillis ());
408+ user .setFailedAttempt (currentAttempts + 1 );
361409 user .setDeleted (true );
410+ user .setLockTimestamp (lockTime );
362411 user = iEMRUserRepositoryCustom .save (user );
363412 logger .warn ("User Account has been locked after reaching the limit of {} failed login attempts." ,
364413 ConfigProperties .getInteger ("failedLoginAttempt" ));
365-
366- throw new IEMRException (
367- "Invalid username or password. Please contact administrator." );
368- } else {
369- user .setFailedAttempt (user .getFailedAttempt () + 1 );
370- user = iEMRUserRepositoryCustom .save (user );
371- logger .warn ("Failed login attempt {} of {} for a user account." ,
372- user .getFailedAttempt (), ConfigProperties .getInteger ("failedLoginAttempt" ));
373- throw new IEMRException (
374- "Invalid username or password. Please contact administrator." );
414+ throw new IEMRException (generateLockoutErrorMessage (lockTime ));
375415 }
376416 } else {
377417 checkUserAccountStatus (user );
378- if (user .getFailedAttempt () != 0 ) {
418+ if (user .getFailedAttempt () != null && user . getFailedAttempt () != 0 ) {
379419 user .setFailedAttempt (0 );
420+ user .setLockTimestamp (null );
380421 user = iEMRUserRepositoryCustom .save (user );
381422 }
382423 }
@@ -1205,12 +1246,12 @@ public User getUserById(Long userId) throws IEMRException {
12051246 try {
12061247 // Fetch user from custom repository by userId
12071248 User user = iEMRUserRepositoryCustom .findByUserID (userId );
1208-
1249+
12091250 // Check if user is found
12101251 if (user == null ) {
12111252 throw new IEMRException ("User not found with ID: " + userId );
12121253 }
1213-
1254+
12141255 return user ;
12151256 } catch (Exception e ) {
12161257 // Log and throw custom exception in case of errors
@@ -1221,7 +1262,90 @@ public User getUserById(Long userId) throws IEMRException {
12211262
12221263 @ Override
12231264 public List <User > getUserIdbyUserName (String userName ) {
1224-
12251265 return iEMRUserRepositoryCustom .findByUserName (userName );
12261266 }
1267+
1268+ @ Override
1269+ public boolean unlockUserAccount (Long userId ) throws IEMRException {
1270+ try {
1271+ User user = iEMRUserRepositoryCustom .findById (userId ).orElse (null );
1272+
1273+ if (user == null ) {
1274+ throw new IEMRException ("User not found with ID: " + userId );
1275+ }
1276+
1277+ if (user .getDeleted () != null && user .getDeleted () && user .getLockTimestamp () != null ) {
1278+ user .setDeleted (false );
1279+ user .setFailedAttempt (0 );
1280+ user .setLockTimestamp (null );
1281+ iEMRUserRepositoryCustom .save (user );
1282+ logger .info ("Admin manually unlocked user account for userID: {}" , userId );
1283+ return true ;
1284+ } else if (user .getDeleted () != null && user .getDeleted () && user .getLockTimestamp () == null ) {
1285+ throw new IEMRException ("User account is deactivated by administrator. Use user management to reactivate." );
1286+ } else {
1287+ logger .info ("User account is not locked for userID: {}" , userId );
1288+ return false ;
1289+ }
1290+ } catch (IEMRException e ) {
1291+ throw e ;
1292+ } catch (Exception e ) {
1293+ logger .error ("Error unlocking user account with ID: " + userId , e );
1294+ throw new IEMRException ("Error unlocking user account: " + e .getMessage (), e );
1295+ }
1296+ }
1297+
1298+ @ Override
1299+ public String getUserLockStatusJson (Long userId ) throws IEMRException {
1300+ try {
1301+ User user = iEMRUserRepositoryCustom .findById (userId ).orElse (null );
1302+ if (user == null ) {
1303+ throw new IEMRException ("User not found with ID: " + userId );
1304+ }
1305+
1306+ org .json .JSONObject status = new org .json .JSONObject ();
1307+ status .put ("userId" , user .getUserID ());
1308+ status .put ("userName" , user .getUserName ());
1309+ status .put ("failedAttempts" , user .getFailedAttempt () != null ? user .getFailedAttempt () : 0 );
1310+ status .put ("statusID" , user .getStatusID ());
1311+
1312+ boolean isDeleted = user .getDeleted () != null && user .getDeleted ();
1313+ boolean isLockedDueToFailedAttempts = isDeleted && user .getLockTimestamp () != null ;
1314+
1315+ status .put ("isLocked" , isDeleted );
1316+ status .put ("isLockedDueToFailedAttempts" , isLockedDueToFailedAttempts );
1317+
1318+ if (isLockedDueToFailedAttempts ) {
1319+ long lockDurationMillis = (long ) accountLockDurationHours * 60 * 60 * 1000 ;
1320+ long remainingMillis = (user .getLockTimestamp ().getTime () + lockDurationMillis ) - System .currentTimeMillis ();
1321+ boolean lockExpired = remainingMillis <= 0 ;
1322+
1323+ status .put ("lockExpired" , lockExpired );
1324+ status .put ("lockTimestamp" , user .getLockTimestamp ().toString ());
1325+ status .put ("remainingTime" , lockExpired ? "Lock expired - will unlock on next login" : formatRemainingTime (remainingMillis ));
1326+ if (!lockExpired ) {
1327+ status .put ("unlockTime" , new java .sql .Timestamp (user .getLockTimestamp ().getTime () + lockDurationMillis ).toString ());
1328+ }
1329+ } else {
1330+ status .put ("lockExpired" , false );
1331+ status .put ("lockTimestamp" , org .json .JSONObject .NULL );
1332+ status .put ("remainingTime" , org .json .JSONObject .NULL );
1333+ }
1334+
1335+ return status .toString ();
1336+ } catch (IEMRException e ) {
1337+ throw e ;
1338+ } catch (Exception e ) {
1339+ logger .error ("Error fetching user lock status with ID: " + userId , e );
1340+ throw new IEMRException ("Error fetching user lock status: " + e .getMessage (), e );
1341+ }
1342+ }
1343+
1344+ private String formatRemainingTime (long remainingMillis ) {
1345+ long hours = remainingMillis / (60 * 60 * 1000 );
1346+ long minutes = (remainingMillis % (60 * 60 * 1000 )) / (60 * 1000 );
1347+ if (hours > 0 && minutes > 0 ) return String .format ("%d hours %d minutes" , hours , minutes );
1348+ if (hours > 0 ) return String .format ("%d hours" , hours );
1349+ return String .format ("%d minutes" , minutes );
1350+ }
12271351}
0 commit comments