Skip to content

jwt api implementation#56

Merged
ravishanigarapu merged 8 commits intodevelopfrom
jwt-api
Feb 5, 2025
Merged

jwt api implementation#56
ravishanigarapu merged 8 commits intodevelopfrom
jwt-api

Conversation

@indraniBan
Copy link
Copy Markdown
Contributor

@indraniBan indraniBan commented Dec 2, 2024

📋 Description

JIRA ID:

Please provide a summary of the change and the motivation behind it. Include relevant context and details.


✅ 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 JWT authentication support with new configuration entries for JWT secret keys across various environments.
    • Added utility classes for managing JWT tokens and cookies, enhancing security and user session management.
    • Implemented a filter for validating JWT tokens in incoming requests.
  • Bug Fixes

    • Minor formatting adjustments made in configuration files for improved readability.
  • Chores

    • Added Redis configuration for caching user data, improving application performance.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 2, 2024

Warning

Rate limit exceeded

@indraniBan has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 17 minutes and 55 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 18a7440 and 12cbd71.

📒 Files selected for processing (4)
  • src/main/environment/common_ci.properties (1 hunks)
  • src/main/java/com/iemr/tm/data/login/Users.java (1 hunks)
  • src/main/java/com/iemr/tm/repo/login/UserLoginRepo.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java (1 hunks)

Walkthrough

This pull request introduces a comprehensive implementation of JWT (JSON Web Token) authentication and authorization for the application. The changes span multiple files, adding dependencies, configuring JWT-related utilities, implementing token validation filters, and setting up Redis caching. The modifications enhance the security and authentication mechanisms by introducing token-based user validation, cookie management, and centralized JWT processing utilities.

Changes

File Change Summary
pom.xml Added JWT-related dependencies: jjwt-api, jjwt-impl, and jjwt-jackson
src/main/environment/*.properties Added jwt.secret configuration across different environment files
src/main/java/com/iemr/tm/data/login/Users.java Refactored with Lombok @Data, implemented Serializable, removed multiple fields
src/main/java/com/iemr/tm/utils/* Added new utility classes for JWT processing: CookieUtil, JwtUtil, JwtAuthenticationUtil, JwtUserIdValidationFilter
src/main/java/com/iemr/tm/config/RedisConfig.java New configuration for Redis caching and serialization
src/main/java/com/iemr/tm/Application.java Added Redis template configuration method

Sequence Diagram

sequenceDiagram
    participant Client
    participant JwtUserIdValidationFilter
    participant JwtAuthenticationUtil
    participant JwtUtil
    participant RedisCache

    Client->>JwtUserIdValidationFilter: HTTP Request
    JwtUserIdValidationFilter->>JwtAuthenticationUtil: Validate JWT Token
    JwtAuthenticationUtil->>JwtUtil: Extract Token Claims
    JwtUtil-->>JwtAuthenticationUtil: Token Claims
    JwtAuthenticationUtil->>RedisCache: Retrieve User
    RedisCache-->>JwtAuthenticationUtil: User Details
    JwtAuthenticationUtil-->>JwtUserIdValidationFilter: Token Valid
    JwtUserIdValidationFilter->>Client: Allow Request
Loading

Poem

🐰 A Rabbit's JWT Ballad 🔐

In tokens we trust, with secrets so bright,
Filtering requests with cryptographic might
Redis caches users, cookies in hand
JWT dances across our digital land
Security hops, with rabbity glee! 🚀


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 generate docstrings to generate docstrings for this PR. (Beta)
  • @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
Contributor

@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: 17

🧹 Outside diff range and nitpick comments (7)
src/main/java/com/iemr/tm/utils/CookieUtil.java (1)

27-27: Fix parameter naming convention

The parameter name 'Jwttoken' doesn't follow Java naming conventions.

-    public void addJwtTokenToCookie(String Jwttoken, HttpServletResponse response) {
+    public void addJwtTokenToCookie(String jwtToken, HttpServletResponse response) {
src/main/java/com/iemr/tm/utils/FilterConfig.java (1)

11-11: Consider adding error handling for JwtAuthenticationUtil initialization

The filter initialization doesn't handle potential errors if JwtAuthenticationUtil fails to initialize properly.

Consider adding validation:

 @Bean
 public FilterRegistrationBean<JwtUserIdValidationFilter> jwtUserIdValidationFilter(JwtAuthenticationUtil jwtAuthenticationUtil) {
+    if (jwtAuthenticationUtil == null) {
+        throw new IllegalStateException("JwtAuthenticationUtil must be configured");
+    }
     FilterRegistrationBean<JwtUserIdValidationFilter> registrationBean = new FilterRegistrationBean<>();
src/main/java/com/iemr/tm/repo/user/UserLoginRepo.java (1)

14-15: LGTM with security considerations

The query correctly filters out deleted users. However, consider the following security improvements:

  1. Add index on userID column if not already present
  2. Consider projecting only necessary fields instead of entire Users entity

Consider this projection approach to limit exposed data:

@Query("SELECT new com.iemr.tm.data.login.UserProjection(u.userID, u.userName, u.statusID) " +
       "FROM Users u WHERE u.userID = :userID AND u.deleted = false")
public UserProjection getUserByUserID(@Param("userID") Long userID);
src/main/java/com/iemr/tm/data/login/Users.java (1)

Based on the analysis of the codebase, I can now provide a final response regarding the JWT token expiry and user status handling:

JWT token expiry and user status validation need attention

  • Token expiry is properly configured (24 hours) and validated in JwtUtil.java
  • However, user status (statusID) validation is missing in the authentication flow
  • The JwtUserIdValidationFilter only validates token presence and format, but doesn't check user status
  • Consider adding user status validation in JwtAuthenticationUtil to prevent deactivated users from accessing the system

Recommendations:

  • Add user status validation in token validation flow
  • Consider implementing token blacklisting for logged out users
  • Add proper error responses for expired/invalid tokens and inactive users
🔗 Analysis chain

Line range hint 1-100: Verify JWT token expiry handling

Since this entity is used with JWT authentication, ensure proper handling of token expiry and user status.


Let me check the JWT and security implementation details to verify the token expiry handling.


Let me check one more aspect regarding user status validation in the authentication flow.


Let me check the implementation of token validation and user status checks in the security filters.


Let me check one final aspect about the token validation and user status checks.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for token expiry handling in JWT related files
rg -l "exp|expiry|expired" --type java

# Check for user status validation in authentication
rg -l "statusID|UserStatus|isActive" --type java

# Look for security configurations
fd -e yml -e properties -e xml | xargs rg -l "jwt|token|security"

Length of output: 1386


Script:

#!/bin/bash
# Check JWT token expiry implementation
rg -A 10 "exp|expiry|expired" src/main/java/com/iemr/tm/utils/JwtUtil.java

# Check user status validation in authentication flow
ast-grep --pattern 'public $_ $_(Users $_) {
  $$$
}'

# Check security configuration properties
cat src/main/environment/common_dev.properties

Length of output: 2909


Script:

#!/bin/bash
# Check authentication implementation for user status validation
rg -A 10 "authenticate|login" --type java

# Check if there's any filter/interceptor that validates tokens
fd -e java | xargs rg -l "Filter|Interceptor|WebSecurityConfigurerAdapter|SecurityFilterChain"

Length of output: 52401


Script:

#!/bin/bash
# Check JWT token validation implementation
rg -A 10 "validateToken|JwtUserIdValidationFilter" src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java

# Check user status validation in authentication
rg -A 10 "statusID|UserStatus|isActive" src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java

# Check security configuration
cat src/main/java/com/iemr/tm/utils/FilterConfig.java

Length of output: 2225


Script:

#!/bin/bash
# Check JwtUtil implementation for token validation
rg -A 15 "validateToken|Claims" src/main/java/com/iemr/tm/utils/JwtUtil.java

# Check if there are any user status checks in authentication flow
rg -A 10 "authenticate|login" src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java

# Check if there are any user status validations in repositories
rg -A 5 "statusID|UserStatus|isActive" src/main/java/com/iemr/tm/repo/user/UserLoginRepo.java

Length of output: 1933

pom.xml (1)

282-301: Consider using dependency management for version control

Extract the version to a property for better maintenance:

 <properties>
+    <jjwt.version>0.11.5</jjwt.version>
 </properties>

 <dependency>
     <groupId>io.jsonwebtoken</groupId>
     <artifactId>jjwt-api</artifactId>
-    <version>0.11.5</version>
+    <version>${jjwt.version}</version>
 </dependency>
src/main/java/com/iemr/tm/utils/JwtUtil.java (1)

21-21: Make EXPIRATION_TIME configurable.

Instead of hardcoding the token expiration time, consider making EXPIRATION_TIME configurable through application properties to allow flexibility across different environments.

src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java (1)

72-75: Consider redirecting unauthorized requests.

Instead of sending an error response, consider redirecting to a login page or an error handling endpoint to enhance user experience.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between fec827f and c6193b1.

📒 Files selected for processing (12)
  • pom.xml (1 hunks)
  • src/main/environment/common_ci.properties (1 hunks)
  • src/main/environment/common_dev.properties (1 hunks)
  • src/main/environment/common_test.properties (1 hunks)
  • src/main/environment/common_uat.properties (1 hunks)
  • src/main/java/com/iemr/tm/data/login/Users.java (1 hunks)
  • src/main/java/com/iemr/tm/repo/user/UserLoginRepo.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/CookieUtil.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/FilterConfig.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/JwtUtil.java (1 hunks)
🔇 Additional comments (7)
src/main/java/com/iemr/tm/utils/CookieUtil.java (2)

1-13: LGTM! Well-structured class setup

The class is properly organized with appropriate imports and Spring service annotation.


1-45: Verify consistency of JWT cookie handling across the codebase

Let's ensure this implementation aligns with other JWT cookie handling in the codebase.

✅ Verification successful

Let me check the JwtUserIdValidationFilter and JwtUtil implementations to ensure complete consistency.


JWT cookie handling implementation is consistent and secure

The implementation in CookieUtil.java aligns well with the rest of the codebase. Key findings:

  • Cookie security settings are consistently applied (httpOnly and secure flags)
  • Same cookie name "Jwttoken" is used across implementations
  • Cookie path "/" is consistently set
  • Proper JWT token validation is in place via JwtUtil
  • Clear separation of concerns between CookieUtil (cookie management), JwtUtil (token operations), and JwtUserIdValidationFilter (request filtering)
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other JWT cookie implementations
echo "Searching for other cookie implementations..."
rg -l "new Cookie.*[\"']Jwttoken[\"']" --type java

echo "Searching for other JWT-related utilities..."
rg -l "class.*JwtUtil|class.*TokenUtil" --type java

echo "Searching for other cookie configurations..."
rg "cookie\.set(HttpOnly|Secure|Path|MaxAge)" --type java

Length of output: 1274


Script:

#!/bin/bash
# Check JwtUserIdValidationFilter implementation
echo "=== JwtUserIdValidationFilter Implementation ==="
cat src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java

echo -e "\n=== JwtUtil Implementation ==="
cat src/main/java/com/iemr/tm/utils/JwtUtil.java

Length of output: 6951

src/main/java/com/iemr/tm/utils/FilterConfig.java (3)

1-6: LGTM: Imports are appropriate and minimal

The necessary Spring imports for filter registration and configuration are present.


7-8: LGTM: Proper Spring configuration class setup

The class is correctly annotated with @Configuration for Spring bean configuration.


1-18: Verify JWT configuration across environments

Since this is part of a larger JWT implementation, ensure that the JWT secret keys are properly configured across all environments.

Let's verify the JWT configuration:

src/main/java/com/iemr/tm/utils/JwtUtil.java (1)

19-19: Consider securing the SECRET_KEY management.

Storing the SECRET_KEY as a plain string could pose security risks. It's advisable to load the secret key from a secure configuration or environment variable, and ensure it's not checked into version control.

src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java (1)

62-90: 🛠️ Refactor suggestion

Ensure proper exception handling in validateUserIdAndJwtToken.

Catching generic Exception may obscure specific issues. Catch specific exceptions and handle them appropriately.

Apply this diff to enhance exception handling:

 try {
     // existing code...
 } catch (IEMRException e) {
     logger.error("Validation failed: " + e.getMessage(), e);
     throw e;
-} catch (Exception e) {
-    logger.error("Validation failed: " + e.getMessage(), e);
-    throw new IEMRException("Validation error: " + e.getMessage(), e);
 }

Likely invalid or redundant comment.

Comment on lines +37 to +43
public String getJwtTokenFromCookie(HttpServletRequest request) {
return Arrays.stream(request.getCookies())
.filter(cookie -> "Jwttoken".equals(cookie.getName()))
.map(Cookie::getValue)
.findFirst()
.orElse(null);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix potential NullPointerException and improve consistency

The method needs null checks and should use the constant for cookie name:

     public String getJwtTokenFromCookie(HttpServletRequest request) {
+        if (request == null) {
+            throw new IllegalArgumentException("Request cannot be null");
+        }
+        Cookie[] cookies = request.getCookies();
+        if (cookies == null) {
+            return null;
+        }
-        return Arrays.stream(request.getCookies())
-                     .filter(cookie -> "Jwttoken".equals(cookie.getName()))
+        return Arrays.stream(cookies)
+                     .filter(cookie -> JWT_COOKIE_NAME.equals(cookie.getName()))
                      .map(Cookie::getValue)
                      .findFirst()
                      .orElse(null);
     }

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +15 to +25
public Optional<String> getCookieValue(HttpServletRequest request, String cookieName) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookieName.equals(cookie.getName())) {
return Optional.of(cookie.getValue());
}
}
}
return Optional.empty();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add parameter validation for robustness

While the implementation is functional, consider adding parameter validation:

  1. Check if request is null
  2. Validate cookieName is not null or empty
 public Optional<String> getCookieValue(HttpServletRequest request, String cookieName) {
+    if (request == null) {
+        throw new IllegalArgumentException("Request cannot be null");
+    }
+    if (cookieName == null || cookieName.trim().isEmpty()) {
+        throw new IllegalArgumentException("Cookie name cannot be null or empty");
+    }
     Cookie[] cookies = request.getCookies();
     if (cookies != null) {
         for (Cookie cookie : cookies) {
📝 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 Optional<String> getCookieValue(HttpServletRequest request, String cookieName) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookieName.equals(cookie.getName())) {
return Optional.of(cookie.getValue());
}
}
}
return Optional.empty();
}
public Optional<String> getCookieValue(HttpServletRequest request, String cookieName) {
if (request == null) {
throw new IllegalArgumentException("Request cannot be null");
}
if (cookieName == null || cookieName.trim().isEmpty()) {
throw new IllegalArgumentException("Cookie name cannot be null or empty");
}
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookieName.equals(cookie.getName())) {
return Optional.of(cookie.getValue());
}
}
}
return Optional.empty();
}

Comment on lines +27 to +35
public void addJwtTokenToCookie(String Jwttoken, HttpServletResponse response) {
// Create a new cookie with the JWT token
Cookie cookie = new Cookie("Jwttoken", Jwttoken);
cookie.setHttpOnly(true); // Prevent JavaScript access for security
cookie.setSecure(true); // Ensure the cookie is sent only over HTTPS
cookie.setMaxAge(60 * 60 * 24); // 1 day expiration time
cookie.setPath("/"); // Make the cookie available for the entire application
response.addCookie(cookie); // Add the cookie to the response
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add security attributes and improve parameter validation

The cookie configuration needs additional security measures and parameter validation:

  1. Add SameSite attribute to prevent CSRF attacks
  2. Validate JWT token parameter
  3. Extract cookie name as a constant
+    private static final String JWT_COOKIE_NAME = "Jwttoken";
+
     public void addJwtTokenToCookie(String Jwttoken, HttpServletResponse response) {
+        if (Jwttoken == null || Jwttoken.trim().isEmpty()) {
+            throw new IllegalArgumentException("JWT token cannot be null or empty");
+        }
+        if (response == null) {
+            throw new IllegalArgumentException("Response cannot be null");
+        }
         // Create a new cookie with the JWT token
-        Cookie cookie = new Cookie("Jwttoken", Jwttoken);
+        Cookie cookie = new Cookie(JWT_COOKIE_NAME, Jwttoken);
         cookie.setHttpOnly(true); // Prevent JavaScript access for security
         cookie.setSecure(true);   // Ensure the cookie is sent only over HTTPS
         cookie.setMaxAge(60 * 60 * 24); // 1 day expiration time
         cookie.setPath("/");      // Make the cookie available for the entire application
+        cookie.setAttribute("SameSite", "Strict"); // Prevent CSRF attacks
         response.addCookie(cookie); // Add the cookie to the response
     }
📝 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 void addJwtTokenToCookie(String Jwttoken, HttpServletResponse response) {
// Create a new cookie with the JWT token
Cookie cookie = new Cookie("Jwttoken", Jwttoken);
cookie.setHttpOnly(true); // Prevent JavaScript access for security
cookie.setSecure(true); // Ensure the cookie is sent only over HTTPS
cookie.setMaxAge(60 * 60 * 24); // 1 day expiration time
cookie.setPath("/"); // Make the cookie available for the entire application
response.addCookie(cookie); // Add the cookie to the response
}
private static final String JWT_COOKIE_NAME = "Jwttoken";
public void addJwtTokenToCookie(String Jwttoken, HttpServletResponse response) {
if (Jwttoken == null || Jwttoken.trim().isEmpty()) {
throw new IllegalArgumentException("JWT token cannot be null or empty");
}
if (response == null) {
throw new IllegalArgumentException("Response cannot be null");
}
// Create a new cookie with the JWT token
Cookie cookie = new Cookie(JWT_COOKIE_NAME, Jwttoken);
cookie.setHttpOnly(true); // Prevent JavaScript access for security
cookie.setSecure(true); // Ensure the cookie is sent only over HTTPS
cookie.setMaxAge(60 * 60 * 24); // 1 day expiration time
cookie.setPath("/"); // Make the cookie available for the entire application
cookie.setAttribute("SameSite", "Strict"); // Prevent CSRF attacks
response.addCookie(cookie); // Add the cookie to the response
}

Comment on lines +10 to +16
@Bean
public FilterRegistrationBean<JwtUserIdValidationFilter> jwtUserIdValidationFilter(JwtAuthenticationUtil jwtAuthenticationUtil) {
FilterRegistrationBean<JwtUserIdValidationFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new JwtUserIdValidationFilter(jwtAuthenticationUtil));
registrationBean.addUrlPatterns("/*"); // Apply filter to all API endpoints
return registrationBean;
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Dec 2, 2024

Choose a reason for hiding this comment

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

💡 Codebase verification

🛠️ Refactor suggestion

Consider excluding Swagger UI and health check endpoints from JWT validation

Based on the codebase analysis, while the filter order suggestion is valid, we should specifically exclude Swagger UI and actuator endpoints from JWT validation. The application uses Spring Boot Actuator and Swagger/OpenAPI for documentation, which typically need to be publicly accessible.

 @Bean
 public FilterRegistrationBean<JwtUserIdValidationFilter> jwtUserIdValidationFilter(JwtAuthenticationUtil jwtAuthenticationUtil) {
     FilterRegistrationBean<JwtUserIdValidationFilter> registrationBean = new FilterRegistrationBean<>();
     registrationBean.setFilter(new JwtUserIdValidationFilter(jwtAuthenticationUtil));
-    registrationBean.addUrlPatterns("/*");  // Apply filter to all API endpoints
+    registrationBean.addUrlPatterns("/api/*");  // Apply only to API endpoints
+    registrationBean.setOrder(1);  // Set explicit order
+    registrationBean.addInitParameter("excludePatterns", "/swagger-ui/*,/v3/api-docs/*,/actuator/*");
     return registrationBean;
 }
  • The application uses Swagger UI (paths: /swagger-ui/*, /v3/api-docs/*)
  • Spring Boot Actuator is included in dependencies (paths: /actuator/*)
  • These endpoints should be excluded from JWT validation to remain accessible
🔗 Analysis chain

Consider filter order and URL pattern exclusions

While the implementation is functionally correct, there are several considerations:

  1. The filter is applied to all endpoints ("/*"), which might not be desirable for certain paths (e.g., public endpoints, health checks, or Swagger documentation).
  2. The filter order is not explicitly set, which could lead to unexpected behavior with other filters.

Consider these improvements:

 @Bean
 public FilterRegistrationBean<JwtUserIdValidationFilter> jwtUserIdValidationFilter(JwtAuthenticationUtil jwtAuthenticationUtil) {
     FilterRegistrationBean<JwtUserIdValidationFilter> registrationBean = new FilterRegistrationBean<>();
     registrationBean.setFilter(new JwtUserIdValidationFilter(jwtAuthenticationUtil));
-    registrationBean.addUrlPatterns("/*");  // Apply filter to all API endpoints
+    registrationBean.addUrlPatterns("/api/*");  // Apply only to API endpoints
+    registrationBean.setOrder(1);  // Set explicit order
+    registrationBean.addInitParameter("excludePatterns", "/api/public/*,/api/health,/api/swagger-ui/*");
     return registrationBean;
 }

Let's verify if there are any public endpoints or health checks that should be excluded:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for potential public endpoints or health check controllers
ast-grep --pattern 'class $_ {
  $$$
  @GetMapping("/health")
  $$$
}'

# Search for Swagger configuration
ast-grep --pattern '@EnableSwagger2'

# Search for public endpoint annotations
rg -l "@PreAuthorize|@Secured|@PermitAll" 

Length of output: 153


Script:

#!/bin/bash
# Let's try a broader search for endpoints and configurations

# Search for controller endpoints
rg -A 2 "@(Get|Post|Put|Delete|Patch)Mapping"

# Search for security configurations
rg -A 5 "WebSecurityConfigurerAdapter|SecurityFilterChain"

# Search for Swagger/OpenAPI configurations
rg -A 3 "swagger|openapi|@OpenAPIDefinition"

# Search for any filter configurations
rg -A 3 "FilterRegistrationBean|@Order.*Filter"

Length of output: 82551


Script:

#!/bin/bash
# Let's search for public endpoints and security configurations

# Search for any security configuration files
rg -l "SecurityConfig|WebSecurityConfig"

# Search for any public/open endpoints
rg -A 2 "@Public|permitAll|anonymous"

# Search for any health check endpoints
rg -A 2 "health|healthcheck|actuator"

# Search for any Swagger/API documentation configurations
rg -A 2 "swagger|openapi|SwaggerConfig"

# Search for filter order configurations
rg -A 2 "@Order|setOrder|FilterRegistrationBean"

Length of output: 22283

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

not required

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@sandipkarmakar3 Understood, I'll keep the filter configuration as is.

import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Dec 2, 2024

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add validation and security measures for sensitive fields

The entity contains sensitive PII data (Aadhaar, PAN) and credentials without proper validation or security measures.

Recommended improvements:

  1. Add field validation:
@Column(name = "Password")
@JsonIgnore // Remove @Expose
private String password;

@Column(name = "AadhaarNo")
@Pattern(regexp = "^[0-9]{12}$")
@Convert(converter = AttributeEncryptor.class)
private String aadhaarNo;

@Column(name = "PAN")
@Pattern(regexp = "^[A-Z]{5}[0-9]{4}[A-Z]{1}$")
@Convert(converter = AttributeEncryptor.class)
private String pan;
  1. Implement field-level encryption for sensitive data:
@Component
public class AttributeEncryptor implements AttributeConverter<String, String> {
    // Implement encryption/decryption using your security key
}

Also applies to: 33-33, 34-34, 35-35, 36-36, 37-37, 38-38

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

not required

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@sandipkarmakar3 Understood, I see that the sensitive fields have been removed from the Users class, making the validation suggestions unnecessary.


✏️ Learnings added
Learnt from: sandipkarmakar3
PR: PSMRI/TM-API#56
File: src/main/java/com/iemr/tm/data/login/Users.java:34-34
Timestamp: 2025-02-03T12:50:15.717Z
Learning: The Users class in TM-API has been simplified to only contain basic fields (userID, userName, Deleted) with sensitive PII data fields intentionally removed.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +54 to +55
logger.info("JWT token from header: " + jwtTokenFromHeader);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid logging sensitive information.

Logging the JWT token can lead to security vulnerabilities. Remove or mask sensitive data in logs.

Apply this diff:

-logger.info("JWT token from header: " + jwtTokenFromHeader);
+logger.info("JWT token from header received.");
📝 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.info("JWT token from header: " + jwtTokenFromHeader);
logger.info("JWT token from header received.");

Comment thread src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java Outdated
Comment thread src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java Outdated

### Redis IP
spring.redis.host=localhost
jwt.secret=<Enter_Your_Secret_Key>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure the jwt.secret is securely managed.

Replace <Enter_Your_Secret_Key> with a secure, environment-specific value. Ensure that this key is managed securely and not exposed in version control.

prescription=TMPrescription SMS
### Redis IP
spring.redis.host=localhost
jwt.secret=<Enter_Your_Secret_Key>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure the jwt.secret is securely managed.

Replace <Enter_Your_Secret_Key> with a secure, environment-specific value. Ensure that this key is managed securely and not exposed in version control.

Copy link
Copy Markdown
Contributor

@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: 3

🧹 Outside diff range and nitpick comments (2)
src/main/java/com/iemr/tm/utils/JwtUtil.java (1)

18-19: Follow Java naming conventions for private fields

Private instance variables should be named using camelCase according to Java naming conventions. Consider renaming SECRET_KEY to secretKey for consistency.

Apply this diff:

-    @Value("${jwt.secret}")
-    private String SECRET_KEY;
+    @Value("${jwt.secret}")
+    private String secretKey;

Don't forget to update all references to SECRET_KEY within the class.

src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java (1)

87-89: Avoid catching generic Exception

Catching the generic Exception may mask other unintended exceptions and make debugging harder. Consider catching more specific exceptions that you expect could be thrown within the try block.

Apply this diff:

 } catch (Exception e) {
     logger.error("Validation failed: " + e.getMessage(), e);
     throw new IEMRException("Validation error: " + e.getMessage(), e);
 }

Identify and catch specific exceptions, such as ExpiredJwtException, MalformedJwtException, etc.

} catch (ExpiredJwtException e) {
    logger.error("Token has expired", e);
    throw new IEMRException("Token has expired", e);
} catch (MalformedJwtException e) {
    logger.error("Invalid token", e);
    throw new IEMRException("Invalid JWT Token", e);
} catch (Exception e) {
    logger.error("Validation failed: " + e.getMessage(), e);
    throw new IEMRException("Validation error: " + e.getMessage(), e);
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c6193b1 and 0f43112.

📒 Files selected for processing (6)
  • pom.xml (8 hunks)
  • src/main/java/com/iemr/tm/utils/CookieUtil.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/FilterConfig.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/JwtUtil.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • pom.xml
  • src/main/java/com/iemr/tm/utils/FilterConfig.java
  • src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java
  • src/main/java/com/iemr/tm/utils/CookieUtil.java
🔇 Additional comments (3)
src/main/java/com/iemr/tm/utils/JwtUtil.java (1)

43-49: ⚠️ Potential issue

Improve exception handling in validateToken method

Catching all exceptions and returning null may obscure specific token validation issues and complicate debugging. It's better to catch specific exceptions like ExpiredJwtException, MalformedJwtException, and handle them appropriately, perhaps by logging detailed error messages or rethrowing them.

Apply this diff to enhance exception handling:

 public Claims validateToken(String token) {
     try {
-        return Jwts.parser().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();
+        return Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();
     } catch (ExpiredJwtException e) {
+        logger.error("Token has expired", e);
+        throw e;
+    } catch (MalformedJwtException e) {
+        logger.error("Invalid token", e);
+        throw e;
+    } catch (Exception e) {
+        logger.error("Token validation error", e);
+        throw new RuntimeException("Token validation error", e);
     }
 }

Ensure you have a logger instance in your class:

private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java (2)

22-26: 🛠️ Refactor suggestion

Inconsistent dependency injection

Fields cookieUtil and jwtUtil are annotated with @Autowired and also initialized via constructor injection. This redundancy may cause conflicts and is not considered best practice. Choose one method of dependency injection to maintain consistency—preferably constructor injection for mandatory dependencies.

Apply this diff to resolve the inconsistency:

-    @Autowired
-    private final CookieUtil cookieUtil;
-    @Autowired
-    private final JwtUtil jwtUtil;
+    private final CookieUtil cookieUtil;
+    private final JwtUtil jwtUtil;

Ensure that your constructor remains the same for constructor injection:

@Autowired
public JwtAuthenticationUtil(CookieUtil cookieUtil, JwtUtil jwtUtil) {
    this.cookieUtil = cookieUtil;
    this.jwtUtil = jwtUtil;
}

35-60: 🛠️ Refactor suggestion

Refactor validateJwtToken to separate concerns

Returning ResponseEntity<String> from a utility class couples it with the web layer, which goes against the principle of separation of concerns. Utility classes should be independent of HTTP-specific implementations. Consider refactoring this method to return the username or throw a custom exception upon validation failure. Handle HTTP responses in the controller layer instead.

Here's how you might refactor the method:

-public ResponseEntity<String> validateJwtToken(HttpServletRequest request) {
+public String validateJwtToken(HttpServletRequest request) throws IEMRException {
     Optional<String> jwtTokenOpt = cookieUtil.getCookieValue(request, "Jwttoken");

     if (jwtTokenOpt.isEmpty()) {
-        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
-                .body("Error 401: Unauthorized - JWT Token is not set!");
+        throw new IEMRException("JWT Token is not set!");
     }

     String jwtToken = jwtTokenOpt.get();

     // Validate the token
     Claims claims = jwtUtil.validateToken(jwtToken);
     if (claims == null) {
-        return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Error 401: Unauthorized - Invalid JWT Token!");
+        throw new IEMRException("Invalid JWT Token!");
     }

     // Extract username from token
     String usernameFromToken = claims.getSubject();
     if (usernameFromToken == null || usernameFromToken.isEmpty()) {
-        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
-                .body("Error 401: Unauthorized - Username is missing!");
+        throw new IEMRException("Username is missing in JWT Token!");
     }

     // Return the username if valid
-    return ResponseEntity.ok(usernameFromToken);
+    return usernameFromToken;
 }

Adjust your controllers to handle the IEMRException and return appropriate HTTP responses.

Comment thread src/main/java/com/iemr/tm/utils/JwtUtil.java
// Validate and parse JWT Token
public Claims validateToken(String token) {
try {
return Jwts.parser().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Dec 5, 2024

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use parserBuilder() instead of deprecated parser() method

The Jwts.parser() method is deprecated in recent versions of the jjwt library. Replace it with Jwts.parserBuilder() to avoid deprecation warnings and ensure future compatibility.

Apply this diff:

-return Jwts.parser().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();
+return Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();
📝 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
return Jwts.parser().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();
return Jwts.parserBuilder().setSigningKey(getSigningKey()).build().parseClaimsJws(token).getBody();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@indraniBan can you address this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java

### Redis IP
spring.redis.host=localhost
jwt.secret=<Enter_Your_Secret_Key>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Use @JWT_SECRET_KEY@

Copy link
Copy Markdown
Contributor

@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: 0

🧹 Outside diff range and nitpick comments (1)
src/main/java/com/iemr/tm/data/login/Users.java (1)

Password field requires security enhancements

The codebase analysis reveals that the password field lacks essential security measures:

  1. The password is stored as plain text without encryption
  2. No password encoder/encryption utility is implemented
  3. The @expose annotation could lead to accidental password exposure in serialization
  4. No validation constraints are enforced on password strength

Required changes:

  • Remove @expose annotation
  • Add @JsonIgnore to prevent serialization
  • Implement a proper password encoder (BCrypt recommended)
  • Add password strength validation
  • Store encrypted passwords only
🔗 Analysis chain

Line range hint 65-67: Enhance password field security

The password field requires additional security measures:

  1. Remove @expose to prevent accidental serialization
  2. Add encryption for the stored password
  3. Add validation constraints
-@Expose
@Column(name = "Password")
+@JsonIgnore
+@Convert(converter = PasswordEncryptor.class)
+@Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}$",
+        message = "Password must be at least 8 characters long and contain at least one digit, one uppercase, one lowercase, and one special character")
private String password;

Let's verify if password encryption is implemented elsewhere:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for password encryption implementation
ast-grep --pattern 'class $_ implements AttributeConverter<String, String>'

# Search for password-related security configurations
rg -l "password|encrypt|security" --type yaml --type properties

Length of output: 178


Script:

#!/bin/bash
# Search for password encryption or security related classes
rg -l "password|encrypt|security" --type java

# Search for password field usage and annotations
ast-grep --pattern '@Column(name = "Password")'

# Search for any existing security configurations
fd -e yml -e yaml -e properties -x cat {}

Length of output: 11083


Script:

#!/bin/bash
# Let's check the Users class implementation for password handling
rg -A 10 -B 10 "Password" src/main/java/com/iemr/tm/data/login/Users.java

# Check for any security related configurations in Spring Security
rg -l "WebSecurityConfigurerAdapter|SecurityFilterChain|PasswordEncoder" --type java

# Check for any encryption utilities
rg -l "encrypt|decrypt|BCrypt|SCrypt|PBKDF2" --type java

Length of output: 682

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 0f43112 and 59be63d.

📒 Files selected for processing (7)
  • src/main/environment/common_ci.properties (1 hunks)
  • src/main/environment/common_dev.properties (1 hunks)
  • src/main/environment/common_test.properties (1 hunks)
  • src/main/environment/common_uat.properties (1 hunks)
  • src/main/java/com/iemr/tm/data/login/Users.java (2 hunks)
  • src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/main/environment/common_dev.properties
  • src/main/environment/common_uat.properties
  • src/main/environment/common_ci.properties
  • src/main/environment/common_test.properties
  • src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java
  • src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java
🔇 Additional comments (2)
src/main/java/com/iemr/tm/data/login/Users.java (2)

32-32: Security concern: Replace @DaTa with specific Lombok annotations

Using @DaTa on an entity containing sensitive information (password) is risky as it generates toString() that could expose this data in logs.

Replace with specific annotations:

-@Data
+@Getter
+@Setter
+@EqualsAndHashCode(exclude = {"password"})
+@ToString(exclude = {"password"})

Also applies to: 37-37


46-47: LGTM!

The formatting change is purely cosmetic and doesn't affect functionality.

Comment thread src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java
Comment thread src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java Outdated
Comment thread src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java Outdated
public interface UserLoginRepo extends CrudRepository<Users, Long> {

@Query(" SELECT u FROM Users u WHERE u.userID = :userID AND u.deleted = false ")
public Users getUserByUserID(@Param("userID") Long userID);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For every request that comes though there will be SQL query against a table. Why do this?
This data should be ideally cached in Redis. If not present in Redis, make the query.

@indraniBan

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

instead of redis changed to inbuilt query

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@indraniBan sorry, what does inbuilt query mean?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@drtechie implemented redis concept, fetching from redis. but need to do one round testing from UI, if working fine will commit the code

Comment thread src/main/java/com/iemr/tm/utils/CookieUtil.java Outdated

### Redis IP
spring.redis.host=localhost
jwt.secret=@JWT_SECRET_KEY@
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

use env.JWT_SECRET_KEY
check how it's added in other places.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

Comment thread src/main/environment/common_dev.properties Outdated
Copy link
Copy Markdown
Contributor

@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: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59be63d and 4cf1c43.

📒 Files selected for processing (7)
  • src/main/environment/common_ci.properties (1 hunks)
  • src/main/java/com/iemr/tm/data/login/Users.java (1 hunks)
  • src/main/java/com/iemr/tm/repo/user/UserLoginRepo.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/CookieUtil.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/JwtUtil.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/main/environment/common_ci.properties
  • src/main/java/com/iemr/tm/utils/CookieUtil.java
  • src/main/java/com/iemr/tm/utils/JwtUtil.java
  • src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java
  • src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java
🔇 Additional comments (3)
src/main/java/com/iemr/tm/repo/user/UserLoginRepo.java (1)

8-11: Consider implementing caching for user lookups

For JWT validation, this repository will be queried frequently. Implementing caching would significantly improve performance.

Consider implementing Redis caching:

@Repository
@CacheConfig(cacheNames = "users")
public interface UserLoginRepo extends CrudRepository<Users, Long> {
    @Cacheable(key = "#userID")
    Users findByUserID(Long userID);
}
src/main/java/com/iemr/tm/data/login/Users.java (2)

33-33: ⚠️ Potential issue

Security concern: Replace @DaTa with specific Lombok annotations

Using @DaTa on an entity containing sensitive information is risky as it generates toString() that could expose this data in logs.

Replace with specific annotations:

-@Data
+@Getter
+@Setter
+@EqualsAndHashCode(exclude = {"password"})
+@ToString(exclude = {"password"})

36-42: ⚠️ Potential issue

Add validation and security measures for user fields

The entity lacks proper validation and security measures for user data.

Add field validation and security annotations:

 @Column(name = "UserID")
 private Long userID;
+
 @Column(name = "UserName")
+@NotBlank(message = "Username is required")
+@Size(min = 3, max = 50, message = "Username must be between 3 and 50 characters")
 private String userName;

Likely invalid or redundant comment.

Comment thread src/main/java/com/iemr/tm/data/login/Users.java Outdated
Copy link
Copy Markdown
Contributor

@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: 3

♻️ Duplicate comments (4)
src/main/java/com/iemr/tm/data/login/Users.java (1)

37-37: ⚠️ Potential issue

Security concerns with @DaTa and missing validations

The current implementation has several security concerns:

  1. @DaTa generates toString() that could expose sensitive data
  2. Missing proper validation annotations
  3. Missing security annotations for sensitive fields

Previous review comments about replacing @DaTa with specific Lombok annotations and adding validation remain valid. Additionally:

-@Data
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@ToString(exclude = {"password"})
+@EqualsAndHashCode(of = "userID")
 @Table(name = "m_user")
 @JsonIgnoreProperties(ignoreUnknown = true)
+@EntityListeners(AuditingEntityListener.class)
 public class Users implements Serializable {

Also applies to: 39-40

src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java (3)

23-34: ⚠️ Potential issue

Fix inconsistent dependency injection pattern.

The class uses both constructor injection and field injection for the same dependencies, which could lead to unexpected behavior.

Apply this diff to standardize the dependency injection:

-    @Autowired
-    private CookieUtil cookieUtil;
-    @Autowired
-    private JwtUtil jwtUtil;
     @Autowired
     private RedisTemplate<String, Object> redisTemplate;
 
-    public JwtAuthenticationUtil(CookieUtil cookieUtil, JwtUtil jwtUtil) {
+    public JwtAuthenticationUtil(CookieUtil cookieUtil, JwtUtil jwtUtil, RedisTemplate<String, Object> redisTemplate) {
         this.cookieUtil = cookieUtil;
         this.jwtUtil = jwtUtil;
+        this.redisTemplate = redisTemplate;
     }

36-61: 🛠️ Refactor suggestion

Improve separation of concerns in validateJwtToken.

The method currently handles both token validation and HTTP response creation. Consider:

  1. Moving HTTP response handling to the controller layer
  2. Extracting the cookie name to a constant or configuration

Apply this diff:

+    private static final String JWT_COOKIE_NAME = "Jwttoken";
+
-    public ResponseEntity<String> validateJwtToken(HttpServletRequest request) {
+    public String validateJwtToken(HttpServletRequest request) throws IEMRException {
-        Optional<String> jwtTokenOpt = cookieUtil.getCookieValue(request, "Jwttoken");
+        Optional<String> jwtTokenOpt = cookieUtil.getCookieValue(request, JWT_COOKIE_NAME);
 
         if (jwtTokenOpt.isEmpty()) {
-            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
-                    .body("Error 401: Unauthorized - JWT Token is not set!");
+            throw new IEMRException("JWT Token is not set!");
         }
 
         String jwtToken = jwtTokenOpt.get();
 
         Claims claims = jwtUtil.validateToken(jwtToken);
         if (claims == null) {
-            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Error 401: Unauthorized - Invalid JWT Token!");
+            throw new IEMRException("Invalid JWT Token!");
         }
 
         String usernameFromToken = claims.getSubject();
         if (usernameFromToken == null || usernameFromToken.isEmpty()) {
-            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
-                    .body("Error 401: Unauthorized - Username is missing!");
+            throw new IEMRException("Username is missing!");
         }
 
-        return ResponseEntity.ok(usernameFromToken);
+        return usernameFromToken;
     }

63-85: ⚠️ Potential issue

Enhance error handling and validation in validateUserIdAndJwtToken.

  1. Add null check for userId claim
  2. Catch specific exceptions instead of generic Exception

Apply this diff:

     public boolean validateUserIdAndJwtToken(String jwtToken) throws IEMRException {
         try {
             Claims claims = jwtUtil.validateToken(jwtToken);
 
             if (claims == null) {
                 throw new IEMRException("Invalid JWT token.");
             }
 
             String userId = claims.get("userId", String.class);
+            if (userId == null || userId.isEmpty()) {
+                throw new IEMRException("User ID is missing in JWT Token.");
+            }
 
             Users user = getUserFromCache(userId);
             if (user == null) {
                 throw new IEMRException("Invalid User ID.");
             }
 
             return true;
-        } catch (Exception e) {
+        } catch (io.jsonwebtoken.JwtException e) {
             logger.error("Validation failed: " + e.getMessage(), e);
-            throw new IEMRException("Validation error: " + e.getMessage(), e);
+            throw new IEMRException("JWT validation error: " + e.getMessage(), e);
+        } catch (Exception e) {
+            logger.error("Unexpected error during validation: " + e.getMessage(), e);
+            throw new IEMRException("Validation failed due to system error", e);
         }
     }
🧹 Nitpick comments (1)
src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java (1)

87-98: Improve Redis cache configuration and handling.

Consider the following improvements:

  1. Move Redis key prefix to configuration
  2. Add TTL handling for cache entries
  3. Add more specific logging details

Apply this diff:

+    private static final String REDIS_USER_PREFIX = "user_";
+
     private Users getUserFromCache(String userId) {
-        String redisKey = "user_" + userId; // The Redis key format
+        String redisKey = REDIS_USER_PREFIX + userId;
         Users user = (Users) redisTemplate.opsForValue().get(redisKey);
 
         if (user == null) {
-            logger.warn("User not found in Redis.");
+            logger.warn("User with ID {} not found in Redis cache", userId);
         } else {
-            logger.info("User fetched successfully from Redis.");
+            logger.debug("User with ID {} fetched successfully from Redis cache", userId);
+            // Refresh TTL if needed
+            redisTemplate.expire(redisKey, 24, TimeUnit.HOURS);
         }
 
         return user;
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dfbe41e and 18a7440.

📒 Files selected for processing (5)
  • src/main/environment/common_ci.properties (1 hunks)
  • src/main/java/com/iemr/tm/Application.java (2 hunks)
  • src/main/java/com/iemr/tm/config/RedisConfig.java (1 hunks)
  • src/main/java/com/iemr/tm/data/login/Users.java (1 hunks)
  • src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/environment/common_ci.properties
🔇 Additional comments (1)
src/main/java/com/iemr/tm/config/RedisConfig.java (1)

19-21: Review security implications of NO_OP configuration

Using ConfigureRedisAction.NO_OP disables Redis configuration commands. While this might be intentional for cloud environments, ensure this aligns with your security requirements and Redis deployment model.

✅ Verification successful

Redis configuration is appropriately secured for cloud environments

The use of ConfigureRedisAction.NO_OP is actually a recommended practice in cloud environments where Redis configuration is managed externally. The configuration is secure because:

  1. Redis is configured with authentication (password field present in properties)
  2. Redis is configured to run locally (host=localhost) in all environments
  3. The application uses Spring Session with Redis, which requires minimal Redis commands
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if Redis is configured in protected mode in properties files
rg -l "redis.*protected-mode" 

Length of output: 31


Script:

#!/bin/bash
# Let's check for Redis configuration properties and deployment settings
rg -l "redis" --type properties --type yaml --type yml

# Also check if there are any Redis security configurations or documentation
rg -i "redis.*security|redis.*config" --type md --type properties --type yaml --type yml -A 3

Length of output: 220


Script:

#!/bin/bash
# Search for any Redis configuration files with common extensions
fd -e properties -e yml -e yaml -e xml | xargs rg -l "redis"

# Look for Spring configuration files that might contain Redis settings
fd -e properties -e yml -e yaml -e xml | xargs rg -l "spring.*redis"

# Check for any Redis-related security configurations in the codebase
rg -i "redis.*(security|password|protected|auth)" -g '!{*.class,*.jar}'

Length of output: 714


Script:

#!/bin/bash
# Let's examine the Redis configuration in application.properties and environment files
cat src/main/resources/application.properties
cat src/main/environment/common_*.properties

# Also check if there are any Redis-specific security configurations in the Spring configuration class
rg -A 5 "class RedisConfig" src/main/java/com/iemr/tm/config/RedisConfig.java

Length of output: 10697

Comment thread src/main/java/com/iemr/tm/config/RedisConfig.java
Comment thread src/main/java/com/iemr/tm/Application.java
Comment on lines +46 to +53
// Validate the token
Claims claims = jwtUtil.validateToken(jwtToken);
if (claims == null) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Error 401: Unauthorized - Invalid JWT Token!");
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codebase verification

⚠️ Potential issue

Based on the gathered evidence, I can now provide the final response:

Current JWT validation implementation needs improvement

The JWT validation in JwtUtil.validateToken() has several security gaps:

  • It silently returns null for all validation errors without distinguishing between different failure cases
  • Uses HMAC-SHA signing but doesn't explicitly verify the algorithm
  • No explicit expiration time validation is implemented
  • Exception handling is too broad and masks specific security issues

Key locations:

  • src/main/java/com/iemr/tm/utils/JwtUtil.java: Implement specific checks in validateToken() method
  • src/main/java/com/iemr/tm/utils/JwtAuthenticationUtil.java: Add specific error handling for different validation failures
🔗 Analysis chain

Enhance JWT security validation.

The current token validation should explicitly check:

  1. Token expiration
  2. Token signature algorithm
  3. Token issuer (if applicable)

Let me check the JWT signing key configuration and algorithm settings.


Let me check for token generation and expiration settings.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for JWT security configurations
rg -A 5 "validateToken|jwtUtil" src/

Length of output: 2858


Script:

#!/bin/bash
# Check for JWT signing key and algorithm configurations
rg -A 5 "getSigningKey|setSigningKey|SignatureAlgorithm" src/

Length of output: 1655


Script:

#!/bin/bash
# Check for JWT token generation and expiration settings
rg -A 10 "generateToken|setExpiration|getExpiration" src/

Length of output: 59

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown
Contributor

@ravishanigarapu ravishanigarapu left a comment

Choose a reason for hiding this comment

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

looks ok

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.

4 participants