diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties index f0ed942..b8941f0 100644 --- a/src/main/environment/common_ci.properties +++ b/src/main/environment/common_ci.properties @@ -25,4 +25,6 @@ logging.file.name=@env.SCHEDULER_API_LOGGING_FILE_NAME@ springdoc.api-docs.enabled=@env.SWAGGER_DOC_ENABLED@ springdoc.swagger-ui.enabled=@env.SWAGGER_DOC_ENABLED@ +cors.allowed-origins=@CORS_ALLOWED_ORIGINS@ + diff --git a/src/main/environment/common_example.properties b/src/main/environment/common_example.properties index d6b4463..8ac4d97 100644 --- a/src/main/environment/common_example.properties +++ b/src/main/environment/common_example.properties @@ -22,3 +22,4 @@ jwt.secret=my-32-character-ultra-secure-and-ultra-long-secret logging.path=logs/ logging.file.name=logs/scheduler-api.log +cors.allowed-origins=http://localhost:* \ No newline at end of file diff --git a/src/main/java/com/iemr/tm/config/CorsConfig.java b/src/main/java/com/iemr/tm/config/CorsConfig.java new file mode 100644 index 0000000..19166c4 --- /dev/null +++ b/src/main/java/com/iemr/tm/config/CorsConfig.java @@ -0,0 +1,24 @@ +package com.iemr.tm.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class CorsConfig implements WebMvcConfigurer { + + @Value("${cors.allowed-origins}") + private String allowedOrigins; + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOriginPatterns(allowedOrigins.split(",")) + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("*") + .exposedHeaders("Authorization", "Jwttoken") // Explicitly expose headers if needed + .allowCredentials(true) + .maxAge(3600); + } +} \ No newline at end of file diff --git a/src/main/java/com/iemr/tm/controller/schedule/SchedulingController.java b/src/main/java/com/iemr/tm/controller/schedule/SchedulingController.java index 3cb6812..54113b9 100644 --- a/src/main/java/com/iemr/tm/controller/schedule/SchedulingController.java +++ b/src/main/java/com/iemr/tm/controller/schedule/SchedulingController.java @@ -52,7 +52,7 @@ public class SchedulingController { @Autowired private SchedulingService schedulingService; - @CrossOrigin() + @Operation(summary = "Mark availability of specialist") @RequestMapping(value = "markavailability", method = RequestMethod.POST) public String markavailability(@RequestBody String specialistInput1) { @@ -72,7 +72,7 @@ public String markavailability(@RequestBody String specialistInput1) { return response.toString(); } - @CrossOrigin() + @Operation(summary = "Mark unavailability of specialist") @RequestMapping(value = "unmarkavailability", method = RequestMethod.POST) public String unmarkavailability(@RequestBody String specialistInput1) { @@ -91,7 +91,7 @@ public String unmarkavailability(@RequestBody String specialistInput1) { return response.toString(); } - @CrossOrigin() + @Operation(summary = "Get available slots of specialist for a particular day") @RequestMapping(value = "getavailableSlot", method = RequestMethod.POST) public String getavailableSlot(@RequestBody String specialistInput1) { @@ -109,7 +109,7 @@ public String getavailableSlot(@RequestBody String specialistInput1) { return response.toString(); } - @CrossOrigin() + @Operation(summary = "Get available slots of specialist for a particular month") @RequestMapping(value = { "/monthview/{year}", "/monthview/{year}/{month}", "/monthview/{year}/{month}/{day}" }, method = RequestMethod.POST) @@ -131,7 +131,7 @@ public String view(@RequestBody String specialistInput1, @PathVariable("year") I return response.toString(); } - @CrossOrigin() + @Operation(summary = "Book available slots of specialist of a particular day") @RequestMapping(value = "bookSlot", method = RequestMethod.POST) public String bookSlot(@RequestBody String specialistInput1) { @@ -149,7 +149,7 @@ public String bookSlot(@RequestBody String specialistInput1) { return response.toString(); } - @CrossOrigin() + @Operation(summary = "Cancel booked slots of specialist of a particular day") @RequestMapping(value = "cancelBookedSlot", method = RequestMethod.POST) public String cancelBookedSlot(@RequestBody String specialistInput1) { @@ -167,7 +167,7 @@ public String cancelBookedSlot(@RequestBody String specialistInput1) { return response.toString(); } - @CrossOrigin() + @Operation(summary = "Get day view of particular specialization") @RequestMapping(value = "getdayview", method = RequestMethod.POST) public String getdayview(@RequestBody String specialistInput1) { diff --git a/src/main/java/com/iemr/tm/controller/specialist/SpecialistController.java b/src/main/java/com/iemr/tm/controller/specialist/SpecialistController.java index c42e124..a1e51e5 100644 --- a/src/main/java/com/iemr/tm/controller/specialist/SpecialistController.java +++ b/src/main/java/com/iemr/tm/controller/specialist/SpecialistController.java @@ -50,7 +50,7 @@ public class SpecialistController { @Autowired private SpecialistService specialistService; - @CrossOrigin() + @Operation(summary = "Fetch master specialization") @RequestMapping(value = "masterspecialization", method = RequestMethod.POST) public String markavailability() { @@ -66,7 +66,7 @@ public String markavailability() { return response.toString(); } - @CrossOrigin() + @Operation(summary = "Fetch list of specialists") @RequestMapping(value = "getSpecialist", method = RequestMethod.POST) public String getSpecialist(@RequestBody Specialist specialist) { @@ -83,7 +83,7 @@ public String getSpecialist(@RequestBody Specialist specialist) { return response.toString(); } - @CrossOrigin() + @Operation(summary = "Fetch user specialist") @RequestMapping(value = "info/{userID}", method = RequestMethod.GET) public String info(@PathVariable("userID") Long userID) { @@ -101,7 +101,7 @@ public String info(@PathVariable("userID") Long userID) { return response.toString(); } - @CrossOrigin() + @Operation(summary = "Fetch all specialists") @RequestMapping(value = "getSpecialistAll", method = RequestMethod.POST) public String getSpecialistAll(@RequestBody Specialist specialist) { diff --git a/src/main/java/com/iemr/tm/controller/van/VanController.java b/src/main/java/com/iemr/tm/controller/van/VanController.java index 876f593..c723b78 100644 --- a/src/main/java/com/iemr/tm/controller/van/VanController.java +++ b/src/main/java/com/iemr/tm/controller/van/VanController.java @@ -46,7 +46,7 @@ public class VanController { @Autowired private VanService vanService; - @CrossOrigin() + @Operation(summary = "Fetch specialization by van id") @RequestMapping(value = "getvan/{vanid}", method = RequestMethod.GET) public String markavailability(@PathVariable("vanid") Integer vanid) { diff --git a/src/main/java/com/iemr/tm/utils/DynamicCorsFilter.java b/src/main/java/com/iemr/tm/utils/DynamicCorsFilter.java new file mode 100644 index 0000000..ce6d9db --- /dev/null +++ b/src/main/java/com/iemr/tm/utils/DynamicCorsFilter.java @@ -0,0 +1,38 @@ +package com.iemr.tm.utils; + +import java.io.IOException; +import java.util.Arrays; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@Component +public class DynamicCorsFilter extends OncePerRequestFilter { + + @Value("${cors.allowed-origins}") + private String[] allowedOrigins; + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) + throws ServletException, IOException { + + String origin = request.getHeader("Origin"); + if (origin != null && Arrays.asList(allowedOrigins).contains(origin)) { + response.setHeader("Access-Control-Allow-Origin", origin); + } + + if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { + response.setStatus(HttpServletResponse.SC_OK); + } else { + filterChain.doFilter(request, response); + } + } +} diff --git a/src/main/java/com/iemr/tm/utils/FilterConfig.java b/src/main/java/com/iemr/tm/utils/FilterConfig.java index 48aee27..802a3dd 100644 --- a/src/main/java/com/iemr/tm/utils/FilterConfig.java +++ b/src/main/java/com/iemr/tm/utils/FilterConfig.java @@ -1,19 +1,27 @@ package com.iemr.tm.utils; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; + @Configuration public class FilterConfig { + @Value("${cors.allowed-origins}") + private String allowedOrigins; + @Bean public FilterRegistrationBean jwtUserIdValidationFilter( JwtAuthenticationUtil jwtAuthenticationUtil) { FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); - registrationBean.setFilter(new JwtUserIdValidationFilter(jwtAuthenticationUtil)); + + // Pass allowedOrigins explicitly to the filter constructor + JwtUserIdValidationFilter filter = new JwtUserIdValidationFilter(jwtAuthenticationUtil, allowedOrigins); + + registrationBean.setFilter(filter); registrationBean.addUrlPatterns("/*"); // Apply filter to all API endpoints return registrationBean; } - } diff --git a/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java b/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java index 174ed73..c49639b 100644 --- a/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java +++ b/src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java @@ -1,9 +1,11 @@ package com.iemr.tm.utils; import java.io.IOException; +import java.util.Arrays; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import com.iemr.tm.utils.http.AuthorizationHeaderRequestWrapper; @@ -22,9 +24,12 @@ public class JwtUserIdValidationFilter implements Filter { private final JwtAuthenticationUtil jwtAuthenticationUtil; private final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); + private final String allowedOrigins; - public JwtUserIdValidationFilter(JwtAuthenticationUtil jwtAuthenticationUtil) { + public JwtUserIdValidationFilter(JwtAuthenticationUtil jwtAuthenticationUtil, + @Value("${cors.allowed-origins}") String allowedOrigins) { this.jwtAuthenticationUtil = jwtAuthenticationUtil; + this.allowedOrigins = allowedOrigins; } @Override @@ -37,6 +42,21 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo String contextPath = request.getContextPath(); logger.info("JwtUserIdValidationFilter invoked for path: " + path); + String origin = request.getHeader("Origin"); + 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("Access-Control-Allow-Credentials", "true"); + } + + if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { + logger.info("OPTIONS request - skipping JWT validation"); + response.setStatus(HttpServletResponse.SC_OK); + return; + } + + // Log cookies for debugging Cookie[] cookies = request.getCookies(); if (cookies != null) { @@ -110,6 +130,17 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authorization error: " + e.getMessage()); } } + + private boolean isOriginAllowed(String origin) { + if (origin == null || allowedOrigins == null || allowedOrigins.trim().isEmpty()) { + logger.warn("No allowed origins configured or origin is null"); + return false; + } + + return Arrays.stream(allowedOrigins.split(",")).map(String::trim) + .anyMatch(pattern -> origin.matches(pattern.replace(".", "\\.").replace("*", ".*"))); + } + private boolean isMobileClient(String userAgent) { if (userAgent == null) return false;