Skip to content

Commit bffa971

Browse files
authored
Merge pull request #110 from PSMRI/amm-1927
fix: amm-1927 send headers only if the request is from the allowed origin
2 parents 0acb505 + eb98a5a commit bffa971

3 files changed

Lines changed: 90 additions & 21 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: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,57 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
3737
HttpServletResponse response = (HttpServletResponse) servletResponse;
3838

3939
String origin = request.getHeader("Origin");
40-
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");
44-
response.setHeader("Access-Control-Allow-Credentials", "true");
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+
}
4559
} else {
46-
logger.warn("Origin [{}] is NOT allowed. CORS headers NOT added.", origin);
47-
}
48-
49-
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
50-
logger.info("OPTIONS request - skipping JWT validation");
51-
response.setStatus(HttpServletResponse.SC_OK);
52-
return;
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+
}
5366
}
5467

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

5993
// Log cookies for debugging
@@ -70,7 +104,7 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
70104
}
71105

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

75109
// Skip login and public endpoints
76110
if (path.equals(contextPath + "/user/userAuthenticate")
@@ -132,6 +166,15 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
132166
}
133167
}
134168

169+
private void addCorsHeaders(HttpServletResponse response, String origin) {
170+
response.setHeader("Access-Control-Allow-Origin", origin); // Never use wildcard
171+
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
172+
response.setHeader("Access-Control-Allow-Headers",
173+
"Authorization, Content-Type, Accept, Jwttoken, serverAuthorization, ServerAuthorization, serverauthorization, Serverauthorization");
174+
response.setHeader("Access-Control-Allow-Credentials", "true");
175+
response.setHeader("Access-Control-Max-Age", "3600");
176+
}
177+
135178
private boolean isOriginAllowed(String origin) {
136179
if (origin == null || allowedOrigins == null || allowedOrigins.trim().isEmpty()) {
137180
logger.warn("No allowed origins configured or origin is null");
@@ -144,14 +187,12 @@ private boolean isOriginAllowed(String origin) {
144187
String regex = pattern
145188
.replace(".", "\\.")
146189
.replace("*", ".*")
147-
.replace("http://localhost:.*", "http://localhost:\\d+"); // special case for wildcard port
148-
190+
.replace("http://localhost:.*", "http://localhost:\\d+");
191+
149192
boolean matched = origin.matches(regex);
150193
return matched;
151194
});
152-
}
153-
154-
private boolean isMobileClient(String userAgent) {
195+
} private boolean isMobileClient(String userAgent) {
155196
if (userAgent == null)
156197
return false;
157198
userAgent = userAgent.toLowerCase();

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

Lines changed: 28 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,20 @@ 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+
.replace("http://localhost:.*", "http://localhost:\\d+");
165+
return origin.matches(regex);
166+
});
167+
}
141168
}

0 commit comments

Comments
 (0)