Skip to content

Commit e57bce4

Browse files
author
Varun Deep Saini
committed
AMM-118: Add time-based account lockout with auto-unlock
Signed-off-by: Varun Deep Saini <varun.23bcs10048@ms.sst.scaler.com>
1 parent a1a0027 commit e57bce4

6 files changed

Lines changed: 346 additions & 126 deletions

File tree

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

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
@RequestMapping("/user")
7878
@RestController
7979
public class IEMRAdminController {
80+
private static final String USER_ID_FIELD = "userId";
8081
private final Logger logger = LoggerFactory.getLogger(this.getClass().getName());
8182
private InputMapper inputMapper = new InputMapper();
8283

@@ -583,6 +584,13 @@ public String getLoginResponse(HttpServletRequest request) {
583584
throw new IEMRException("Authentication failed. Please log in again.");
584585
}
585586

587+
// Validate the token first
588+
Claims claims = jwtUtil.validateToken(jwtToken);
589+
if (claims == null) {
590+
logger.warn("Authentication failed: invalid or expired token.");
591+
throw new IEMRException("Authentication failed. Please log in again.");
592+
}
593+
586594
// Extract user ID from the JWT token
587595
String userId = jwtUtil.getUserIdFromToken(jwtToken);
588596

@@ -1230,4 +1238,87 @@ public ResponseEntity<?> getUserDetails(@PathVariable("userName") String userNam
12301238
}
12311239

12321240
}
1241+
1242+
@Operation(summary = "Unlock user account locked due to failed login attempts")
1243+
@PostMapping(value = "/unlockUserAccount", produces = MediaType.APPLICATION_JSON, headers = "Authorization")
1244+
public String unlockUserAccount(@RequestBody String request, HttpServletRequest httpRequest) {
1245+
OutputResponse response = new OutputResponse();
1246+
try {
1247+
Long authenticatedUserId = getAuthenticatedUserId(httpRequest);
1248+
validateAdminPrivileges(authenticatedUserId);
1249+
Long userId = parseUserIdFromRequest(request);
1250+
boolean unlocked = iemrAdminUserServiceImpl.unlockUserAccount(userId);
1251+
response.setResponse(unlocked ? "User account successfully unlocked" : "User account was not locked");
1252+
} catch (Exception e) {
1253+
logger.error("Error unlocking user account: " + e.getMessage(), e);
1254+
response.setError(e);
1255+
}
1256+
return response.toString();
1257+
}
1258+
1259+
@Operation(summary = "Get user account lock status")
1260+
@PostMapping(value = "/getUserLockStatus", produces = MediaType.APPLICATION_JSON, headers = "Authorization")
1261+
public String getUserLockStatus(@RequestBody String request, HttpServletRequest httpRequest) {
1262+
OutputResponse response = new OutputResponse();
1263+
try {
1264+
Long authenticatedUserId = getAuthenticatedUserId(httpRequest);
1265+
validateAdminPrivileges(authenticatedUserId);
1266+
Long userId = parseUserIdFromRequest(request);
1267+
String lockStatusJson = iemrAdminUserServiceImpl.getUserLockStatusJson(userId);
1268+
response.setResponse(lockStatusJson);
1269+
} catch (Exception e) {
1270+
logger.error("Error getting user lock status: " + e.getMessage(), e);
1271+
response.setError(e);
1272+
}
1273+
return response.toString();
1274+
}
1275+
1276+
private Long parseUserIdFromRequest(String request) throws IEMRException {
1277+
try {
1278+
JsonObject requestObj = JsonParser.parseString(request).getAsJsonObject();
1279+
if (!requestObj.has(USER_ID_FIELD) || requestObj.get(USER_ID_FIELD).isJsonNull()) {
1280+
throw new IEMRException(USER_ID_FIELD + " is required");
1281+
}
1282+
JsonElement userIdElement = requestObj.get(USER_ID_FIELD);
1283+
if (!userIdElement.isJsonPrimitive() || !userIdElement.getAsJsonPrimitive().isNumber()) {
1284+
throw new IEMRException(USER_ID_FIELD + " must be a number");
1285+
}
1286+
return userIdElement.getAsLong();
1287+
} catch (IEMRException e) {
1288+
throw e;
1289+
} catch (Exception e) {
1290+
logger.error("Failed to parse {} from request: {}", USER_ID_FIELD, e.getMessage());
1291+
throw new IEMRException("Invalid request body");
1292+
}
1293+
}
1294+
1295+
private Long getAuthenticatedUserId(HttpServletRequest httpRequest) throws IEMRException {
1296+
String authorization = httpRequest.getHeader("Authorization");
1297+
if (authorization != null && authorization.contains("Bearer ")) {
1298+
authorization = authorization.replace("Bearer ", "");
1299+
}
1300+
if (authorization == null || authorization.isEmpty()) {
1301+
throw new IEMRException("Authentication required");
1302+
}
1303+
try {
1304+
String sessionJson = sessionObject.getSessionObject(authorization);
1305+
if (sessionJson == null || sessionJson.isEmpty()) {
1306+
throw new IEMRException("Session expired. Please log in again.");
1307+
}
1308+
JSONObject session = new JSONObject(sessionJson);
1309+
return session.getLong("userID");
1310+
} catch (IEMRException e) {
1311+
throw e;
1312+
} catch (Exception e) {
1313+
logger.error("Authentication failed while extracting user ID: {}", e.getMessage());
1314+
throw new IEMRException("Authentication failed");
1315+
}
1316+
}
1317+
1318+
private void validateAdminPrivileges(Long userId) throws IEMRException {
1319+
if (!iemrAdminUserServiceImpl.hasAdminPrivileges(userId)) {
1320+
logger.warn("Unauthorized access attempt by userId: {}", userId);
1321+
throw new IEMRException("Access denied. Admin privileges required.");
1322+
}
1323+
}
12331324
}

src/main/java/com/iemr/common/data/users/User.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,10 @@ public class User implements Serializable {
213213
@Column(name = "dhistoken")
214214
private String dhistoken;
215215

216+
@Expose
217+
@Column(name = "lock_timestamp")
218+
private Timestamp lockTimestamp;
219+
216220
/*
217221
* protected User() { }
218222
*/

src/main/java/com/iemr/common/repository/users/IEMRUserRepositoryCustom.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ UserSecurityQMapping verifySecurityQuestionAnswers(@Param("UserID") Long UserID,
7575

7676
@Query("SELECT u FROM User u WHERE u.userID=5718")
7777
User getAllExistingUsers();
78-
78+
7979
User findByUserID(Long userID);
8080

8181
}

src/main/java/com/iemr/common/service/users/IEMRAdminUserService.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,10 @@ public List<ServiceRoleScreenMapping> getUserServiceRoleMappingForProvider(Integ
123123

124124
List<User> getUserIdbyUserName(String userName) throws IEMRException;
125125

126+
boolean unlockUserAccount(Long userId) throws IEMRException;
127+
128+
String getUserLockStatusJson(Long userId) throws IEMRException;
129+
130+
boolean hasAdminPrivileges(Long userId) throws IEMRException;
126131

127-
128132
}

0 commit comments

Comments
 (0)