Skip to content

Commit 9a7c741

Browse files
authored
Add Security Check to Prevent User Enumeration (#240)
* fix: user enmeration fix for login and forgot password * fix: user enmeration fix for login and forgot password * fix: user enmeration fix for login and forgot password
1 parent 5846036 commit 9a7c741

2 files changed

Lines changed: 103 additions & 65 deletions

File tree

src/main/java/com/iemr/common/controller/users/IEMRAdminController.java

Lines changed: 58 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ public String userAuthenticate(
156156
logger.info("CAPTCHA validated successfully for user: {}", m_User.getUserName());
157157
} else {
158158
logger.warn("CAPTCHA token missing for user: {}", m_User.getUserName());
159-
response.setError(new IEMRException("CAPTCHA token is required"));
159+
response.setError(new IEMRException("CAPTCHA validation failed. Please try again."));
160160
return response.toString();
161161
}
162162
} else {
@@ -254,20 +254,24 @@ public ResponseEntity<?> refreshToken(@RequestBody Map<String, String> request)
254254

255255
try {
256256
if (jwtUtil.validateToken(refreshToken) == null) {
257-
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid token");
257+
logger.warn("Token validation failed: invalid token provided.");
258+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized.");
258259
}
259260

260261
Claims claims = jwtUtil.getAllClaimsFromToken(refreshToken);
261262

262263
// Verify token type
263264
if (!"refresh".equals(claims.get("token_type", String.class))) {
264-
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid token type");
265+
logger.warn("Token validation failed: incorrect token type in refresh request.");
266+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized.");
267+
265268
}
266269

267270
// Check revocation using JTI
268271
String jti = claims.getId();
269272
if (!redisTemplate.hasKey("refresh:" + jti)) {
270-
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Token revoked");
273+
logger.warn("Token validation failed: refresh token is revoked or not found in store.");
274+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized.");
271275
}
272276

273277
// Get user details
@@ -277,11 +281,13 @@ public ResponseEntity<?> refreshToken(@RequestBody Map<String, String> request)
277281

278282
// Validate that the user still exists and is active
279283
if (user == null) {
280-
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("User not found");
284+
logger.warn("Token validation failed: user not found for userId in token.");
285+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized.");
281286
}
282287

283288
if (user.getM_status() == null || !"Active".equalsIgnoreCase(user.getM_status().getStatus())) {
284-
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("User account is inactive");
289+
logger.warn("Token validation failed: user account is inactive or not in 'Active' status.");
290+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized.");
285291
}
286292
// Generate new tokens
287293
String newJwt = jwtUtil.generateToken(user.getUserName(), userId);
@@ -302,10 +308,14 @@ public ResponseEntity<?> refreshToken(@RequestBody Map<String, String> request)
302308

303309
return ResponseEntity.ok(tokens);
304310
} catch (ExpiredJwtException ex) {
305-
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Token expired");
311+
logger.warn("Token validation failed: token has expired.");
312+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
313+
.body("Authentication failed. Please log in again.");
306314
} catch (Exception e) {
307315
logger.error("Refresh failed: ", e);
308-
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Token refresh failed");
316+
logger.error("Token refresh failed due to unexpected server error.");
317+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
318+
.body("An unexpected error occurred. Please try again later.");
309319
}
310320
}
311321

@@ -321,18 +331,21 @@ public String logOutUserFromConcurrentSession(
321331
List<User> mUsers = iemrAdminUserServiceImpl.userExitsCheck(m_User.getUserName());
322332

323333
if (mUsers == null || mUsers.size() <= 0) {
324-
throw new IEMRException("User not found, please contact administrator");
325-
} else if (mUsers.size() > 1)
326-
throw new IEMRException("More than 1 user found, please contact administrator");
327-
else if (mUsers.size() == 1) {
334+
logger.error("User not found");
335+
throw new IEMRException("Logout request failed, please try again later");
336+
} else if (mUsers.size() > 1) {
337+
logger.error("More than 1 user found");
338+
throw new IEMRException("Logout failed. Please retry or contact administrator");
339+
} else if (mUsers.size() == 1) {
328340
String previousTokenFromRedis = sessionObject
329341
.getSessionObject((mUsers.get(0).getUserName().toString().trim().toLowerCase()));
330342
if (previousTokenFromRedis != null) {
331343
deleteSessionObjectByGettingSessionDetails(previousTokenFromRedis);
332344
sessionObject.deleteSessionObject(previousTokenFromRedis);
333345
response.setResponse("User successfully logged out");
334346
} else
335-
throw new IEMRException("Unable to fetch session from redis");
347+
logger.error("Unable to fetch session from redis");
348+
throw new IEMRException("Session error. Please try again later");
336349
}
337350
} else {
338351
throw new IEMRException("Invalid request object");
@@ -404,7 +417,7 @@ private void createUserMapping(User mUser, JSONObject resMap, JSONObject service
404417
previlegeObj.getJSONObject(serv).put("agentPassword", m_UserServiceRoleMapping.getAgentPassword());
405418
}
406419
JSONArray roles = previlegeObj.getJSONObject(serv).getJSONArray("roles");
407-
// roles.put(new JSONObject(m_UserServiceRoleMapping.getM_Role().toString()));
420+
// roles.put(new JSONObject(m_UserServiceRoleMapping.getM_Role().toString()));
408421
JSONObject roleObject = new JSONObject(m_UserServiceRoleMapping.getM_Role().toString());
409422
roleObject.put("teleConsultation", m_UserServiceRoleMapping.getTeleConsultation());
410423
roles.put(roleObject);
@@ -506,7 +519,7 @@ public String superUserAuthenticate(
506519
response.setResponse(responseObj.toString());
507520
} catch (Exception e) {
508521
logger.error("userAuthenticate failed with error " + e.getMessage(), e);
509-
response.setError(e);
522+
response.setError(5000, "Authentication failed. Please try again later."); // Generic fallback
510523
}
511524
logger.info("userAuthenticate response " + response.toString());
512525
return response.toString();
@@ -563,7 +576,8 @@ public String getLoginResponse(HttpServletRequest request) {
563576
}
564577

565578
if (jwtToken == null) {
566-
throw new IEMRException("No authentication token found in header or cookie");
579+
logger.warn("Authentication failed: no token found in header or cookies.");
580+
throw new IEMRException("Authentication failed. Please log in again.");
567581
}
568582

569583
// Extract user ID from the JWT token
@@ -572,7 +586,9 @@ public String getLoginResponse(HttpServletRequest request) {
572586
// Get user details and prepare response
573587
User user = iemrAdminUserServiceImpl.getUserById(Long.parseLong(userId));
574588
if (user == null) {
575-
throw new IEMRException("User not found");
589+
logger.warn("User lookup failed for provided userId.");
590+
throw new IEMRException("Authentication failed. Please try again.");
591+
576592
}
577593

578594
String remoteAddress = request.getHeader("X-FORWARDED-FOR");
@@ -603,10 +619,13 @@ public String forgetPassword(
603619
List<User> mUsers = iemrAdminUserServiceImpl.userExitsCheck(m_User.getUserName());
604620

605621
if (mUsers == null || mUsers.size() <= 0) {
606-
throw new IEMRException("user not found, please contact administrator");
607-
} else if (mUsers.size() > 1)
608-
throw new IEMRException("more than 1 user found, please contact administrator");
609-
else if (mUsers.size() == 1) {
622+
logger.error("User not found");
623+
throw new IEMRException("Request failed, please try again later");
624+
} else if (mUsers.size() > 1) {
625+
logger.error("More than 1 user found");
626+
throw new IEMRException("Request failed. Please retry again");
627+
628+
} else if (mUsers.size() == 1) {
610629
List<Map<String, String>> quesAnsList = new ArrayList<>();
611630
Map<String, String> quesAnsMap;
612631
Map<Object, Object> resMap = new HashMap<>();
@@ -626,7 +645,7 @@ else if (mUsers.size() == 1) {
626645
}
627646
} catch (Exception e) {
628647
logger.error("forgetPassword failed with error " + e.getMessage(), e);
629-
response.setError(e);
648+
response.setError(5000, "ForgetPassword failed.");
630649
}
631650
logger.info("forgetPassword response " + response.toString());
632651
return response.toString();
@@ -642,8 +661,11 @@ public String setPassword(
642661
int noOfRowModified = 0;
643662
List<User> mUsers = iemrAdminUserServiceImpl.userExitsCheck(m_user.getUserName());
644663
if (mUsers.size() != 1) {
645-
throw new IEMRException(
646-
"Set forgot password failed as the user does not exist or is not active or multiple user found.Please contact with administrator");
664+
logger.warn(
665+
"Password reset failed for username '{}'. Reason: user not found, inactive, or multiple matches.",
666+
m_user.getUserName());
667+
668+
throw new IEMRException("Unable to process your request. Please try again or contact support.");
647669
}
648670
User mUser = mUsers.get(0);
649671
String setStatus;
@@ -660,7 +682,7 @@ public String setPassword(
660682
} catch (Exception e) {
661683
logger.error("setForgetPassword failed with error " + e.getMessage(), e);
662684
if (e.getMessage().equals(
663-
"Set forgot password failed as the user does not exist or is not active or multiple user found.Please contact with administrator"))
685+
"Unable to process your request. Please try again or contact support."))
664686
response.setError(e);
665687
else
666688
response.setError(5000, e.getMessage());
@@ -681,7 +703,9 @@ public String changePassword(
681703
List<User> mUsers = iemrAdminUserServiceImpl.userExitsCheck(changePassword.getUserName());
682704
String changeReqResult;
683705
if (mUsers.size() != 1) {
684-
throw new IEMRException("Change password failed with error as user is not available");
706+
logger.warn("Change password attempt failed. User not found or not available.");
707+
708+
throw new IEMRException("Unable to change password. Please try again later");
685709
}
686710
try {
687711
int validatePassword;
@@ -710,7 +734,7 @@ public String changePassword(
710734
response.setResponse(changeReqResult);
711735
} catch (Exception e) {
712736
logger.error("changePassword failed with error " + e.getMessage(), e);
713-
response.setError(e);
737+
response.setError(5000, "Password change failed. Please try again later.");
714738
}
715739
logger.info("changePassword response " + response.toString());
716740
return response.toString();
@@ -728,7 +752,7 @@ public String saveUserSecurityQuesAns(
728752
response.setResponse(responseData);
729753
} catch (Exception e) {
730754
logger.error("saveUserSecurityQuesAns failed with error " + e.getMessage(), e);
731-
response.setError(e);
755+
response.setError(5000, "Failed to save security questions. Please try again later.");
732756
}
733757
logger.info("saveUserSecurityQuesAns response " + response.toString());
734758
return response.toString();
@@ -748,7 +772,7 @@ public String getSecurityts() {
748772
response.setResponse(test.toString());
749773
} catch (Exception e) {
750774
logger.error("getsecurityquetions failed with error " + e.getMessage(), e);
751-
response.setError(e);
775+
response.setError(5000, "Unable to fetch security questions");
752776
}
753777
logger.info("getsecurityquetions response " + response.toString());
754778
return response.toString();
@@ -1034,7 +1058,7 @@ public String userAuthenticateByEncryption(
10341058
response.setResponse(responseObj.toString());
10351059
} catch (Exception e) {
10361060
logger.error("userAuthenticateByEncryption failed with error " + e.getMessage(), e);
1037-
response.setError(e);
1061+
response.setError(5000, "Request failed. Please try again.");
10381062
}
10391063
logger.info("userAuthenticateByEncryption response " + response.toString());
10401064
return response.toString();
@@ -1052,7 +1076,7 @@ public String getrolewrapuptime(@PathVariable("roleID") Integer roleID) {
10521076
}
10531077
response.setResponse(test.toString());
10541078
} catch (Exception e) {
1055-
response.setError(e);
1079+
response.setError(5000, "Request failed. Please try again.");
10561080
}
10571081
return response.toString();
10581082
}
@@ -1079,8 +1103,8 @@ public String validateSecurityQuestionAndAnswer(
10791103
} else
10801104
throw new IEMRException("Invalid Request");
10811105
} catch (Exception e) {
1082-
response.setError(5000, e.getMessage());
1083-
logger.error(e.toString());
1106+
logger.error("validateSecurityQuestionAndAnswer failed: {}", e.toString());
1107+
response.setError(5000, "Request failed. Please try again.");
10841108
}
10851109
logger.info("validateSecurityQuestionAndAnswer API response" + response.toString());
10861110
return response.toString();
@@ -1136,7 +1160,7 @@ public String userAuthenticateBhavya(
11361160
response.setResponse(responseObj.toString());
11371161
} catch (Exception e) {
11381162
logger.error("userAuthenticate failed with error " + e.getMessage(), e);
1139-
response.setError(e);
1163+
response.setError(5000, "Authentication failed. Please try again.");
11401164
}
11411165
logger.info("userAuthenticate response " + response.toString());
11421166
return response.toString();

0 commit comments

Comments
 (0)