Skip to content

Redis Changes and download master changes#54

Merged
devikasuresh20 merged 3 commits intoPSMRI:developfrom
ravishanigarapu:develop
Sep 24, 2024
Merged

Redis Changes and download master changes#54
devikasuresh20 merged 3 commits intoPSMRI:developfrom
ravishanigarapu:develop

Conversation

@ravishanigarapu
Copy link
Copy Markdown
Contributor

@ravishanigarapu ravishanigarapu commented Sep 20, 2024

📋 Description

JIRA ID: AMM-985

download masters are not working


✅ Type of Change

  • 🐞 Bug fix (non-breaking change which resolves an issue)
  • New feature (non-breaking change which adds functionality)
  • 🔥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 🛠 Refactor (change that is neither a fix nor a new feature)
  • ⚙️ Config change (configuration file or build script updates)
  • 📚 Documentation (updates to docs or readme)
  • 🧪 Tests (adding new or updating existing tests)
  • 🎨 UI/UX (changes that affect the user interface)
  • 🚀 Performance (improves performance)
  • 🧹 Chore (miscellaneous changes that don't modify src or test files)

ℹ️ Additional Information

Please describe how the changes were tested, and include any relevant screenshots, logs, or other information that provides additional context.

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a SessionObject class for improved session management using Redis.
    • Added a Validator class to enhance session validation and IP address checks during user logins.
    • New ConfigProperties class for centralized management of application configuration settings.
  • Improvements

    • Updated timestamp handling for download dates to include full timestamp precision.
    • Enhanced error handling in session management methods with specific exceptions.
    • Streamlined authorization handling in the HttpInterceptor class for better session management.
  • Refactor

    • Restructured session management methods in the RedisStorage class for clarity and functionality.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Sep 20, 2024

Walkthrough

The changes involve multiple modifications across various classes in the application, focusing on enhancing session management, improving configuration property handling, and refining authorization processes. Key updates include the introduction of new classes for managing session data and validation, modifications to existing repository methods for timestamp precision, and adjustments to error handling mechanisms. Overall, the changes aim to streamline functionality and improve the robustness of session and configuration management.

Changes

File Change Summary
src/main/java/com/iemr/mmu/repo/syncActivity_syncLayer/SyncDownloadMasterRepo.java Updated method updateTableSyncDownloadMasterForLastDownloadDate to use current_timestamp() instead of current_date(), affecting the precision of the lastDownloadDate field.
src/main/java/com/iemr/mmu/service/dataSyncActivity/DownloadDataFromServerImpl.java Minor formatting change: added space after the closing brace of an if statement in the downloadMasterDataFromServer method.
src/main/java/com/iemr/mmu/utils/config/ConfigProperties.java New class added to manage application configuration properties, including methods to retrieve various property values and handle errors during property loading.
src/main/java/com/iemr/mmu/utils/http/HttpInterceptor.java Refactored to improve authorization handling, replaced RedisStorage with Validator, and updated error handling mechanisms in preHandle and postHandle methods.
src/main/java/com/iemr/mmu/utils/redis/RedisStorage.java Several method updates for session management, including removal of getSessionObject, addition of setObject, and improved error handling with RedisSessionException.
src/main/java/com/iemr/mmu/utils/sessionobject/SessionObject.java New class for managing session data in Redis, with methods for getting, setting, updating, and deleting session objects.
src/main/java/com/iemr/mmu/utils/validator/Validator.java New class for session validation, focusing on IP address checks during user sessions, with methods for updating the session cache and checking key existence.

Possibly related PRs

  • Downloadmaster changes #53: The changes in the SyncDownloadMasterRepo interface are related as both PRs modify the same file (SyncDownloadMasterRepo.java) and involve updates to method signatures within the interface.

Suggested reviewers

  • devikasuresh20

Poem

🐰 In the code where bunnies play,
New changes hop in bright array.
With sessions snug and config neat,
Our app's now light on its little feet!
So let us cheer, with joyful glee,
For every line, a victory! 🌟


Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 58c7f09 and e49029f.

Files selected for processing (4)
  • src/main/java/com/iemr/mmu/utils/config/ConfigProperties.java (1 hunks)
  • src/main/java/com/iemr/mmu/utils/http/HttpInterceptor.java (1 hunks)
  • src/main/java/com/iemr/mmu/utils/redis/RedisStorage.java (1 hunks)
  • src/main/java/com/iemr/mmu/utils/sessionobject/SessionObject.java (1 hunks)
Files skipped from review as they are similar to previous changes (3)
  • src/main/java/com/iemr/mmu/utils/config/ConfigProperties.java
  • src/main/java/com/iemr/mmu/utils/redis/RedisStorage.java
  • src/main/java/com/iemr/mmu/utils/sessionobject/SessionObject.java
Additional comments not posted (3)
src/main/java/com/iemr/mmu/utils/http/HttpInterceptor.java (3)

61-64: LGTM!

The logging statements provide useful information for debugging and monitoring purposes.


70-89: Looks good!

The list of endpoints that bypass the authorization check seems reasonable and includes authentication, password management, and documentation-related endpoints.


134-134: LGTM!

The debug logging statement in the afterCompletion method provides useful information for debugging and monitoring purposes.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    -- I pushed a fix in commit <commit_id>, please review it.
    -- Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    -- @coderabbitai generate unit testing code for this file.
    -- @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    -- @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    -- @coderabbitai read src/utils.ts and generate unit testing code.
    -- @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    -- @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 19

Outside diff range and nitpick comments (5)
src/main/java/com/iemr/mmu/utils/sessionObject/SessionObject.java (2)

18-18: Remove the commented @Required annotation.

The @Required annotation is not needed here as the @Autowired annotation already marks the dependency as required.

-	// @Required

1-73: Summary of the review:

The SessionObject class is properly structured and annotated as a Spring component. It manages session objects using Redis operations through the injected RedisStorage dependency.

However, there are a few areas that need improvement:

  1. The config values extendExpirationTime and sessionExpiryTime are initialized in the constructor but fetched again from ConfigProperties for each method invocation. This is inefficient and should be refactored to use the already initialized fields.

  2. The purpose and logic behind updating a concurrent session object based on the userName field in the updateConcurrentSessionObject method are not clear from the given context. More clarification is needed to understand how this fits into the overall system design and requirements.

Please address the refactoring suggestions and provide more context on the concurrent session object update logic.

src/main/java/com/iemr/mmu/utils/validator/Validator.java (2)

22-22: Remove deprecated @Required annotation

The @Required annotation is deprecated in recent versions of Spring and is currently commented out. It's safe to remove it to clean up the code.

Apply this diff to remove the commented-out annotation:

-	//	@Required

42-42: Declare logger as static final

For best practices, the logger should be declared as static final to ensure that only one instance exists and to reflect that it does not change.

Apply this diff:

-	private Logger logger = LoggerFactory.getLogger(Validator.class);
+	private static final Logger logger = LoggerFactory.getLogger(Validator.class);
src/main/java/com/iemr/mmu/utils/config/ConfigProperties.java (1)

96-96: Typo in log messages: 'retrival' should be 'retrieval'

There's a typo in the log messages where "retrival" should be "retrieval".

Apply this diff to fix the typo:

-logger.error(propertyName + " retrival failed.", e);
+logger.error(propertyName + " retrieval failed.", e);

Repeat this correction for all occurrences.

Also applies to: 106-106, 116-116, 126-126, 136-136

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 8df7736 and 070bc94.

Files selected for processing (8)
  • src/main/java/com/iemr/mmu/repo/syncActivity_syncLayer/SyncDownloadMasterRepo.java (1 hunks)
  • src/main/java/com/iemr/mmu/service/dataSyncActivity/DownloadDataFromServerImpl.java (1 hunks)
  • src/main/java/com/iemr/mmu/utils/config/ConfigProperties.java (1 hunks)
  • src/main/java/com/iemr/mmu/utils/http/HttpInterceptor.java (1 hunks)
  • src/main/java/com/iemr/mmu/utils/redis/RedisSessionException.java (1 hunks)
  • src/main/java/com/iemr/mmu/utils/redis/RedisStorage.java (1 hunks)
  • src/main/java/com/iemr/mmu/utils/sessionObject/SessionObject.java (1 hunks)
  • src/main/java/com/iemr/mmu/utils/validator/Validator.java (1 hunks)
Files skipped from review due to trivial changes (1)
  • src/main/java/com/iemr/mmu/service/dataSyncActivity/DownloadDataFromServerImpl.java
Additional comments not posted (8)
src/main/java/com/iemr/mmu/utils/redis/RedisSessionException.java (1)

1-13: LGTM!

The custom exception class RedisSessionException is implemented correctly:

  • It extends the IEMRException class, which is likely a base exception class in the project.
  • It provides two constructors:
    1. One that takes a message and a cause, and passes them to the super constructor.
    2. Another that takes only a message and passes it to the super constructor.
  • This is a common and recommended pattern for defining custom exception classes in Java.

The code follows Java naming conventions and there are no apparent issues with the logic or syntax.

src/main/java/com/iemr/mmu/repo/syncActivity_syncLayer/SyncDownloadMasterRepo.java (1)

42-42: Verify impact of changing lastDownloadDate precision.

The change from current_date() to current_timestamp() for the lastDownloadDate field seems to be an intentional improvement in precision to capture more granular download timestamps.

However, please verify the following:

  1. Ensure that this change does not break any downstream processes, reporting, or assumptions that might be relying on the lastDownloadDate field being a date-only value.

  2. Review the database schema to confirm that the lastDownloadDate column is defined with a data type that can accommodate a full timestamp. If it's currently a date-only column, a schema update might be necessary.

  3. Scan the codebase for any references to the lastDownloadDate field to ensure compatibility with the new timestamp format.

Run the following script to search for potential impact areas:

src/main/java/com/iemr/mmu/utils/sessionObject/SessionObject.java (2)

24-28: LGTM!

The constructor correctly initializes the extendExpirationTime and sessionExpiryTime fields from ConfigProperties.


50-52: Clarify the purpose and logic of updating a concurrent session object.

The updateConcurrentSessionObject method is called to update a concurrent session object based on the userName field in the session data. However, the purpose and logic behind this operation is not clear from the given context.

  • What is the significance of updating a concurrent session object?
  • How does updating a concurrent session object based on userName fit into the overall system design and requirements?

Please provide more context and clarification on this aspect of the code.

src/main/java/com/iemr/mmu/utils/redis/RedisStorage.java (4)

43-59: LGTM!

The setObject method correctly sets the value for a given key and updates the session expiration time if the key does not exist. The logic is sound and the implementation is accurate.


61-80: LGTM!

The getObject method correctly retrieves the value for a given key and extends the expiration time if specified. The logic is sound and the implementation is accurate. The method also correctly throws a RedisSessionException if the session data does not exist.


82-89: LGTM!

The deleteObject method correctly deletes the session object for a given key using the Redis connection. The logic is sound and the implementation is accurate.


91-113: LGTM!

The updateObject method correctly updates the value for a given key and extends the expiration time if specified. The logic is sound and the implementation is accurate. The method also correctly throws a RedisSessionException if the session data does not exist.

Comment on lines +34 to +39
public String getSessionObject(String key) throws RedisSessionException {
Boolean extendExpirationTime = ConfigProperties.getExtendExpiryTime();
Integer sessionExpiryTime = ConfigProperties.getSessionExpiryTime();

return objectStore.getObject(key, Boolean.valueOf(extendExpirationTime), sessionExpiryTime);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the already initialized fields.

The extendExpirationTime and sessionExpiryTime fields are already initialized in the constructor from ConfigProperties. Instead of fetching these values again for each method invocation, directly use the initialized fields.

-		Boolean extendExpirationTime = ConfigProperties.getExtendExpiryTime();
-		Integer sessionExpiryTime = ConfigProperties.getSessionExpiryTime();
+		Boolean extendExpirationTime = this.extendExpirationTime;
+		Integer sessionExpiryTime = this.sessionExpiryTime;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public String getSessionObject(String key) throws RedisSessionException {
Boolean extendExpirationTime = ConfigProperties.getExtendExpiryTime();
Integer sessionExpiryTime = ConfigProperties.getSessionExpiryTime();
return objectStore.getObject(key, Boolean.valueOf(extendExpirationTime), sessionExpiryTime);
}
public String getSessionObject(String key) throws RedisSessionException {
Boolean extendExpirationTime = this.extendExpirationTime;
Integer sessionExpiryTime = this.sessionExpiryTime;
return objectStore.getObject(key, Boolean.valueOf(extendExpirationTime), sessionExpiryTime);
}

Comment on lines +46 to +52
public String updateSessionObject(String key, String value) throws RedisSessionException {
Boolean extendExpirationTime = ConfigProperties.getExtendExpiryTime();
Integer sessionExpiryTime = ConfigProperties.getSessionExpiryTime();

updateConcurrentSessionObject(key, value, extendExpirationTime, sessionExpiryTime);
return objectStore.updateObject(key, value, Boolean.valueOf(extendExpirationTime), sessionExpiryTime);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the already initialized fields.

The extendExpirationTime and sessionExpiryTime fields are already initialized in the constructor from ConfigProperties. Instead of fetching these values again for each method invocation, directly use the initialized fields.

-		Boolean extendExpirationTime = ConfigProperties.getExtendExpiryTime();
-		Integer sessionExpiryTime = ConfigProperties.getSessionExpiryTime();
+		Boolean extendExpirationTime = this.extendExpirationTime;
+		Integer sessionExpiryTime = this.sessionExpiryTime;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public String updateSessionObject(String key, String value) throws RedisSessionException {
Boolean extendExpirationTime = ConfigProperties.getExtendExpiryTime();
Integer sessionExpiryTime = ConfigProperties.getSessionExpiryTime();
updateConcurrentSessionObject(key, value, extendExpirationTime, sessionExpiryTime);
return objectStore.updateObject(key, value, Boolean.valueOf(extendExpirationTime), sessionExpiryTime);
}
public String updateSessionObject(String key, String value) throws RedisSessionException {
Boolean extendExpirationTime = this.extendExpirationTime;
Integer sessionExpiryTime = this.sessionExpiryTime;
updateConcurrentSessionObject(key, value, extendExpirationTime, sessionExpiryTime);
return objectStore.updateObject(key, value, Boolean.valueOf(extendExpirationTime), sessionExpiryTime);
}

Comment on lines +41 to +44
public String setSessionObject(String key, String value) throws RedisSessionException {
Integer sessionExpiryTime = ConfigProperties.getSessionExpiryTime();
return objectStore.setObject(key, value, sessionExpiryTime);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the already initialized field.

The sessionExpiryTime field is already initialized in the constructor from ConfigProperties. Instead of fetching this value again for each method invocation, directly use the initialized field.

-		Integer sessionExpiryTime = ConfigProperties.getSessionExpiryTime();
+		Integer sessionExpiryTime = this.sessionExpiryTime;
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public String setSessionObject(String key, String value) throws RedisSessionException {
Integer sessionExpiryTime = ConfigProperties.getSessionExpiryTime();
return objectStore.setObject(key, value, sessionExpiryTime);
}
public String setSessionObject(String key, String value) throws RedisSessionException {
Integer sessionExpiryTime = this.sessionExpiryTime;
return objectStore.setObject(key, value, sessionExpiryTime);
}

Comment on lines +107 to +112
throw new Exception();
}
}
} catch (Exception e)
{
throw new IEMRException("Session is expired. Please login again.");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid throwing and catching generic Exception

Throwing and catching the generic Exception can obscure the actual cause of errors and complicate debugging. Instead, throw a specific exception or handle the condition appropriately.

Apply this diff to throw a specific exception:

-					throw new Exception();
+					throw new IEMRException("IP address mismatch. Session may be compromised.");
 			} 
 		}
-	} catch (Exception e)
+	} catch (IEMRException e)
 	{
-		throw new IEMRException("Session is expired. Please login again.");
+		throw e;
 	}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
throw new Exception();
}
}
} catch (Exception e)
{
throw new IEMRException("Session is expired. Please login again.");
throw new IEMRException("IP address mismatch. Session may be compromised.");
}
}
} catch (IEMRException e)
{
throw e;

Comment on lines +19 to +39
private static Boolean enableIPValidation = false;

@Autowired(required = true)
// @Required
public void setSessionObject(SessionObject sessionObject)
{
this.session = sessionObject;
}

// private static void setSessionObject() {
// if (session == null) {
// session = new SessionObject();
// }
// }

public Validator()
{
if (!enableIPValidation)
{
enableIPValidation = ConfigProperties.getBoolean("enableIPValidation");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid modifying static variables in the constructor

The enableIPValidation variable is declared as static, but it is being modified within the instance constructor. Modifying static variables in the constructor can lead to unexpected behavior in multi-threaded environments and is generally discouraged.

Consider initializing enableIPValidation in a static initializer block or at the point of declaration.

Apply this diff to initialize enableIPValidation at the point of declaration:

-	private static Boolean enableIPValidation = false;
+	private static final Boolean enableIPValidation = ConfigProperties.getBoolean("enableIPValidation");

Then remove the initialization from the constructor:

-	public Validator()
-	{
-		if (!enableIPValidation)
-		{
-			enableIPValidation = ConfigProperties.getBoolean("enableIPValidation");
-		}
-	}
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static Boolean enableIPValidation = false;
@Autowired(required = true)
// @Required
public void setSessionObject(SessionObject sessionObject)
{
this.session = sessionObject;
}
// private static void setSessionObject() {
// if (session == null) {
// session = new SessionObject();
// }
// }
public Validator()
{
if (!enableIPValidation)
{
enableIPValidation = ConfigProperties.getBoolean("enableIPValidation");
}
private static final Boolean enableIPValidation = ConfigProperties.getBoolean("enableIPValidation");
@Autowired(required = true)
// @Required
public void setSessionObject(SessionObject sessionObject)
{
this.session = sessionObject;
}
// private static void setSessionObject() {
// if (session == null) {
// session = new SessionObject();
// }
// }

@Component
public class HttpInterceptor implements HandlerInterceptor {
Logger logger = LoggerFactory.getLogger(this.getClass().getName());
private Validator validator;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer Constructor Injection Over Setter Injection for Mandatory Dependencies

The Validator and SessionObject dependencies are injected via setter methods. It's a best practice to use constructor injection for mandatory dependencies to ensure immutability and make the class's requirements explicit.

Refactor the class to use constructor injection:

 public class HttpInterceptor implements HandlerInterceptor {
-	private Validator validator;
-	private SessionObject sessionObject;

-	@Autowired
-	public void setValidator(Validator validator) {
-		this.validator = validator;
-	}

-	@Autowired
-	public void setSessionObject(SessionObject sessionObject) {
-		this.sessionObject = sessionObject;
-	}

+	private final Validator validator;
+	private final SessionObject sessionObject;

+	@Autowired
+	public HttpInterceptor(Validator validator, SessionObject sessionObject) {
+		this.validator = validator;
+		this.sessionObject = sessionObject;
+	}

Also applies to: 53-53, 49-51, 56-58

Logger logger = LoggerFactory.getLogger(this.getClass().getName());
private Validator validator;

Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider Using Fully Qualified Class Name in Logger Initialization

The logger is initialized using getClass().getSimpleName(), which returns only the simple class name. Using getClass().getName() provides the fully qualified class name, offering more context in logs, especially in larger projects where class names might overlap.

Apply this change to enhance logging clarity:

- Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName());
+ Logger logger = LoggerFactory.getLogger(this.getClass().getName());
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName());
Logger logger = LoggerFactory.getLogger(this.getClass().getName());

Comment on lines +64 to +66
String authorization = request.getHeader("Authorization");
logger.debug("RequestURI::" + request.getRequestURI() + " || Authorization ::" + authorization
+ " || method :: " + request.getMethod());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure Safe Handling of Null or Missing Authorization Header

The authorization header is retrieved and used in logging without null checks. This could lead to a NullPointerException or logging "null" as the authorization value, which might not be desirable.

Add a null check for the authorization header:

 String authorization = request.getHeader("Authorization");
+ if (authorization == null || authorization.trim().isEmpty()) {
+     logger.warn("Authorization header is missing or empty for request URI: " + request.getRequestURI());
+     // Handle missing authorization appropriately, e.g., set status to false or return an error response
+ }
 logger.debug("RequestURI::" + request.getRequestURI() + " || Authorization :: " + authorization
         + " || method :: " + request.getMethod());
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
String authorization = request.getHeader("Authorization");
logger.debug("RequestURI::" + request.getRequestURI() + " || Authorization ::" + authorization
+ " || method :: " + request.getMethod());
String authorization = request.getHeader("Authorization");
if (authorization == null || authorization.trim().isEmpty()) {
logger.warn("Authorization header is missing or empty for request URI: " + request.getRequestURI());
// Handle missing authorization appropriately, e.g., set status to false or return an error response
}
logger.debug("RequestURI::" + request.getRequestURI() + " || Authorization ::" + authorization
+ " || method :: " + request.getMethod());

Comment on lines +123 to +127
logger.debug("In postHandle we are Intercepting the Request");
String authorization = request.getHeader("Authorization");
logger.debug("RequestURI::" + request.getRequestURI() + " || Authorization ::" + authorization);
if (authorization != null) {
// redisStorage.updateConcurrentSessionObject(redisStorage.getSessionObject(authorization));
// redisStorage.updateSessionObject(authorization);
sessionObject.updateSessionObject(authorization, sessionObject.getSessionObject(authorization));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure Session Object Updates Handle Null Values Safely

The code updates the session object without checking if sessionObject.getSessionObject(authorization) returns null. If it does, this could lead to a NullPointerException.

Add a null check before updating the session:

 if (authorization != null) {
+    SessionData sessionData = sessionObject.getSessionObject(authorization);
+    if (sessionData != null) {
         sessionObject.updateSessionObject(authorization, sessionData);
+    } else {
+        logger.warn("Session data not found for authorization: " + authorization);
+        // Handle the missing session data as needed
+    }
 }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.debug("In postHandle we are Intercepting the Request");
String authorization = request.getHeader("Authorization");
logger.debug("RequestURI::" + request.getRequestURI() + " || Authorization ::" + authorization);
if (authorization != null) {
// redisStorage.updateConcurrentSessionObject(redisStorage.getSessionObject(authorization));
// redisStorage.updateSessionObject(authorization);
sessionObject.updateSessionObject(authorization, sessionObject.getSessionObject(authorization));
logger.debug("In postHandle we are Intercepting the Request");
String authorization = request.getHeader("Authorization");
logger.debug("RequestURI::" + request.getRequestURI() + " || Authorization ::" + authorization);
if (authorization != null) {
SessionData sessionData = sessionObject.getSessionObject(authorization);
if (sessionData != null) {
sessionObject.updateSessionObject(authorization, sessionData);
} else {
logger.warn("Session data not found for authorization: " + authorization);
// Handle the missing session data as needed
}
}

Comment on lines +105 to +112
OutputResponse output = new OutputResponse();
output.setError(e);
response.setContentType(MediaType.APPLICATION_JSON);

// response.setContentLength(e.getLocalizedMessage().length());
response.setContentLength(output.toString().length());
response.setHeader("Access-Control-Allow-Origin", "*");
response.getOutputStream().print(output.toString());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle Exceptions More Specifically and Securely

Catching the generic Exception class can obscure the root cause of errors and potentially expose sensitive information if exceptions are sent to clients. It's better to catch specific exceptions and avoid returning stack traces or internal messages in API responses.

Modify the exception handling to catch specific exceptions and provide a user-friendly error message:

- } catch (Exception e) {
+ } catch (SpecificExceptionType e) {
     OutputResponse output = new OutputResponse();
-    output.setError(e);
+    output.setError(new ErrorResponse("An unexpected error occurred. Please try again later."));
     response.setContentType(MediaType.APPLICATION_JSON);
     response.setContentLength(output.toString().length());
     response.setHeader("Access-Control-Allow-Origin", "*");
     response.getOutputStream().print(output.toString());
     status = false;
 }

Replace SpecificExceptionType with the actual exceptions you expect (e.g., IOException, ValidationException). Ensure that detailed error information is logged internally but not exposed to the client.

Committable suggestion was skipped due to low confidence.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Outside diff range and nitpick comments (1)
src/main/java/com/iemr/mmu/utils/redis/RedisStorage.java (1)

43-58: Consider if the behavior of not overwriting existing non-empty values is desired for all use cases.

The setObject method provides a way to set a value in Redis with an expiration time, but only if the key doesn't already have a non-empty value. This behavior might be intended to avoid overwriting existing non-empty values.

However, it's unclear if this is the desired behavior for all use cases. Consider if there are scenarios where you might want to overwrite the value regardless of its current state.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 070bc94 and 58c7f09.

Files selected for processing (5)
  • src/main/java/com/iemr/mmu/utils/config/ConfigProperties.java (1 hunks)
  • src/main/java/com/iemr/mmu/utils/http/HttpInterceptor.java (1 hunks)
  • src/main/java/com/iemr/mmu/utils/redis/RedisStorage.java (1 hunks)
  • src/main/java/com/iemr/mmu/utils/sessionobject/SessionObject.java (1 hunks)
  • src/main/java/com/iemr/mmu/utils/validator/Validator.java (1 hunks)
Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/iemr/mmu/utils/config/ConfigProperties.java
  • src/main/java/com/iemr/mmu/utils/validator/Validator.java
Additional comments not posted (12)
src/main/java/com/iemr/mmu/utils/sessionobject/SessionObject.java (5)

19-22: LGTM!

The method is a simple setter for the RedisStorage object, and the implementation is correct.


24-28: LGTM!

The constructor correctly initializes the extendExpirationTime and sessionExpiryTime variables with values from the ConfigProperties class.


34-38: LGTM!

The method correctly retrieves a session object from Redis using the getObject method of the RedisStorage object, passing the sessionExpiryTime as an argument.


40-43: LGTM!

The method correctly sets a session object in Redis using the setObject method of the RedisStorage object, passing the sessionExpiryTime as an argument.


45-51: LGTM!

The method correctly updates a session object in Redis using the updateObject method of the RedisStorage object, passing the extendExpirationTime and sessionExpiryTime as arguments. It also calls the updateConcurrentSessionObject method to update a concurrent session object.

src/main/java/com/iemr/mmu/utils/redis/RedisStorage.java (1)

60-75: LGTM!

The updated getObject method looks good:

  • It retrieves the value for a given key from Redis and extends its expiration time.
  • It throws a specific RedisSessionException if the value is not found or is empty, providing clear error handling.
  • The exception type change from a generic Exception to RedisSessionException is an improvement.
src/main/java/com/iemr/mmu/utils/http/HttpInterceptor.java (6)

33-35: LGTM!

The new imports are valid and necessary for the class.


61-64: LGTM!

The updated logger statements using parameterized logging are a good practice.


70-88: LGTM!

The new switch case block correctly handles the APIs that should be allowed without authorization.


106-109: LGTM!

The response handling in the exception block is appropriate.


120-122: LGTM!

The updated logger statements using parameterized logging are a good practice.


134-134: LGTM!

The updated logger statement in the afterCompletion method is acceptable.

Comment on lines +52 to +65
private void updateConcurrentSessionObject(String key, String value, Boolean extendExpirationTime,
Integer sessionExpiryTime) {
try {
JsonObject jsnOBJ = new JsonObject();
JsonParser jsnParser = new JsonParser();
JsonElement jsnElmnt = jsnParser.parse(value);
jsnOBJ = jsnElmnt.getAsJsonObject();
if (jsnOBJ.has("userName") && jsnOBJ.get("userName") != null) {
objectStore.updateObject(jsnOBJ.get("userName").getAsString().trim().toLowerCase(), key,
extendExpirationTime, sessionExpiryTime);
}
} catch (Exception e) {
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle exceptions properly instead of swallowing them.

The method is catching and ignoring any exceptions that may occur. This is not a good practice, as it can make it difficult to diagnose and fix issues in the code. If an exception occurs, it will be silently ignored, and the concurrent session object may not be updated correctly.

Consider logging the exception or throwing a custom exception to handle it properly. For example:

-		} catch (Exception e) {
-		}
+		} catch (Exception e) {
+			log.error("Error updating concurrent session object", e);
+			throw new RedisSessionException("Error updating concurrent session object", e);
+		}

Committable suggestion was skipped due to low confidence.

Comment on lines +79 to +86
public Long deleteObject(String key) throws RedisSessionException {
RedisConnection redCon = connection.getConnection();
Long userRespFromRedis = Long.valueOf(0L);

userRespFromRedis = redCon.del(key.getBytes());

return userRespFromRedis;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove or add the appropriate conditions for the declared RedisSessionException.

The deleteObject method provides the functionality to delete a Redis key and return the number of keys deleted, which looks good.

However, the method signature declares that it throws a RedisSessionException, but there is no explicit throw statement in the method body. This could confuse callers of the method.

Consider either removing the throws RedisSessionException from the method signature if it's not needed, or adding the appropriate conditions to throw the exception in the method body.

Comment on lines +88 to +106
public String updateObject(String key, String value, Boolean extendExpirationTime, int expirationTime)
throws RedisSessionException {
RedisConnection redCon = connection.getConnection();


if ((sessionData != null) && sessionData.length > 0) {
logger.info("updating session time in redis for " + key);
redisCon.stringCommands().set(key.getBytes(), sessionData, Expiration.seconds(sessionExpiryTimeInSec),
SetOption.UPSERT);
byte[] sessionData = redCon.get(key.getBytes());
String userRespFromRedis = null;
if (sessionData != null) {
userRespFromRedis = new String(redCon.get(key.getBytes()));
}

if ((userRespFromRedis != null) && (userRespFromRedis.trim().length() != 0)) {

redCon.set(key.getBytes(), value.getBytes(), Expiration.seconds(expirationTime), SetOption.UPSERT);
} else {
throw new Exception("Unable to fetch session object from Redis server");
throw new RedisSessionException("Unable to fetch session object from Redis server");
}


Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove or implement the functionality for the unused extendExpirationTime parameter.

The updateObject method provides the functionality to update the value and expiration time for a key in Redis, but only if the key already exists and has a non-empty value. It throws a RedisSessionException if the key doesn't exist or has an empty value, providing clear error handling. This looks good.

However, the extendExpirationTime parameter is unused in the method body. This could indicate an incomplete implementation or a redundant parameter.

Consider either removing the extendExpirationTime parameter if it's not needed, or implementing the intended functionality for it in the method body.

Comment on lines +94 to +98
String remoteAddress = request.getHeader("X-FORWARDED-FOR");
if (remoteAddress == null || remoteAddress.trim().length() == 0) {
remoteAddress = request.getRemoteAddr();
}
validator.checkKeyExists(authorization, remoteAddress);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Validate and sanitize the remote address.

As mentioned in the previous review, using the X-FORWARDED-FOR header directly can be insecure, as clients can spoof this header. Relying on this value without validation might lead to security vulnerabilities.

Consider implementing additional validation to ensure the remoteAddress is trustworthy before passing it to the validator.checkKeyExists method.

Comment on lines +102 to +103
OutputResponse output = new OutputResponse();
output.setError(e);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle exceptions more securely.

Catching the generic Exception and setting it directly in the OutputResponse may expose sensitive information to clients.

Consider catching specific exceptions and providing a user-friendly error message instead:

} catch (SpecificExceptionType e) {
    OutputResponse output = new OutputResponse();
    output.setError(new ErrorResponse("An unexpected error occurred. Please try again later."));
    // ...
}

Replace SpecificExceptionType with the actual exceptions you expect (e.g., IOException, ValidationException). Ensure that detailed error information is logged internally but not exposed to the client.

if (authorization != null) {
// redisStorage.updateConcurrentSessionObject(redisStorage.getSessionObject(authorization));
// redisStorage.updateSessionObject(authorization);
sessionObject.updateSessionObject(authorization, sessionObject.getSessionObject(authorization));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure session object updates handle null values safely.

The code updates the session object without checking if sessionObject.getSessionObject(authorization) returns null. If it does, this could lead to a NullPointerException.

Add a null check before updating the session:

SessionData sessionData = sessionObject.getSessionObject(authorization);
if (sessionData != null) {
    sessionObject.updateSessionObject(authorization, sessionData);
} else {
    logger.warn("Session data not found for authorization: " + authorization);
    // Handle the missing session data as needed
}

}
} catch (Exception e) {
logger.error("postHandle failed with error " + e.getMessage());
logger.debug("postHandle failed with error " + e.getMessage());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log exceptions with appropriate severity and details.

Logging exceptions at the debug level may cause important exceptions to be overlooked, and valuable debugging information could be missed.

Update the logging to use the error level and include the stack trace:

logger.error("postHandle failed with error", e);

This change ensures that significant errors are properly recorded and can be investigated if necessary.

@sonarqubecloud
Copy link
Copy Markdown

@devikasuresh20 devikasuresh20 self-requested a review September 24, 2024 09:36
@devikasuresh20 devikasuresh20 merged commit 42d1b6a into PSMRI:develop Sep 24, 2024
@coderabbitai coderabbitai Bot mentioned this pull request Jan 29, 2025
10 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants