Skip to content

Conversation

@ravishanigarapu
Copy link
Contributor

@ravishanigarapu ravishanigarapu commented May 20, 2025

πŸ“‹ Description

JIRA ID: AMM-1456

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

  • New Features

    • Improved handling of JWT tokens from cookies and headers for enhanced authentication.
    • Requests from mobile clients are now better supported and identified.
    • Standardized unauthorized error messages for failed authentication.
    • Added centralized utility for constructing HTTP requests with consistent headers.
    • Introduced user agent context management for improved client identification.
  • Bug Fixes

    • Prevented potential errors when cookies are missing in requests.
  • Refactor

    • Streamlined authentication logic and HTTP request construction for maintainability and consistency.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented May 20, 2025

Walkthrough

This update introduces new utility classes for HTTP request handling and user agent context, refactors JWT validation logic in the filter, and centralizes HTTP entity creation for REST calls. It adjusts token extraction methods, improves request header management, and enhances logging and error responses for unauthorized requests.

Changes

File(s) Change Summary
src/main/java/com/wipro/fhir/utils/JwtUserIdValidationFilter.java Refactored JWT validation logic: now checks cookies and headers separately, wraps requests to override Authorization header, adds mobile client detection, standardized error responses, and logging improvements. Added shouldSkipPath and isMobileClient helper methods.
src/main/java/com/wipro/fhir/utils/http/AuthorizationHeaderRequestWrapper.java New class to wrap requests and override/inject the Authorization header, ensuring consistent header handling.
src/main/java/com/wipro/fhir/utils/http/HTTPRequestInterceptor.java Modified preHandle to bypass validation if the Authorization header is missing or empty, logging this event.
src/main/java/com/wipro/fhir/service/api_channel/APIChannelImpl.java Refactored to use new utility method for creating HTTP request entities, removing manual JWT token extraction and header assembly.
src/main/java/com/wipro/fhir/utils/CookieUtil.java Made getJwtTokenFromCookie static and added a null check for cookies to prevent null pointer exceptions.
src/main/java/com/wipro/fhir/utils/RestTemplateUtil.java New utility class with a static method to create HTTP entities for REST calls, propagating headers and tokens consistently.
src/main/java/com/wipro/fhir/utils/UserAgentContext.java New class to manage a thread-local user agent string, providing static methods to set, get, and clear the user agent.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant JwtUserIdValidationFilter
    participant AuthorizationHeaderRequestWrapper
    participant UserAgentContext

    Client->>JwtUserIdValidationFilter: Sends HTTP request
    JwtUserIdValidationFilter->>JwtUserIdValidationFilter: shouldSkipPath(path)
    alt Path should be skipped
        JwtUserIdValidationFilter-->>Client: Passes request through
    else
        JwtUserIdValidationFilter->>JwtUserIdValidationFilter: Check JWT in cookies
        alt JWT valid in cookies
            JwtUserIdValidationFilter->>AuthorizationHeaderRequestWrapper: Wrap request (empty Authorization)
            AuthorizationHeaderRequestWrapper-->>JwtUserIdValidationFilter: Wrapped request
            JwtUserIdValidationFilter-->>Client: Passes wrapped request
        else
            JwtUserIdValidationFilter->>JwtUserIdValidationFilter: Check JWT in header
            alt JWT valid in header
                JwtUserIdValidationFilter->>AuthorizationHeaderRequestWrapper: Wrap request (empty Authorization)
                AuthorizationHeaderRequestWrapper-->>JwtUserIdValidationFilter: Wrapped request
                JwtUserIdValidationFilter-->>Client: Passes wrapped request
            else
                JwtUserIdValidationFilter->>JwtUserIdValidationFilter: isMobileClient(User-Agent)
                alt Mobile client with Authorization header
                    JwtUserIdValidationFilter->>UserAgentContext: setUserAgent
                    JwtUserIdValidationFilter-->>Client: Passes request
                    JwtUserIdValidationFilter->>UserAgentContext: clear
                else
                    JwtUserIdValidationFilter-->>Client: Responds 401 Unauthorized
                end
            end
        end
    end
Loading

Possibly related PRs

  • Skip JwtToken validation for mobile appΒ #79: Also modifies JwtUserIdValidationFilter to add shouldSkipPath and isMobileClient, and changes JWT validation logic for mobile clientsβ€”addresses the same functionality.
  • Swagger ChangesΒ #67: Alters the same filter by adding path conditions to skip JWT validation for Swagger endpoints, but does not overlap with the core JWT logic changes.

Poem

A hop and a skip through headers anew,
With cookies and tokens, we filter the queue.
Mobile or web, we know who you are,
Wrapping requests, both near and far.
User agents in threads, all neat and conciseβ€”
This rabbit ensures your auth is precise!
πŸ‡βœ¨


πŸ“œ Recent review details

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

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between b6dae7d and fb8f028.

πŸ“’ Files selected for processing (2)
  • src/main/java/com/wipro/fhir/utils/JwtUserIdValidationFilter.java (4 hunks)
  • src/main/java/com/wipro/fhir/utils/RestTemplateUtil.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/wipro/fhir/utils/RestTemplateUtil.java
  • src/main/java/com/wipro/fhir/utils/JwtUserIdValidationFilter.java
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (java)
✨ Finishing Touches
  • πŸ“ Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this 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
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: 2

🧹 Nitpick comments (6)
src/main/environment/common_ci.properties (1)

116-118: Confirm env-substitution & provide a safe default for Swagger toggle

The two new flags look correct for Springdoc, but because you rely on an environment placeholder (@env.SWAGGER_DOC_ENABLED@) the build will fail if that variable is missing.
Consider adding a profile-specific .properties file (e.g. application-local.properties) with an explicit default (true or false) so that local runs and CI don’t unexpectedly crash.

src/main/java/com/wipro/fhir/utils/http/AuthorizationHeaderRequestWrapper.java (3)

9-15: Rename the field to follow Java conventions

Authorization looks like a constant but is actually per-request state. A lower-camel-case name (e.g. authorizationValue) prevents confusion and avoids the sonar smell that β€œVariables should not start with an upper-case letter”.

-private final String Authorization;
+private final String authorizationValue;

Remember to update the three usages below.


25-31: Guard against null header values when building the enumeration

If the wrapper is instantiated with null (unlikely but possible), Collections.singletonList(null) will throw an NPE. A tiny guard keeps things safe:

-    if ("Authorization".equalsIgnoreCase(name)) {
-        return Collections.enumeration(Collections.singletonList(Authorization));
+    if ("Authorization".equalsIgnoreCase(name)) {
+        return Authorization == null
+                ? Collections.emptyEnumeration()
+                : Collections.enumeration(Collections.singletonList(Authorization));

34-40: Potential duplication in header names

getHeaderNames() unconditionally adds "Authorization" even when the custom value is null.
That can report a header name whose value subsequently resolves to null, which may surprise downstream code.
Consider adding it only when authorizationValue is non-null/ non-empty.

src/main/java/com/wipro/fhir/utils/JwtUserIdValidationFilter.java (2)

65-71: Avoid duplicate validation effort

The cookie and header branches are almost identical except for the token source. You can reduce duplication (and future bugs) by extracting a helper method that attempts validation and, upon success, forwards the wrapped request.


109-114: Hard-coded allow-list of paths is prone to drift

Every new public endpoint requires touching this filter. A property-driven list or annotation-based approach (@PermitAll, @SecurityRequirement) is more maintainable.

πŸ“œ Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 5c1d5a6 and 06015ae.

πŸ“’ Files selected for processing (4)
  • src/main/environment/common_ci.properties (1 hunks)
  • src/main/java/com/wipro/fhir/utils/JwtUserIdValidationFilter.java (3 hunks)
  • src/main/java/com/wipro/fhir/utils/http/AuthorizationHeaderRequestWrapper.java (1 hunks)
  • src/main/java/com/wipro/fhir/utils/http/HTTPRequestInterceptor.java (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/java/com/wipro/fhir/utils/JwtUserIdValidationFilter.java (1)
src/main/java/com/wipro/fhir/utils/http/AuthorizationHeaderRequestWrapper.java (1)
  • AuthorizationHeaderRequestWrapper (9-41)

Comment on lines 62 to 65
if (authorization.equals("")) {
logger.info("Authorization header is null or empty. Skipping HTTPRequestInterceptor.");
return true; // Allow the request to proceed without validation
}
Copy link
Contributor

Choose a reason for hiding this comment

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

πŸ› οΈ Refactor suggestion

Null-pointer risk when Authorization header is absent

authorization.equals("") throws an NPE if the header is missing.
A safe check protects all callers and keeps the log message aligned with reality.

-if (authorization.equals("")) {
+if (authorization == null || authorization.trim().isEmpty()) {
πŸ“ 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
if (authorization.equals("")) {
logger.info("Authorization header is null or empty. Skipping HTTPRequestInterceptor.");
return true; // Allow the request to proceed without validation
}
// src/main/java/com/wipro/fhir/utils/http/HTTPRequestInterceptor.java
// Lines: ~62-65
if (authorization == null || authorization.trim().isEmpty()) {
logger.info("Authorization header is null or empty. Skipping HTTPRequestInterceptor.");
return true; // Allow the request to proceed without validation
}
πŸ€– Prompt for AI Agents
In src/main/java/com/wipro/fhir/utils/http/HTTPRequestInterceptor.java around
lines 62 to 65, the code calls authorization.equals("") without checking if
authorization is null, which can cause a NullPointerException if the
Authorization header is absent. Fix this by first checking if authorization is
null or empty using a safe method like authorization == null ||
authorization.isEmpty(), and update the log message to reflect that the header
may be null or empty.

Comment on lines 85 to 88
if (userAgent != null && isMobileClient(userAgent) && authHeader != null) {
filterChain.doFilter(servletRequest, servletResponse);
} else {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid JWT token");
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Mobile-client bypass may open an authentication hole

Right now any request whose User-Agent contains β€œokhttp” AND merely has an Authorization header (regardless of its content) skips JWT validation.
An attacker can spoof the UA string and send an empty or bogus header to gain access.

Consider tightening the rule:

  • Check that the header starts with Bearer and validate it, or
  • Restrict the bypass to very specific paths/methods, or
  • Remove the bypass and rely solely on proper token validation.
πŸ€– Prompt for AI Agents
In src/main/java/com/wipro/fhir/utils/JwtUserIdValidationFilter.java around
lines 85 to 88, the current logic allows requests with a User-Agent containing
"okhttp" and any Authorization header to bypass JWT validation, which is a
security risk. To fix this, modify the condition to check that the Authorization
header starts with "Bearer " and validate the token properly before allowing the
bypass, or alternatively restrict the bypass to specific paths or methods, or
remove the bypass entirely to enforce JWT validation on all requests.

Copy link
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: 4

🧹 Nitpick comments (2)
src/main/java/com/wipro/fhir/utils/CookieUtil.java (1)

27-33: Static method with null check is a good improvement

The static implementation with a null check prevents potential NullPointerExceptions when cookies are not present. The stream-based approach is also more concise than the traditional loop.

For consistency and better documentation, consider:

+    /**
+     * Retrieves the JWT token from the request cookies.
+     * @param request The HTTP request containing cookies
+     * @return The JWT token value or null if not found
+     */
 	public static String getJwtTokenFromCookie(HttpServletRequest request) {
 		if (request.getCookies() == null) {
 	        return null;  // If cookies are null, return null safely.
 	    }
 		return Arrays.stream(request.getCookies()).filter(cookie -> "Jwttoken".equals(cookie.getName()))
 				.map(Cookie::getValue).findFirst().orElse(null);
 	}

Also, consider extracting "Jwttoken" as a constant to avoid duplication and make future changes easier.

src/main/java/com/wipro/fhir/service/api_channel/APIChannelImpl.java (1)

109-110: Good refactoring to use centralized request entity creation

Similar to the previous refactoring, this change utilizes RestTemplateUtil.createRequestEntity for consistent request entity creation.

Consider removing the unused getHttpHeader method (lines 128-139) since it's no longer being used after these refactorings.

πŸ“œ Review details

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

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 06015ae and b6dae7d.

πŸ“’ Files selected for processing (6)
  • src/main/java/com/wipro/fhir/service/api_channel/APIChannelImpl.java (3 hunks)
  • src/main/java/com/wipro/fhir/utils/CookieUtil.java (1 hunks)
  • src/main/java/com/wipro/fhir/utils/JwtUserIdValidationFilter.java (3 hunks)
  • src/main/java/com/wipro/fhir/utils/RestTemplateUtil.java (1 hunks)
  • src/main/java/com/wipro/fhir/utils/UserAgentContext.java (1 hunks)
  • src/main/java/com/wipro/fhir/utils/http/HTTPRequestInterceptor.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/com/wipro/fhir/utils/http/HTTPRequestInterceptor.java
  • src/main/java/com/wipro/fhir/utils/JwtUserIdValidationFilter.java
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/main/java/com/wipro/fhir/service/api_channel/APIChannelImpl.java (1)
src/main/java/com/wipro/fhir/utils/RestTemplateUtil.java (1)
  • RestTemplateUtil (13-42)
src/main/java/com/wipro/fhir/utils/RestTemplateUtil.java (1)
src/main/java/com/wipro/fhir/utils/UserAgentContext.java (1)
  • UserAgentContext (3-18)
πŸ”‡ Additional comments (2)
src/main/java/com/wipro/fhir/service/api_channel/APIChannelImpl.java (2)

42-42: Good addition of RestTemplateUtil import

The import for the new utility class is correctly added.


76-79: Clean refactoring to use centralized request entity creation

The code now uses the RestTemplateUtil.createRequestEntity method instead of manually creating the HTTP entity, which reduces code duplication and centralizes header management.

Comment on lines +3 to +18
public class UserAgentContext {
private static final ThreadLocal<String> userAgentHolder = new ThreadLocal<>();

public static void setUserAgent(String userAgent) {
userAgentHolder.set(userAgent);
}

public static String getUserAgent() {
return userAgentHolder.get();
}

public static void clear() {
userAgentHolder.remove();
}

}
Copy link
Contributor

Choose a reason for hiding this comment

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

πŸ› οΈ Refactor suggestion

The ThreadLocal implementation looks good but needs documentation

The implementation correctly uses ThreadLocal for thread-safe user agent storage, which is essential in a multi-threaded web application environment. The class provides appropriate methods for setting, getting, and clearing the user agent value.

Add JavaDoc comments to explain the purpose of this class and its methods, especially highlighting the importance of calling clear() to prevent memory leaks in thread pools:

 package com.wipro.fhir.utils;

+/**
+ * Utility class to store and retrieve User-Agent information in a thread-local context.
+ * This allows the User-Agent string to be propagated across the request processing chain.
+ */
 public class UserAgentContext {
 	private static final ThreadLocal<String> userAgentHolder = new ThreadLocal<>();

+    /**
+     * Sets the User-Agent string for the current thread.
+     * @param userAgent The User-Agent string to store
+     */
     public static void setUserAgent(String userAgent) {
         userAgentHolder.set(userAgent);
     }

+    /**
+     * Gets the User-Agent string for the current thread.
+     * @return The User-Agent string or null if not set
+     */
     public static String getUserAgent() {
         return userAgentHolder.get();
     }

+    /**
+     * Clears the User-Agent string for the current thread.
+     * Important: This should be called after request processing is complete
+     * to prevent memory leaks in thread pools.
+     */
     public static void clear() {
         userAgentHolder.remove();
     }

 }
πŸ“ 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 class UserAgentContext {
private static final ThreadLocal<String> userAgentHolder = new ThreadLocal<>();
public static void setUserAgent(String userAgent) {
userAgentHolder.set(userAgent);
}
public static String getUserAgent() {
return userAgentHolder.get();
}
public static void clear() {
userAgentHolder.remove();
}
}
package com.wipro.fhir.utils;
/**
* Utility class to store and retrieve User-Agent information in a thread-local context.
* This allows the User-Agent string to be propagated across the request processing chain.
*/
public class UserAgentContext {
private static final ThreadLocal<String> userAgentHolder = new ThreadLocal<>();
/**
* Sets the User-Agent string for the current thread.
* @param userAgent The User-Agent string to store
*/
public static void setUserAgent(String userAgent) {
userAgentHolder.set(userAgent);
}
/**
* Gets the User-Agent string for the current thread.
* @return The User-Agent string or null if not set
*/
public static String getUserAgent() {
return userAgentHolder.get();
}
/**
* Clears the User-Agent string for the current thread.
* Important: This should be called after request processing is complete
* to prevent memory leaks in thread pools.
*/
public static void clear() {
userAgentHolder.remove();
}
}
πŸ€– Prompt for AI Agents
In src/main/java/com/wipro/fhir/utils/UserAgentContext.java around lines 3 to
18, add JavaDoc comments to the class and each method. Document the class
purpose as a thread-safe holder for user agent strings using ThreadLocal. For
each method, explain its role, especially emphasize in the clear() method's
JavaDoc the importance of calling it to prevent memory leaks in thread pool
environments by removing the ThreadLocal value after use.

drtechie
drtechie previously approved these changes May 22, 2025
@sonarqubecloud
Copy link

@ravishanigarapu ravishanigarapu merged commit da71080 into PSMRI:develop May 22, 2025
7 of 8 checks passed
@coderabbitai coderabbitai bot mentioned this pull request May 26, 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