Skip to content

Commit 7e2639f

Browse files
authored
Merge pull request #106 from PSMRI/amm-1927
fix: amm-1927 res headers based on cors config for security
2 parents e44c208 + 9047c22 commit 7e2639f

2 files changed

Lines changed: 80 additions & 16 deletions

File tree

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

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,27 +38,56 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
3838

3939
String origin = request.getHeader("Origin");
4040

41+
String method = request.getMethod();
42+
String uri = request.getRequestURI();
43+
4144
logger.debug("Incoming Origin: {}", origin);
45+
logger.debug("Request Method: {}", method);
46+
logger.debug("Request URI: {}", uri);
4247
logger.debug("Allowed Origins Configured: {}", allowedOrigins);
4348

44-
if (origin != null && isOriginAllowed(origin)) {
45-
response.setHeader("Access-Control-Allow-Origin", origin);
46-
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
47-
response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, Jwttoken");
48-
response.setHeader("Vary", "Origin");
49-
response.setHeader("Access-Control-Allow-Credentials", "true");
49+
if ("OPTIONS".equalsIgnoreCase(method)) {
50+
if (origin == null) {
51+
logger.warn("BLOCKED - OPTIONS request without Origin header | Method: {} | URI: {}", method, uri);
52+
response.sendError(HttpServletResponse.SC_FORBIDDEN, "OPTIONS request requires Origin header");
53+
return;
54+
}
55+
if (!isOriginAllowed(origin)) {
56+
logger.warn("BLOCKED - Unauthorized Origin | Origin: {} | Method: {} | URI: {}", origin, method, uri);
57+
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Origin not allowed");
58+
return;
59+
}
5060
} else {
51-
logger.warn("Origin [{}] is NOT allowed. CORS headers NOT added.", origin);
52-
}
53-
54-
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
55-
logger.info("OPTIONS request - skipping JWT validation");
56-
response.setStatus(HttpServletResponse.SC_OK);
57-
return;
61+
// For non-OPTIONS requests, validate origin if present
62+
if (origin != null && !isOriginAllowed(origin)) {
63+
logger.warn("BLOCKED - Unauthorized Origin | Origin: {} | Method: {} | URI: {}", origin, method, uri);
64+
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Origin not allowed");
65+
return;
66+
}
5867
}
5968

6069
String path = request.getRequestURI();
6170
String contextPath = request.getContextPath();
71+
72+
// Set CORS headers and handle OPTIONS request only if origin is valid and
73+
// allowed
74+
if (origin != null && isOriginAllowed(origin)) {
75+
addCorsHeaders(response, origin);
76+
logger.info("Origin Validated | Origin: {} | Method: {} | URI: {}", origin, method, uri);
77+
78+
if ("OPTIONS".equalsIgnoreCase(method)) {
79+
// OPTIONS (preflight) - respond with full allowed methods
80+
response.setStatus(HttpServletResponse.SC_OK);
81+
return;
82+
}
83+
} else {
84+
logger.warn("Origin [{}] is NOT allowed. CORS headers NOT added.", origin);
85+
86+
if ("OPTIONS".equalsIgnoreCase(method)) {
87+
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Origin not allowed for OPTIONS request");
88+
return;
89+
}
90+
}
6291
logger.info("JwtUserIdValidationFilter invoked for path: " + path);
6392

6493
// Log cookies for debugging
@@ -145,8 +174,7 @@ private boolean isOriginAllowed(String origin) {
145174
.anyMatch(pattern -> {
146175
String regex = pattern
147176
.replace(".", "\\.")
148-
.replace("*", ".*")
149-
.replace("http://localhost:.*", "http://localhost:\\d+"); // special case for wildcard port
177+
.replace("*", ".*");
150178

151179
boolean matched = origin.matches(regex);
152180
return matched;
@@ -180,4 +208,13 @@ private void clearUserIdCookie(HttpServletResponse response) {
180208
cookie.setMaxAge(0); // Invalidate the cookie
181209
response.addCookie(cookie);
182210
}
211+
212+
private void addCorsHeaders(HttpServletResponse response, String origin) {
213+
response.setHeader("Access-Control-Allow-Origin", origin); // Never use wildcard
214+
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
215+
response.setHeader("Access-Control-Allow-Headers",
216+
"Authorization, Content-Type, Accept, Jwttoken, serverAuthorization, ServerAuthorization, serverauthorization, Serverauthorization");
217+
response.setHeader("Access-Control-Allow-Credentials", "true");
218+
response.setHeader("Access-Control-Max-Age", "3600");
219+
}
183220
}

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.slf4j.Logger;
2525
import org.slf4j.LoggerFactory;
2626
import org.springframework.beans.factory.annotation.Autowired;
27+
import org.springframework.beans.factory.annotation.Value;
2728
import org.springframework.stereotype.Component;
2829
import org.springframework.web.servlet.HandlerInterceptor;
2930
import org.springframework.web.servlet.ModelAndView;
@@ -36,11 +37,16 @@
3637
import jakarta.ws.rs.core.MediaType;
3738
import java.io.OutputStream;
3839
import java.io.PrintStream;
40+
import java.util.Arrays;
3941

4042
@Component
4143
public class HTTPRequestInterceptor implements HandlerInterceptor {
4244

4345
Logger logger = LoggerFactory.getLogger(this.getClass().getName());
46+
47+
@Value("${cors.allowed-origins}")
48+
private String allowedOrigins;
49+
4450
@Autowired
4551
private RedisStorage redisStorage;
4652

@@ -105,7 +111,13 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
105111

106112
response.setContentType(MediaType.APPLICATION_JSON);
107113

108-
response.setHeader("Access-Control-Allow-Origin", "*");
114+
String origin = request.getHeader("Origin");
115+
if (origin != null && isOriginAllowed(origin)) {
116+
response.setHeader("Access-Control-Allow-Origin", origin);
117+
response.setHeader("Access-Control-Allow-Credentials", "true");
118+
} else if (origin != null) {
119+
logger.warn("CORS headers NOT added for error response | Unauthorized origin: {}", origin);
120+
}
109121
status = false;
110122
}
111123
}
@@ -144,4 +156,19 @@ public void afterCompletion(HttpServletRequest request, HttpServletResponse resp
144156

145157
}
146158

159+
private boolean isOriginAllowed(String origin) {
160+
if (origin == null || allowedOrigins == null || allowedOrigins.trim().isEmpty()) {
161+
return false;
162+
}
163+
164+
return Arrays.stream(allowedOrigins.split(","))
165+
.map(String::trim)
166+
.anyMatch(pattern -> {
167+
String regex = pattern
168+
.replace(".", "\\.")
169+
.replace("*", ".*");
170+
return origin.matches(regex);
171+
});
172+
}
173+
147174
}

0 commit comments

Comments
 (0)