Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
Expand Down Expand Up @@ -95,6 +96,8 @@
private CookieUtil cookieUtil;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;

private AESUtil aesUtil;

Expand Down Expand Up @@ -168,7 +171,88 @@
}

String decryptPassword = aesUtil.decrypt("Piramal12Piramal", m_User.getPassword());
List<User> mUser = iemrAdminUserServiceImpl.userAuthenticate(m_User.getUserName(), decryptPassword);
// Fetch user
List<User> existingUser = iemrAdminUserServiceImpl.userExitsCheck(m_User.getUserName());

/*
* =========================================
* ACCOUNT LOCK CHECK
* =========================================
*/
if(!existingUser.isEmpty()){
if (existingUser.get(0) != null

Check warning on line 183 in src/main/java/com/iemr/common/controller/users/IEMRAdminController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Merge this if statement with the enclosing one.

See more on https://sonarcloud.io/project/issues?id=PSMRI_Common-API&issues=AZ6IQjJ83gzx05SyBiSU&open=AZ6IQjJ83gzx05SyBiSU&pullRequest=426
&& existingUser.get(0).getFailedAttempt() != null
&& existingUser.get(0).getFailedAttempt() >= 5) {

throw new IEMRException(
"Your account has been locked due to multiple failed login attempts. Please contact administrator.");
}
}


List<User> mUser = iemrAdminUserServiceImpl
.userAuthenticate(m_User.getUserName(), decryptPassword);

/*
* =========================================
* FAILED LOGIN ATTEMPT LOGIC
* =========================================
*/
if(!existingUser.isEmpty()){
if (existingUser != null) {

Check warning on line 202 in src/main/java/com/iemr/common/controller/users/IEMRAdminController.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Merge this if statement with the enclosing one.

See more on https://sonarcloud.io/project/issues?id=PSMRI_Common-API&issues=AZ6IQjJ83gzx05SyBiSV&open=AZ6IQjJ83gzx05SyBiSV&pullRequest=426

Integer failedAttempt = existingUser.get(0).getFailedAttempt() != null
? existingUser.get(0).getFailedAttempt()
: 0;

failedAttempt++;

existingUser.get(0).setFailedAttempt(failedAttempt);

iemrAdminUserServiceImpl.save(existingUser.get(0));

int remainingAttempts = 5 - failedAttempt;

// Lock account on 5th attempt
if (failedAttempt >= 5) {



response.setError(new IEMRException(
"Your account has been locked due to multiple failed login attempts."));
return response.toString();
}

// Warning on 3rd attempt
if (failedAttempt == 4) {


response.setError(new IEMRException(
"Invalid username or password. Remaining attempts: "
+ remainingAttempts
+ ". If you enter wrong username or password again, your account will be locked."));
return response.toString();
}


response.setError(new IEMRException(
"Invalid username or password. Remaining attempts: "
+ remainingAttempts));
return response.toString();

}
}

/*
* =========================================
* RESET FAILED ATTEMPTS ON SUCCESS LOGIN
* =========================================
*/
User loggedInUser = mUser.get(0);

loggedInUser.setFailedAttempt(0);

iemrAdminUserServiceImpl.save(loggedInUser);
JSONObject resMap = new JSONObject();
JSONObject serviceRoleMultiMap = new JSONObject();
JSONObject serviceRoleMap = new JSONObject();
Expand All @@ -190,15 +274,26 @@
String jwtToken = null;
String refreshToken = null;
if (mUser.size() == 1) {
jwtToken = jwtUtil.generateToken(m_User.getUserName(), mUser.get(0).getUserID().toString());
String userIdStr = mUser.get(0).getUserID().toString();
jwtToken = isMobile
? jwtUtil.generateSecureToken(userIdStr)
: jwtUtil.generateToken(m_User.getUserName(), userIdStr);

User user = new User(); // Assuming the Users class exists
user.setUserID(mUser.get(0).getUserID());
user.setUserName(mUser.get(0).getUserName());
logger.info("UserAgentUtil isMobile : " + isMobile);

// Store username -> JTI mapping so concurrent-session logout can denylist this token
stringRedisTemplate.opsForValue().set(
"jti:" + m_User.getUserName().trim().toLowerCase(),
jwtUtil.getJtiFromToken(jwtToken) + "|" + mUser.get(0).getUserID(),
jwtUtil.getAccessTokenExpiration(),
TimeUnit.MILLISECONDS
);

if (isMobile) {
refreshToken = jwtUtil.generateRefreshToken(m_User.getUserName(), user.getUserID().toString());
refreshToken = jwtUtil.generateSecureRefreshToken(user.getUserID().toString());
logger.debug("Refresh token generated successfully for user: {}", user.getUserName());
String jti = jwtUtil.getJtiFromToken(refreshToken);
redisTemplate.opsForValue().set(
Expand Down Expand Up @@ -239,7 +334,6 @@
// Facility data for ALL users - common pattern, empty if not applicable
try {
if (mUser.size() == 1) {
User loggedInUser = mUser.get(0);
String userRoleName = "";
if (loggedInUser.getM_UserServiceRoleMapping() != null) {
for (UserServiceRoleMapping usrm : loggedInUser.getM_UserServiceRoleMapping()) {
Expand Down Expand Up @@ -387,6 +481,19 @@
if (previousTokenFromRedis != null) {
deleteSessionObjectByGettingSessionDetails(previousTokenFromRedis);
sessionObject.deleteSessionObject(previousTokenFromRedis);

// Denylist the active JWT so System 1's requests are immediately rejected
String usernameKey = mUsers.get(0).getUserName().trim().toLowerCase();
String jtiData = stringRedisTemplate.opsForValue().get("jti:" + usernameKey);
if (jtiData != null) {
String[] parts = jtiData.split("\\|", 2);
tokenDenylist.addTokenToDenylist(parts[0], jwtUtil.getAccessTokenExpiration());
if (parts.length > 1) {
redisTemplate.delete("user_" + parts[1]);
}
stringRedisTemplate.delete("jti:" + usernameKey);
}

response.setResponse("User successfully logged out");
} else{
logger.error("Unable to fetch session from redis");
Expand Down Expand Up @@ -522,8 +629,16 @@
isMobile = UserAgentUtil.isMobileDevice(userAgent);
logger.info("UserAgentUtil isMobile : " + isMobile);

// Store username -> JTI mapping so concurrent-session logout can denylist this token
stringRedisTemplate.opsForValue().set(
"jti:" + m_User.getUserName().trim().toLowerCase(),
jwtUtil.getJtiFromToken(jwtToken) + "|" + mUser.getUserID(),
jwtUtil.getAccessTokenExpiration(),
TimeUnit.MILLISECONDS
);

if (isMobile) {
refreshToken = jwtUtil.generateRefreshToken(m_User.getUserName(), user.getUserID().toString());
refreshToken = jwtUtil.generateSecureRefreshToken(user.getUserID().toString());
logger.debug("Refresh token generated successfully for user: {}", user.getUserName());
String jti = jwtUtil.getJtiFromToken(refreshToken);
redisTemplate.opsForValue().set(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,20 +155,25 @@ else if(null != benificiaryDetails.getI_bendemographics().getReligion())
identityEditDTO.setReligion(benificiaryDetails.getI_bendemographics().getReligionName());

if (null != benificiaryDetails.getOccupation()) {
identityEditDTO.setOccupationName(benificiaryDetails.getOccupation());
} else if (null != benificiaryDetails.getI_bendemographics() &&
null != benificiaryDetails.getI_bendemographics().getOccupation()) {
identityEditDTO.setOccupationName(benificiaryDetails.getI_bendemographics().getOccupation());
} else {
identityEditDTO.setOccupationName(benificiaryDetails.getOccupationName());
identityEditDTO.setOccupationName(benificiaryDetails.getOccupation());
} else if (null != benificiaryDetails.getI_bendemographics().getOccupation()) {
identityEditDTO.setOccupationName(benificiaryDetails.getI_bendemographics().getOccupation());
} else if (null != benificiaryDetails.getOccupationName()) {
identityEditDTO.setOccupationName(benificiaryDetails.getOccupationName());
}
if (null != benificiaryDetails.getI_bendemographics().getOccupationID()) {
identityEditDTO.setOccupationId(benificiaryDetails.getI_bendemographics().getOccupationID());
}

if (null != benificiaryDetails.getEducation()) {
identityEditDTO.setEducation(benificiaryDetails.getEducation());
identityEditDTO.setEducation(benificiaryDetails.getEducation());
} else if (null != benificiaryDetails.getI_bendemographics() &&
null != benificiaryDetails.getI_bendemographics().getEducationName()) {
identityEditDTO.setEducation(benificiaryDetails.getI_bendemographics().getEducationName());
}
null != benificiaryDetails.getI_bendemographics().getEducationName()) {
identityEditDTO.setEducation(benificiaryDetails.getI_bendemographics().getEducationName());
}
if (null != benificiaryDetails.getI_bendemographics().getEducationID()) {
identityEditDTO.setEducationId(benificiaryDetails.getI_bendemographics().getEducationID());
}
if(null != benificiaryDetails.getIncomeStatus())
identityEditDTO.setIncomeStatus(benificiaryDetails.getIncomeStatus());
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,5 @@ public List<ServiceRoleScreenMapping> getUserServiceRoleMappingForProvider(Integ
List<User> findUserIdByUserName(String userName) throws IEMRException;



User save(User loggedInUser);
}
Original file line number Diff line number Diff line change
Expand Up @@ -1230,4 +1230,9 @@ public List<User> findUserIdByUserName(String userName) {

return iEMRUserRepositoryCustom.findUserName(userName);
}

@Override
public User save(User loggedInUser) {
return iEMRUserRepositoryCustom.save(loggedInUser);
}
}
25 changes: 25 additions & 0 deletions src/main/java/com/iemr/common/utils/JwtUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,27 @@ public String generateToken(String username, String userId) {
return buildToken(username, userId, "access", ACCESS_EXPIRATION_TIME);
}

// Mobile login: token without PII in sub
public String generateSecureToken(String userId) {
return buildSecureToken(userId, "access", ACCESS_EXPIRATION_TIME);
}

public String generateSecureRefreshToken(String userId) {
return buildSecureToken(userId, "refresh", REFRESH_EXPIRATION_TIME);
}

private String buildSecureToken(String userId, String tokenType, long expiration) {
return Jwts.builder()
.subject(userId)
.claim("userId", userId)
.claim("token_type", tokenType)
.id(UUID.randomUUID().toString())
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + expiration))
.signWith(getSigningKey())
.compact();
}

/**
* Generate a refresh token.
*
Expand Down Expand Up @@ -163,6 +184,10 @@ public long getRefreshTokenExpiration() {
return REFRESH_EXPIRATION_TIME;
}

public long getAccessTokenExpiration() {
return ACCESS_EXPIRATION_TIME;
}

/**
* Extract user ID from JWT token in the request (checks header and cookie)
* @param request the HTTP request
Expand Down
Loading