-
Notifications
You must be signed in to change notification settings - Fork 34
fix: amm-1927 res headers based on origin via allowed cors #103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,28 +37,46 @@ 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(); | ||
|
Comment on lines
+65
to
+66
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove unused variables. The Apply this diff: - String path = request.getRequestURI();
- String contextPath = request.getContextPath();
-
π€ Prompt for AI Agents
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tolerable for readbility
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @5Amogh, understood! I'll keep the variables as they are for readability. Thanks for the clarification. π§ Learnings used |
||
|
|
||
| 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())) { | ||
| 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 | ||
|
|
@@ -142,7 +160,7 @@ private boolean isOriginAllowed(String origin) { | |
| String regex = pattern | ||
| .replace(".", "\\.") | ||
| .replace("*", ".*") | ||
| .replace("http://localhost:.*", "http://localhost:\\d+"); // special case for wildcard port | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vishwab1 so should this remain as is?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| .replace("http://localhost:.*", "http://localhost:\\d+"); | ||
|
|
||
| boolean matched = origin.matches(regex); | ||
| return matched; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,27 @@ 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("*", ".*") | ||
| .replace("http://localhost:.*", "http://localhost:\\d+"); | ||
| return origin.matches(regex); | ||
| }); | ||
| } | ||
|
Comment on lines
+149
to
+163
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π οΈ Refactor suggestion | π Major Extract duplicated origin validation to a shared utility. The Create a shared utility class for CORS validation: package com.iemr.tm.utils.cors;
import java.util.Arrays;
public class CorsUtil {
/**
* Check if the given origin is allowed based on configured allowedOrigins.
*
* @param origin The origin to validate
* @param allowedOrigins Comma-separated list of allowed origin patterns
* @return true if origin is allowed, false otherwise
*/
public static boolean isOriginAllowed(String origin, String allowedOrigins) {
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);
});
}
}Then update both classes to use π€ Prompt for AI Agents |
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current implementation returns 200 OK for OPTIONS requests, which is the correct behavior for CORS preflight requests. Could you please check it