Skip to content
Closed
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
2 changes: 1 addition & 1 deletion .factorypath
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<factorypath>
<factorypathentry kind="VARJAR" id="M2_REPO/org/projectlombok/lombok/1.18.36/lombok-1.18.36.jar" enabled="true" runInBatchMode="false"/>
<factorypathentry kind="VARJAR" id="M2_REPO/org/projectlombok/lombok/1.18.30/lombok-1.18.30.jar" enabled="true" runInBatchMode="false"/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify Lombok version consistency between .factorypath and pom.xml
echo "Checking Lombok version in pom.xml:"
rg -n "lombok" pom.xml -A2 -B2 | head -20

echo ""
echo "Checking .factorypath Lombok entry:"
cat .factorypath

Repository: PSMRI/FHIR-API

Length of output: 1775


🏁 Script executed:

#!/bin/bash
# Verify Lombok version in pom.xml and .factorypath
echo "=== Checking pom.xml for Lombok version ==="
if [ -f pom.xml ]; then
    rg -n "lombok" pom.xml -A 3 -B 1
else
    echo "pom.xml not found"
fi

echo ""
echo "=== Checking .factorypath for Lombok version ==="
if [ -f .factorypath ]; then
    cat .factorypath
else
    echo ".factorypath not found"
fi

Repository: PSMRI/FHIR-API

Length of output: 1205


Update .factorypath Lombok version to match pom.xml.

pom.xml declares Lombok 1.18.36 (line 30: <org.projectlombok.version>1.18.36</org.projectlombok.version>), but .factorypath specifies 1.18.30. Update the .factorypath entry to lombok-1.18.36.jar to prevent annotation processor version mismatches during compilation.

πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.factorypath at line 2, The .factorypath entry references an older Lombok
JAR (lombok-1.18.30.jar) which mismatches the pom.xml's
org.projectlombok.version (1.18.36); update the factorypathentry element's id
attribute to use lombok-1.18.36.jar so the VARJAR id matches the pom version
(look for the factorypathentry line containing kind="VARJAR" and
id="...lombok-1.18.30/...").

<factorypathentry kind="VARJAR" id="M2_REPO/org/mapstruct/mapstruct-processor/1.2.0.Final/mapstruct-processor-1.2.0.Final.jar" enabled="true" runInBatchMode="false"/>
</factorypath>

Large diffs are not rendered by default.

104 changes: 95 additions & 9 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</parent>
<groupId>com.wipro.fhir.r4</groupId>
<artifactId>fhir-api</artifactId>
<version>3.1.0</version>
<version>3.6.1</version>
<packaging>war</packaging>
<name>FHIR-API</name>
<description>FHIR_NDHM standard integration in AMRIT</description>
Expand Down Expand Up @@ -53,6 +53,30 @@
</properties>

<dependencies>
<dependency>
<groupId>co.elastic.logging</groupId>
<artifactId>logback-ecs-encoder</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.11.0</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.11.0</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down Expand Up @@ -186,39 +210,74 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.14.2</version>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.2</version>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.14.2</version>
<version>2.15.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/ca.uhn.hapi.fhir/hapi-fhir-structures-r4 -->
<!--
https://mvnrepository.com/artifact/ca.uhn.hapi.fhir/hapi-fhir-structures-r4 -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>6.10.0</version>
<version>8.4.0</version>
</dependency>

<!--
https://mvnrepository.com/artifact/ca.uhn.hapi.fhir/hapi-fhir-validation -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation</artifactId>
<version>8.4.0</version>
</dependency>

<!--
https://mvnrepository.com/artifact/ca.uhn.hapi.fhir/hapi-fhir-structures-r4 -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>8.4.0</version>
</dependency>


<!-- Choose a HAPI version that matches the rest of your modules -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-caching-caffeine</artifactId>
<version>8.4.0</version> <!-- example; align with your HAPI version -->
</dependency>



<dependency>
<groupId>ca.uhn.hapi</groupId>
<artifactId>hapi-base</artifactId>
<version>2.6.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/ca.uhn.hapi.fhir/org.hl7.fhir.utilities -->
<!--
https://mvnrepository.com/artifact/ca.uhn.hapi.fhir/org.hl7.fhir.utilities -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.utilities</artifactId>
<version>6.5.18</version>
</dependency>

<!--
https://mvnrepository.com/artifact/ca.uhn.hapi.fhir/hapi-fhir-validation-resources-r4 -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-r4</artifactId>
<version>8.4.0</version>
</dependency>

<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
Expand Down Expand Up @@ -363,7 +422,8 @@
${target-properties} and
${source-properties}
</echo>
<concat destfile="${target-properties}" append="yes"
<concat destfile="${target-properties}"
append="yes"
force="yes">
<fileset file="${source-properties}">
</fileset>
Expand Down Expand Up @@ -451,6 +511,32 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
<version>7.0.0</version>
<executions>
<execution>
<id>get-the-git-infos</id>
<goals>
<goal>revision</goal>
</goals>
<phase>initialize</phase>
</execution>
</executions>
<configuration>
<generateGitPropertiesFile>true</generateGitPropertiesFile>
<generateGitPropertiesFilename>${project.build.outputDirectory}/git.properties</generateGitPropertiesFilename>
<includeOnlyProperties>
<property>^git.branch$</property>
<property>^git.commit.id.abbrev$</property>
<property>^git.build.version$</property>
<property>^git.build.time$</property>
</includeOnlyProperties>
<failOnNoGitDirectory>false</failOnNoGitDirectory>
<failOnUnableToExtractRepoInfo>false</failOnUnableToExtractRepoInfo>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
Expand Down
16 changes: 16 additions & 0 deletions src/main/environment/common_ci.properties
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ webLoginAbhaRequestOtp = @env.ABDM_PHR_URL@/login/abha/request/otp
webLoginAbhaVerify = @env.ABDM_PHR_URL@/login/abha/verify
webLoginPhrCard = @env.ABDM_PHR_URL@/login/profile/abha/phr-card

## ABDM V3 M2 APIs
generateTokenForLinkCareContext = @env.ABDM_HIECM_BASE_URL@/api/hiecm/v3/token/generate-token
linkCareContext = @env.ABDM_HIECM_BASE_URL@/api/hiecm/hip/v3/link/carecontext

x-CM-ID=@env.X_CM_ID@
abhaMode=@env.ABHA_MODE@

Expand All @@ -120,3 +124,15 @@ springdoc.swagger-ui.enabled=@env.SWAGGER_DOC_ENABLED@
spring.redis.host=@env.REDIS_HOST@

cors.allowed-origins=@env.CORS_ALLOWED_ORIGINS@

hipSystemUrl= @env.HIP_SYSTEM_URL@

# Elasticsearch Configuration
elasticsearch.host=@env.ELASTICSEARCH_HOST@
elasticsearch.port=@env.ELASTICSEARCH_PORT@
elasticsearch.username=@env.ELASTICSEARCH_USERNAME@
elasticsearch.password=@env.ELASTICSEARCH_PASSWORD@
elasticsearch.index.beneficiary=@env.ELASTICSEARCH_INDEX_BENEFICIARY@

# Enable/Disable ES (for gradual rollout)
elasticsearch.enabled=@env.ELASTICSEARCH_ENABLED@
20 changes: 19 additions & 1 deletion src/main/environment/common_docker.properties
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ webLoginAbhaRequestOtp = ${ABDM_PHR_URL}/login/abha/request/otp
webLoginAbhaVerify = ${ABDM_PHR_URL}/login/abha/verify
webLoginPhrCard = ${ABDM_PHR_URL}/login/profile/abha/phr-card

## ABDM V3 M2 APIs
generateTokenForLinkCareContext = @env.ABDM_HIECM_BASE_URL@/api/hiecm/v3/token/generate-token
linkCareContext = @env.ABDM_HIECM_BASE_URL@/api/hiecm/hip/v3/link/carecontext

x-CM-ID=${X_CM_ID}
abhaMode=${ABHA_MODE}

Expand All @@ -117,4 +121,18 @@ springdoc.api-docs.enabled=${SWAGGER_DOC_ENABLED}
springdoc.swagger-ui.enabled=${SWAGGER_DOC_ENABLED}

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


hipSystemUrl= ${HIP_SYSTEM_URL}

# Elasticsearch Configuration
elasticsearch.host=${ELASTICSEARCH_HOST}
elasticsearch.port=${ELASTICSEARCH_PORT}
elasticsearch.username=${ELASTICSEARCH_USERNAME}
elasticsearch.password=${ELASTICSEARCH_PASSWORD}
elasticsearch.index.beneficiary=${ELASTICSEARCH_INDEX_BENEFICIARY}

# Enable/Disable ES (for gradual rollout)
elasticsearch.enabled=${ELASTICSEARCH_ENABLED}

19 changes: 18 additions & 1 deletion src/main/environment/common_example.properties
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,10 @@
webLoginAbhaVerify = https://abhasbx.abdm.gov.in/abha/api/v3/phr/web/login/abha/verify
webLoginPhrCard = https://abhasbx.abdm.gov.in/abha/api/v3/phr/web/login/profile/abha/phr-card

## ABDM V3 M2 APIs
generateTokenForLinkCareContext = https://dev.abdm.gov.in/api/hiecm/v3/token/generate-token
linkCareContext = https://dev.abdm.gov.in/api/hiecm/hip/v3/link/carecontext

x-CM-ID= sbx
abhaMode=sbx

Expand All @@ -113,4 +117,17 @@
logging.path=logs/
logging.file.name=logs/fhir-api.log

cors.allowed-origins=http://localhost:*
cors.allowed-origins=http://localhost:*

hipSystemUrl= <Enter HIP request URL>

# Elasticsearch Configuration
elasticsearch.host=localhost
elasticsearch.port=9200
elasticsearch.username=elastic
elasticsearch.password=piramalES

Check warning on line 128 in src/main/environment/common_example.properties

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Make sure these credentials get revoked, changed, and removed from the code.

See more on https://sonarcloud.io/project/issues?id=PSMRI_FHIR-API&issues=AZ0FlRhEYc8zzi0uaR8X&open=AZ0FlRhEYc8zzi0uaR8X&pullRequest=140

Check notice

Code scanning / SonarCloud

Credentials should not be hard-coded Low

Make sure these credentials get revoked, changed, and removed from the code. See more on SonarQube Cloud
elasticsearch.index.beneficiary=beneficiary_index

# Enable/Disable ES (for gradual rollout)
elasticsearch.enabled=true
Comment on lines +127 to +132

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid concrete Elasticsearch credentials and enabled-by-default in example config.

Line 127–129 ships real-looking defaults, and Line 132 enables ES by default. In sample configs, this increases accidental insecure rollout risk.

Suggested change
-elasticsearch.username=elastic
-elasticsearch.password=piramalES
+elasticsearch.username=<Enter Elasticsearch username>
+elasticsearch.password=<Enter Elasticsearch password>
@@
-elasticsearch.enabled=true
+elasticsearch.enabled=false
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
elasticsearch.username=elastic
elasticsearch.password=piramalES
elasticsearch.index.beneficiary=beneficiary_index
# Enable/Disable ES (for gradual rollout)
elasticsearch.enabled=true
elasticsearch.username=<Enter Elasticsearch username>
elasticsearch.password=<Enter Elasticsearch password>
elasticsearch.index.beneficiary=beneficiary_index
# Enable/Disable ES (for gradual rollout)
elasticsearch.enabled=false
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/environment/common_example.properties` around lines 127 - 132, The
example properties expose concrete Elasticsearch credentials and enable
Elasticsearch by default; replace the hardcoded values for
elasticsearch.username, elasticsearch.password and any real-looking index names
(e.g., elasticsearch.index.beneficiary) with generic placeholders (like
${ELASTICSEARCH_USERNAME} / ${ELASTICSEARCH_PASSWORD} or <username>/<password>)
and update elasticsearch.enabled to false (or comment it) so ES is disabled by
default in the example; ensure keys remain the same (elasticsearch.username,
elasticsearch.password, elasticsearch.index.beneficiary, elasticsearch.enabled)
but contain placeholder values and a disabled default to prevent accidental
insecure rollouts.


2 changes: 2 additions & 0 deletions src/main/java/com/wipro/fhir/FhirApiApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,12 @@
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scheduling.annotation.EnableAsync;

import com.wipro.fhir.data.users.User;

@SpringBootApplication
@EnableAsync
public class FhirApiApplication {

public static void main(String[] args) {
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/com/wipro/fhir/config/CorsConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ public void addCorsMappings(CorsRegistry registry) {
Arrays.stream(allowedOrigins.split(","))
.map(String::trim)
.toArray(String[]::new))
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("Content-Type", "Authorization")
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")
.allowedHeaders("Authorization", "Content-Type", "Accept", "Jwttoken",
"serverAuthorization", "ServerAuthorization", "serverauthorization", "Serverauthorization")
Comment on lines +23 to +24

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

In Spring MVC CORS (WebMvcConfigurer), does allowedHeaders("*") allow all custom request headers (including different header-name casing) during preflight checks?

πŸ’‘ Result:

Yes, in Spring MVC CORS configuration via WebMvcConfigurer, setting allowedHeaders("*") allows all custom request headers during preflight checks, regardless of header name casing.

Citations:


🏁 Script executed:

# First, find the CorsConfig.java file and examine its content
fd CorsConfig.java

Repository: PSMRI/FHIR-API

Length of output: 109


🏁 Script executed:

# Check the size and content of CorsConfig.java
if [ -f "src/main/java/com/wipro/fhir/config/CorsConfig.java" ]; then
  wc -l "src/main/java/com/wipro/fhir/config/CorsConfig.java"
  echo "---"
  cat -n "src/main/java/com/wipro/fhir/config/CorsConfig.java"
fi

Repository: PSMRI/FHIR-API

Length of output: 1521


🏁 Script executed:

# Also check if there are other CORS-related configurations in the codebase
rg -i "corsconfig|@crossorigin|allowedheaders" --type java -l

Repository: PSMRI/FHIR-API

Length of output: 185


Replace hardcoded header list with wildcard to support custom headers required by FHIR integrations.

Lines 23–24 restrict allowed headers to a predefined list, which prevents custom headers and requires code changes when integrations add new headers. FHIR APIs commonly need flexible header support for various healthcare standards and vendor-specific integrations.

Suggested change
-                .allowedHeaders("Authorization", "Content-Type", "Accept", "Jwttoken",
-                        "serverAuthorization", "ServerAuthorization", "serverauthorization", "Serverauthorization")
+                .allowedHeaders("*")
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.allowedHeaders("Authorization", "Content-Type", "Accept", "Jwttoken",
"serverAuthorization", "ServerAuthorization", "serverauthorization", "Serverauthorization")
.allowedHeaders("*")
πŸ€– Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/wipro/fhir/config/CorsConfig.java` around lines 23 - 24, In
CorsConfig update the CORS configuration that currently calls
allowedHeaders("Authorization", "Content-Type", "Accept", "Jwttoken",
"serverAuthorization", "ServerAuthorization", "serverauthorization",
"Serverauthorization") to allow all/custom headers by replacing the hardcoded
header list with a wildcard (e.g., allowedHeaders("*")) so FHIR integrations can
send custom headers; locate this change in the CorsConfig class where
allowedHeaders(...) is invoked and remove the duplicated specific header
entries.

.exposedHeaders("Authorization")
.allowCredentials(true)
.maxAge(3600);
Expand Down
104 changes: 104 additions & 0 deletions src/main/java/com/wipro/fhir/config/ElasticsearchConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.wipro.fhir.config;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
@EnableAsync
public class ElasticsearchConfig {

@Value("${elasticsearch.host}")
private String esHost;

@Value("${elasticsearch.port}")
private int esPort;

@Value("${elasticsearch.username}")
private String esUsername;

@Value("${elasticsearch.password}")
private String esPassword;

@Value("${elasticsearch.connection.timeout:10000}")
private int connectionTimeout;

@Value("${elasticsearch.socket.timeout:120000}")
private int socketTimeout;

@Value("${elasticsearch.max.connections:200}")
private int maxConnections;

@Value("${elasticsearch.max.connections.per.route:100}")
private int maxConnectionsPerRoute;

@Bean
public ElasticsearchClient elasticsearchClient() {
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(esUsername, esPassword)
);

RestClientBuilder builder = RestClient.builder(
new HttpHost(esHost, esPort, "http")
);

// Apply timeout configurations
builder.setRequestConfigCallback(requestConfigBuilder ->
requestConfigBuilder
.setConnectTimeout(connectionTimeout)
.setSocketTimeout(socketTimeout)
.setConnectionRequestTimeout(connectionTimeout)
);

// Apply connection pool settings
builder.setHttpClientConfigCallback(httpClientBuilder ->
httpClientBuilder
.setDefaultCredentialsProvider(credentialsProvider)
.setMaxConnTotal(maxConnections)
.setMaxConnPerRoute(maxConnectionsPerRoute)
.setDefaultIOReactorConfig(
IOReactorConfig.custom()
.setSoTimeout(socketTimeout)
.build()
)
);

RestClient restClient = builder.build();

ElasticsearchTransport transport = new RestClientTransport(
restClient,
new JacksonJsonpMapper()
);

return new ElasticsearchClient(transport);
}

@Bean(name = "esAsyncExecutor")
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("es-sync-");
executor.setRejectedExecutionHandler(new java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
Loading