Skip to content

Commit 608773a

Browse files
committed
fix: amm-1927 send headers only if the request is from the allowed origin
1 parent c4b57e0 commit 608773a

3 files changed

Lines changed: 78 additions & 14 deletions

File tree

src/main/java/com/iemr/admin/config/CorsConfig.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ public void addCorsMappings(CorsRegistry registry) {
1919
Arrays.stream(allowedOrigins.split(","))
2020
.map(String::trim)
2121
.toArray(String[]::new))
22-
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
23-
.allowedHeaders("*")
22+
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
23+
.allowedHeaders("Authorization", "Content-Type", "Accept", "Jwttoken",
24+
"serverAuthorization", "ServerAuthorization", "serverauthorization", "Serverauthorization")
2425
.exposedHeaders("Authorization", "Jwttoken")
2526
.allowCredentials(true)
2627
.maxAge(3600);

src/main/java/com/iemr/admin/utils/JwtUserIdValidationFilter.java

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,61 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
3737
HttpServletResponse response = (HttpServletResponse) servletResponse;
3838

3939
String origin = request.getHeader("Origin");
40+
String method = request.getMethod();
41+
String uri = request.getRequestURI();
42+
43+
logger.debug("Incoming Origin: {}", origin);
44+
logger.debug("Request Method: {}", method);
45+
logger.debug("Request URI: {}", uri);
46+
logger.debug("Allowed Origins Configured: {}", allowedOrigins);
47+
48+
if ("OPTIONS".equalsIgnoreCase(method)) {
49+
if (origin == null) {
50+
logger.warn("BLOCKED - OPTIONS request without Origin header | Method: {} | URI: {}", method, uri);
51+
response.sendError(HttpServletResponse.SC_FORBIDDEN, "OPTIONS request requires Origin header");
52+
return;
53+
}
54+
if (!isOriginAllowed(origin)) {
55+
logger.warn("BLOCKED - Unauthorized Origin | Origin: {} | Method: {} | URI: {}", origin, method, uri);
56+
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Origin not allowed");
57+
return;
58+
}
59+
} else {
60+
// For non-OPTIONS requests, validate origin if present
61+
if (origin != null && !isOriginAllowed(origin)) {
62+
logger.warn("BLOCKED - Unauthorized Origin | Origin: {} | Method: {} | URI: {}", origin, method, uri);
63+
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Origin not allowed");
64+
return;
65+
}
66+
}
67+
68+
// Determine request path/context for later checks
69+
String path = request.getRequestURI();
70+
String contextPath = request.getContextPath();
71+
4072
if (origin != null && isOriginAllowed(origin)) {
41-
response.setHeader("Access-Control-Allow-Origin", origin);
42-
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
43-
response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, Jwttoken");
73+
response.setHeader("Access-Control-Allow-Origin", origin); // Never use wildcard
74+
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
75+
response.setHeader("Access-Control-Allow-Headers",
76+
"Authorization, Content-Type, Accept, Jwttoken, serverAuthorization, ServerAuthorization, serverauthorization, Serverauthorization");
4477
response.setHeader("Access-Control-Allow-Credentials", "true");
78+
response.setHeader("Access-Control-Max-Age", "3600");
79+
logger.info("Origin Validated | Origin: {} | Method: {} | URI: {}", origin, method, uri);
4580
} else {
4681
logger.warn("Origin [{}] is NOT allowed. CORS headers NOT added.", origin);
4782
}
4883

49-
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
50-
logger.info("OPTIONS request - skipping JWT validation");
84+
if ("OPTIONS".equalsIgnoreCase(method)) {
85+
// OPTIONS (preflight) - respond with full allowed methods
86+
response.setHeader("Access-Control-Allow-Origin", origin);
87+
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
88+
response.setHeader("Access-Control-Allow-Headers",
89+
"Authorization, Content-Type, Accept, Jwttoken, serverAuthorization, ServerAuthorization, serverauthorization, Serverauthorization");
90+
response.setHeader("Access-Control-Allow-Credentials", "true");
5191
response.setStatus(HttpServletResponse.SC_OK);
5292
return;
5393
}
5494

55-
String path = request.getRequestURI();
56-
String contextPath = request.getContextPath();
5795
logger.info("JwtUserIdValidationFilter invoked for path: " + path);
5896

5997
// Log cookies for debugging
@@ -70,7 +108,7 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
70108
}
71109

72110
// Log headers for debugging
73-
logger.info("JWT token from header: ");
111+
logger.debug("JWT token from header: {}", request.getHeader("Jwttoken") != null ? "present" : "not present");
74112

75113
// Skip login and public endpoints
76114
if (path.equals(contextPath + "/user/userAuthenticate")
@@ -142,9 +180,8 @@ private boolean isOriginAllowed(String origin) {
142180
.anyMatch(pattern -> {
143181
String regex = pattern
144182
.replace(".", "\\.")
145-
.replace("*", ".*")
146-
.replace("http://localhost:.*", "http://localhost:\\d+"); // special case for wildcard port
147-
183+
.replace("*", ".*");
184+
148185
boolean matched = origin.matches(regex);
149186
return matched;
150187
});

src/main/java/com/iemr/admin/utils/http/HTTPRequestInterceptor.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222
package com.iemr.admin.utils.http;
2323

2424

25+
import java.util.Arrays;
26+
2527
import javax.ws.rs.core.MediaType;
2628

2729
import org.slf4j.Logger;
2830
import org.slf4j.LoggerFactory;
2931
import org.springframework.beans.factory.annotation.Autowired;
32+
import org.springframework.beans.factory.annotation.Value;
3033
import org.springframework.stereotype.Component;
3134
import org.springframework.web.servlet.HandlerInterceptor;
3235
import org.springframework.web.servlet.ModelAndView;
@@ -41,6 +44,8 @@
4144
@Component
4245
public class HTTPRequestInterceptor implements HandlerInterceptor {
4346
Logger logger = LoggerFactory.getLogger(this.getClass().getName());
47+
@Value("${cors.allowed-origins}")
48+
private String allowedOrigins;
4449
@Autowired
4550
private RedisStorage redisStorage;
4651
@Autowired
@@ -104,7 +109,13 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
104109
response.getOutputStream().print(output.toString());
105110
response.setContentType(MediaType.APPLICATION_JSON);
106111
response.setContentLength(output.toString().length());
107-
response.setHeader("Access-Control-Allow-Origin", "*");
112+
String origin = request.getHeader("Origin");
113+
if (origin != null && isOriginAllowed(origin)) {
114+
response.setHeader("Access-Control-Allow-Origin", origin);
115+
response.setHeader("Access-Control-Allow-Credentials", "true");
116+
} else if (origin != null) {
117+
logger.warn("CORS headers NOT added for error response | Unauthorized origin: {}", origin);
118+
}
108119
status = false;
109120
}
110121
}
@@ -138,4 +149,19 @@ public void afterCompletion(HttpServletRequest request, HttpServletResponse resp
138149
logger.info("http interceptor - after completion");
139150

140151
}
152+
153+
private boolean isOriginAllowed(String origin) {
154+
if (origin == null || allowedOrigins == null || allowedOrigins.trim().isEmpty()) {
155+
return false;
156+
}
157+
158+
return Arrays.stream(allowedOrigins.split(","))
159+
.map(String::trim)
160+
.anyMatch(pattern -> {
161+
String regex = pattern
162+
.replace(".", "\\.")
163+
.replace("*", ".*");
164+
return origin.matches(regex);
165+
});
166+
}
141167
}

0 commit comments

Comments
 (0)