Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# --- Stage 1: Build the application using Maven ---
FROM maven:3.9.6-eclipse-temurin-17 AS build

WORKDIR /app

COPY . .

# Build the application while caching Maven dependencies to speed up future builds
RUN --mount=type=cache,target=/root/.m2 \
mvn clean package -DENV_VAR=docker -DskipTests -Dgit.skip=true

# --- Stage 2: Run the application with a minimal JRE image ---
FROM eclipse-temurin:17-jre

WORKDIR /app

# Copy the built WAR file from the build stage
COPY --from=build /app/target/*.war app.war

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.war"]
12 changes: 12 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,18 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<reporting>
Expand Down
3 changes: 2 additions & 1 deletion src/main/environment/common_ci.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ callcentre-server-ip=@env.CALLCENTRE_SERVER_IP@
common-url=@env.COMMON_API@

### Redis IP
spring.redis.host=localhost
spring.redis.host=@env.REDIS_HOST@
jwt.secret=@env.JWT_SECRET_KEY@

#ELK logging file name
Expand All @@ -25,4 +25,5 @@ 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=@env.CORS_ALLOWED_ORIGINS@

24 changes: 24 additions & 0 deletions src/main/environment/common_docker.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# local env
# DB Connections
spring.datasource.url=${DATABASE_URL}
spring.datasource.username=${DATABASE_USERNAME}
spring.datasource.password=${DATABASE_PASSWORD}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.show-sql=true

spring.profiles.active=test

callcentre-server-ip=${CALLCENTRE_SERVER_IP}

common-url=${COMMON_API}

### Redis IP
spring.redis.host=${REDIS_HOST}
jwt.secret=${JWT_SECRET_KEY}

#ELK logging file name
logging.file.name=${SCHEDULER_API_LOGGING_FILE_NAME}

springdoc.api-docs.enabled=${SWAGGER_DOC_ENABLED}
springdoc.swagger-ui.enabled=${SWAGGER_DOC_ENABLED}
2 changes: 2 additions & 0 deletions src/main/environment/common_example.properties
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ 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:*

Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
Expand All @@ -43,7 +43,6 @@

import io.swagger.v3.oas.annotations.Operation;


@RestController
@RequestMapping(value = "/schedule", headers = "Authorization")
public class SchedulingController {
Expand All @@ -52,7 +51,6 @@ 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) {
Expand All @@ -72,7 +70,6 @@ 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) {
Expand All @@ -91,7 +88,6 @@ 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) {
Expand All @@ -109,7 +105,6 @@ 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)
Expand All @@ -131,7 +126,6 @@ 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) {
Expand All @@ -149,7 +143,6 @@ 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) {
Expand All @@ -167,7 +160,6 @@ 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
Expand All @@ -41,7 +41,6 @@

import io.swagger.v3.oas.annotations.Operation;


@RestController
@RequestMapping(value = "/specialist", headers = "Authorization")
public class SpecialistController {
Expand All @@ -50,7 +49,6 @@ public class SpecialistController {
@Autowired
private SpecialistService specialistService;

@CrossOrigin()
@Operation(summary = "Fetch master specialization")
@RequestMapping(value = "masterspecialization", method = RequestMethod.POST)
public String markavailability() {
Expand All @@ -66,7 +64,6 @@ 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) {
Expand All @@ -83,7 +80,6 @@ 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) {
Expand All @@ -101,7 +97,6 @@ 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) {
Expand Down
5 changes: 1 addition & 4 deletions src/main/java/com/iemr/tm/controller/van/VanController.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
Expand All @@ -36,8 +36,6 @@

import io.swagger.v3.oas.annotations.Operation;



@RestController
@RequestMapping(value = "/van", headers = "Authorization")
public class VanController {
Expand All @@ -46,7 +44,6 @@ 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) {
Expand Down
13 changes: 11 additions & 2 deletions src/main/java/com/iemr/tm/utils/FilterConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,26 @@
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.beans.factory.annotation.Value;

@Configuration
public class FilterConfig {

@Value("${cors.allowed-origins}")
private String allowedOrigins;

@Bean
public FilterRegistrationBean<JwtUserIdValidationFilter> jwtUserIdValidationFilter(
JwtAuthenticationUtil jwtAuthenticationUtil) {
FilterRegistrationBean<JwtUserIdValidationFilter> 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.setOrder(Ordered.HIGHEST_PRECEDENCE);
registrationBean.addUrlPatterns("/*"); // Apply filter to all API endpoints
return registrationBean;
}

}
49 changes: 47 additions & 2 deletions src/main/java/com/iemr/tm/utils/JwtUserIdValidationFilter.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.iemr.tm.utils;

import java.io.IOException;
import java.util.Arrays;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -17,14 +18,16 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Component
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,
String allowedOrigins) {
this.jwtAuthenticationUtil = jwtAuthenticationUtil;
this.allowedOrigins = allowedOrigins;
}

@Override
Expand All @@ -33,6 +36,27 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;

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

logger.debug("Incoming Origin: {}", origin);
logger.debug("Allowed Origins Configured: {}", allowedOrigins);

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-Credentials", "true");
} 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);
Expand Down Expand Up @@ -110,12 +134,33 @@ 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 -> {
String regex = pattern
.replace(".", "\\.")
.replace("*", ".*")
.replace("http://localhost:.*", "http://localhost:\\d+"); // special case for wildcard port

boolean matched = origin.matches(regex);
return matched;
});
}

private boolean isMobileClient(String userAgent) {
if (userAgent == null)
return false;
userAgent = userAgent.toLowerCase();
return userAgent.contains("okhttp");
}

private String getJwtTokenFromCookies(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
Expand Down
Loading