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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import com.wipro.fhir.data.request_handler.ResourceRequestHandler;
import com.wipro.fhir.data.request_handler.UserAuthAPIResponse;
import com.wipro.fhir.utils.CookieUtil;
import com.wipro.fhir.utils.RestTemplateUtil;
import com.wipro.fhir.utils.exception.FHIRException;
import com.wipro.fhir.utils.mapper.InputMapper;

Expand Down Expand Up @@ -72,15 +73,9 @@ public String benSearchByBenID(String Authorization, ResourceRequestHandler reso
String responseBody = null;
if (restTemplate == null)
restTemplate = new RestTemplate();
HttpServletRequest requestHeader = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
String jwtTokenFromCookie = cookieUtil.getJwtTokenFromCookie(requestHeader);

MultiValueMap<String, String> header = getHttpHeader(Authorization, "application/json");
HttpEntity<Object> urlRequestOBJ = new HttpEntity<Object>(resourceRequestHandler, header);
header.add("Cookie", "Jwttoken=" + jwtTokenFromCookie);

ResponseEntity<String> response = restTemplate.exchange(benSearchByBenIDURL, HttpMethod.POST, urlRequestOBJ,

HttpEntity<Object> request = RestTemplateUtil.createRequestEntity(resourceRequestHandler, Authorization);
ResponseEntity<String> response = restTemplate.exchange(benSearchByBenIDURL, HttpMethod.POST, request,
String.class);

if (response.getStatusCodeValue() == 200 && response.hasBody()) {
Expand Down Expand Up @@ -111,12 +106,8 @@ public String userAuthentication() throws FHIRException {
restTemplate = new RestTemplate();
HttpServletRequest requestHeader = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
String jwtTokenFromCookie = cookieUtil.getJwtTokenFromCookie(requestHeader);

MultiValueMap<String, String> header = getHttpHeader(null, "application/json");
HttpEntity<Object> urlRequestOBJ = new HttpEntity<Object>(userDetails, header);
header.add("Cookie", "Jwttoken=" + jwtTokenFromCookie);


HttpEntity<Object> urlRequestOBJ = RestTemplateUtil.createRequestEntity(userDetails, requestHeader.getHeader("Authorization"));
ResponseEntity<String> response = restTemplate.exchange(userAuthURL, HttpMethod.POST, urlRequestOBJ,
String.class);

Expand Down
5 changes: 4 additions & 1 deletion src/main/java/com/wipro/fhir/utils/CookieUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ public Optional<String> getCookieValue(HttpServletRequest request, String cookie
return Optional.empty();
}

public String getJwtTokenFromCookie(HttpServletRequest request) {
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);
}
Expand Down
42 changes: 25 additions & 17 deletions src/main/java/com/wipro/fhir/utils/JwtUserIdValidationFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.wipro.fhir.utils.http.AuthorizationHeaderRequestWrapper;

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
Expand All @@ -29,6 +31,7 @@ public JwtUserIdValidationFilter(JwtAuthenticationUtil jwtAuthenticationUtil) {
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;

HttpServletResponse response = (HttpServletResponse) servletResponse;

String path = request.getRequestURI();
Expand All @@ -47,11 +50,7 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
} else {
logger.info("No cookies found in the request");
}

// Log headers for debugging
String jwtTokenFromHeader = request.getHeader("Jwttoken");
logger.info("JWT token from header: ");


// Skip login and public endpoints
if (shouldSkipPath(path, contextPath)) {
filterChain.doFilter(servletRequest, servletResponse);
Expand All @@ -66,24 +65,33 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
if (jwtFromCookie != null) {
logger.info("Validating JWT token from cookie");
if (jwtAuthenticationUtil.validateUserIdAndJwtToken(jwtFromCookie)) {
filterChain.doFilter(servletRequest, servletResponse);

AuthorizationHeaderRequestWrapper authorizationHeaderRequestWrapper = new AuthorizationHeaderRequestWrapper(
request, "");
filterChain.doFilter(authorizationHeaderRequestWrapper, servletResponse);
return;
}
}

if (jwtFromHeader != null) {
} else if (jwtFromHeader != null) {
logger.info("Validating JWT token from header");
if (jwtAuthenticationUtil.validateUserIdAndJwtToken(jwtFromHeader)) {
filterChain.doFilter(servletRequest, servletResponse);
AuthorizationHeaderRequestWrapper authorizationHeaderRequestWrapper = new AuthorizationHeaderRequestWrapper(
request, "");
filterChain.doFilter(authorizationHeaderRequestWrapper, servletResponse);
return;
}
} else {
String userAgent = request.getHeader("User-Agent");
logger.info("User-Agent: " + userAgent);

if (userAgent != null && isMobileClient(userAgent) && authHeader != null) {
try {
UserAgentContext.setUserAgent(userAgent);
filterChain.doFilter(servletRequest, servletResponse);
} finally {
UserAgentContext.clear();
}
return;
}
}
String userAgent = request.getHeader("User-Agent");
logger.info("User-Agent: " + userAgent);

if (userAgent != null && isMobileClient(userAgent) && authHeader != null) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}

logger.warn("No valid authentication token found");
Expand Down
50 changes: 50 additions & 0 deletions src/main/java/com/wipro/fhir/utils/RestTemplateUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.wipro.fhir.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.servlet.http.HttpServletRequest;

public class RestTemplateUtil {
private final static Logger logger = LoggerFactory.getLogger(RestTemplateUtil.class);

public static HttpEntity<Object> createRequestEntity(Object body, String authorization) {

ServletRequestAttributes servletRequestAttributes = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes());
if (servletRequestAttributes == null) {
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE + ";charset=utf-8");
headers.add(HttpHeaders.AUTHORIZATION, authorization);
return new HttpEntity<>(body, headers);
}
HttpServletRequest requestHeader = servletRequestAttributes.getRequest();
String jwtTokenFromCookie = null;
try {
jwtTokenFromCookie = CookieUtil.getJwtTokenFromCookie(requestHeader);

} catch (Exception e) {
logger.error("Error while getting jwtToken from Cookie" + e.getMessage() );
}

MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE + ";charset=utf-8");
if(null != UserAgentContext.getUserAgent()) {
headers.add(HttpHeaders.USER_AGENT, UserAgentContext.getUserAgent());
}
headers.add(HttpHeaders.AUTHORIZATION, authorization);
headers.add("JwtToken",requestHeader.getHeader("JwtToken"));
if(null != jwtTokenFromCookie) {
headers.add(HttpHeaders.COOKIE, "Jwttoken=" + jwtTokenFromCookie);
}

return new HttpEntity<>(body, headers);
}

}
18 changes: 18 additions & 0 deletions src/main/java/com/wipro/fhir/utils/UserAgentContext.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.wipro.fhir.utils;

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();
}

}
Comment on lines +3 to +18
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.

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.wipro.fhir.utils.http;


import java.util.*;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;

public class AuthorizationHeaderRequestWrapper extends HttpServletRequestWrapper{
private final String Authorization;

public AuthorizationHeaderRequestWrapper(HttpServletRequest request, String authHeaderValue) {
super(request);
this.Authorization = authHeaderValue;
}

@Override
public String getHeader(String name) {
if ("Authorization".equalsIgnoreCase(name)) {
return Authorization;
}
return super.getHeader(name);
}

@Override
public Enumeration<String> getHeaders(String name) {
if ("Authorization".equalsIgnoreCase(name)) {
return Collections.enumeration(Collections.singletonList(Authorization));
}
return super.getHeaders(name);
}

@Override
public Enumeration<String> getHeaderNames() {
List<String> names = Collections.list(super.getHeaderNames());
if (!names.contains("Authorization")) {
names.add("Authorization");
}
return Collections.enumeration(names);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
boolean status = true;
logger.debug("In preHandle we are Intercepting the Request");
String authorization = request.getHeader("Authorization");
if (authorization == null || authorization.isEmpty()) {
logger.info("Authorization header is null or empty. Skipping HTTPRequestInterceptor.");
return true; // Allow the request to proceed without validation
}
logger.debug("RequestURI::" + request.getRequestURI() + " || Authorization ::" + authorization
+ " || method :: " + request.getMethod());
if (!request.getMethod().equalsIgnoreCase("OPTIONS")) {
Expand Down
Loading