From 5e5aca1c9852eaa4ff1ede89ceb57e2183cb4b86 Mon Sep 17 00:00:00 2001 From: 5Amogh Date: Mon, 17 Nov 2025 18:05:12 +0530 Subject: [PATCH 1/4] fix: amm-1927 res headers based on origin via allowed cors --- .../tm/utils/JwtUserIdValidationFilter.java | 45 +++++++++++++++---- .../tm/utils/http/HTTPRequestInterceptor.java | 36 ++++++++++++++- 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java b/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java index 5d6c05b3..232f6706 100644 --- a/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java +++ b/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java @@ -37,28 +37,58 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo HttpServletResponse response = (HttpServletResponse) servletResponse; String origin = request.getHeader("Origin"); + String method = request.getMethod(); + String uri = request.getRequestURI(); logger.debug("Incoming Origin: {}", origin); logger.debug("Allowed Origins Configured: {}", allowedOrigins); + if ("OPTIONS".equalsIgnoreCase(method)) { + if (origin == null) { + logger.warn("BLOCKED - OPTIONS request without Origin header | Method: {} | URI: {}", method, uri); + response.sendError(HttpServletResponse.SC_FORBIDDEN, "OPTIONS request requires Origin header"); + return; + } + if (!isOriginAllowed(origin)) { + logger.warn("BLOCKED - Unauthorized Origin | Origin: {} | Method: {} | URI: {}", origin, method, uri); + response.sendError(HttpServletResponse.SC_FORBIDDEN, "Origin not allowed"); + return; + } + } else { + // For non-OPTIONS requests, validate origin if present + if (origin != null && !isOriginAllowed(origin)) { + logger.warn("BLOCKED - Unauthorized Origin | Origin: {} | Method: {} | URI: {}", origin, method, uri); + response.sendError(HttpServletResponse.SC_FORBIDDEN, "Origin not allowed"); + return; + } + } + + String path = request.getRequestURI(); + String contextPath = request.getContextPath(); if (origin != null && isOriginAllowed(origin)) { - response.setHeader("Access-Control-Allow-Origin", origin); - response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); - response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, Jwttoken"); - response.setHeader("Vary", "Origin"); + response.setHeader("Access-Control-Allow-Origin", origin); // Never use wildcard + response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS"); + response.setHeader("Access-Control-Allow-Headers", + "Authorization, Content-Type, Accept, Jwttoken, serverAuthorization, ServerAuthorization, serverauthorization, Serverauthorization"); response.setHeader("Access-Control-Allow-Credentials", "true"); + response.setHeader("Access-Control-Max-Age", "3600"); + logger.info("Origin Validated | Origin: {} | Method: {} | URI: {}", origin, method, uri); } else { logger.warn("Origin [{}] is NOT allowed. CORS headers NOT added.", origin); } if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { + // OPTIONS (preflight) - respond with full allowed methods + response.setHeader("Access-Control-Allow-Origin", origin); + response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS"); + response.setHeader("Access-Control-Allow-Headers", + "Authorization, Content-Type, Accept, Jwttoken, serverAuthorization, ServerAuthorization, serverauthorization, Serverauthorization"); + response.setHeader("Access-Control-Allow-Credentials", "true"); logger.info("OPTIONS request - skipping JWT validation"); response.setStatus(HttpServletResponse.SC_OK); return; } - String path = request.getRequestURI(); - String contextPath = request.getContextPath(); logger.info("JwtUserIdValidationFilter invoked for path: " + path); // Log cookies for debugging @@ -141,8 +171,7 @@ private boolean isOriginAllowed(String origin) { .anyMatch(pattern -> { String regex = pattern .replace(".", "\\.") - .replace("*", ".*") - .replace("http://localhost:.*", "http://localhost:\\d+"); // special case for wildcard port + .replace("*", ".*"); boolean matched = origin.matches(regex); return matched; diff --git a/src/main/java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java b/src/main/java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java index 2da14401..703af427 100644 --- a/src/main/java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java +++ b/src/main/java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java @@ -21,11 +21,14 @@ */ package com.iemr.tm.utils.http; +import java.util.Arrays; + import javax.ws.rs.core.MediaType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.HandlerInterceptor; @@ -39,6 +42,9 @@ @Component public class HTTPRequestInterceptor implements HandlerInterceptor { Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName()); + + @Value("${cors.allowed-origins}") + private String allowedOrigins; private SessionObject sessionObject; @@ -95,7 +101,13 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons response.getOutputStream().print(output.toString()); response.setContentType(MediaType.APPLICATION_JSON); response.setContentLength(output.toString().length()); - response.setHeader("Access-Control-Allow-Origin", "*"); + String origin = request.getHeader("Origin"); + if (origin != null && isOriginAllowed(origin)) { + response.setHeader("Access-Control-Allow-Origin", origin); + response.setHeader("Access-Control-Allow-Credentials", "true"); + } else if (origin != null) { + logger.warn("CORS headers NOT added for error response | Unauthorized origin: {}", origin); + } status = false; } } @@ -126,4 +138,26 @@ public void afterCompletion(HttpServletRequest request, HttpServletResponse resp throws Exception { logger.debug("In afterCompletion Request Completed"); } + + /** + * Check if the given origin is allowed based on configured allowedOrigins. + * Uses the same logic as JwtUserIdValidationFilter for consistency. + * + * @param origin The origin to validate + * @return true if origin is allowed, false otherwise + */ + private boolean isOriginAllowed(String origin) { + if (origin == null || allowedOrigins == null || allowedOrigins.trim().isEmpty()) { + return false; + } + + return Arrays.stream(allowedOrigins.split(",")) + .map(String::trim) + .anyMatch(pattern -> { + String regex = pattern + .replace(".", "\\.") + .replace("*", ".*"); + return origin.matches(regex); + }); + } } \ No newline at end of file From c8f1b3380abcdc136abaa623b4839620aa6a61c8 Mon Sep 17 00:00:00 2001 From: 5Amogh Date: Mon, 17 Nov 2025 18:19:23 +0530 Subject: [PATCH 2/4] fix: amm-1927 coderabbit comments resolved --- .../com/iemr/tm/utils/JwtUserIdValidationFilter.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java b/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java index 232f6706..ba64e272 100644 --- a/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java +++ b/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java @@ -77,18 +77,6 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo logger.warn("Origin [{}] is NOT allowed. CORS headers NOT added.", origin); } - if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { - // OPTIONS (preflight) - respond with full allowed methods - response.setHeader("Access-Control-Allow-Origin", origin); - response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS"); - response.setHeader("Access-Control-Allow-Headers", - "Authorization, Content-Type, Accept, Jwttoken, serverAuthorization, ServerAuthorization, serverauthorization, Serverauthorization"); - response.setHeader("Access-Control-Allow-Credentials", "true"); - logger.info("OPTIONS request - skipping JWT validation"); - response.setStatus(HttpServletResponse.SC_OK); - return; - } - logger.info("JwtUserIdValidationFilter invoked for path: " + path); // Log cookies for debugging From 8b431b6b4eb4922a0e473367b58beb510cdaba3a Mon Sep 17 00:00:00 2001 From: Amoghavarsh <93114621+5Amogh@users.noreply.github.com> Date: Tue, 18 Nov 2025 10:55:37 +0530 Subject: [PATCH 3/4] localhost regex added --- src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java b/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java index ba64e272..79708478 100644 --- a/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java +++ b/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java @@ -159,7 +159,8 @@ private boolean isOriginAllowed(String origin) { .anyMatch(pattern -> { String regex = pattern .replace(".", "\\.") - .replace("*", ".*"); + .replace("*", ".*") + .replace("http://localhost:.*", "http://localhost:\\d+"); boolean matched = origin.matches(regex); return matched; From ce2a0b0869b76d614a8457f370142306a419536f Mon Sep 17 00:00:00 2001 From: Amoghavarsh <93114621+5Amogh@users.noreply.github.com> Date: Tue, 18 Nov 2025 10:59:23 +0530 Subject: [PATCH 4/4] Update regex pattern for localhost in interceptor --- .../java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java b/src/main/java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java index 703af427..f8160d83 100644 --- a/src/main/java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java +++ b/src/main/java/com/iemr/tm/utils/http/HTTPRequestInterceptor.java @@ -156,8 +156,9 @@ private boolean isOriginAllowed(String origin) { .anyMatch(pattern -> { String regex = pattern .replace(".", "\\.") - .replace("*", ".*"); + .replace("*", ".*") + .replace("http://localhost:.*", "http://localhost:\\d+"); return origin.matches(regex); }); } -} \ No newline at end of file +}