Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jwt api implementation #48

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 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
64 changes: 38 additions & 26 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,25 +28,21 @@
<org.mapstruct.version>1.2.0.Final</org.mapstruct.version>
</properties>
<dependencies>
<!--
https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>3.2.2</version>
</dependency>

<dependency>
<groupId>co.elastic.logging</groupId>
<artifactId>logback-ecs-encoder</artifactId>
<version>1.3.2</version>
<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-log4j2</artifactId>
<version>3.2.2</version>
</dependency>-->
<!--<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId>
<version>3.2.2</version> </dependency> -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
Expand Down Expand Up @@ -101,8 +97,7 @@
<version>8.2.0</version>
</dependency>

<!--
https://mvnrepository.com/artifact/jakarta.persistence/jakarta.persistence-api -->
<!-- https://mvnrepository.com/artifact/jakarta.persistence/jakarta.persistence-api -->
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
Expand Down Expand Up @@ -169,15 +164,14 @@
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.5</version>
<!-- <exclusions>-->
<!-- <exclusion>-->
<!-- <groupId>org.apache.commons</groupId>-->
<!-- <artifactId>commons-compress</artifactId>-->
<!-- </exclusion>-->
<!-- </exclusions>-->
</dependency>
<!--
https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<!-- <exclusions> -->
<!-- <exclusion> -->
<!-- <groupId>org.apache.commons</groupId> -->
<!-- <artifactId>commons-compress</artifactId> -->
<!-- </exclusion> -->
<!-- </exclusions> -->
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
Expand Down Expand Up @@ -219,8 +213,7 @@
<artifactId>jackson-datatype-joda</artifactId>
<version>2.17.0</version>
</dependency>
<!--
https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
Expand All @@ -232,6 +225,26 @@
<artifactId>jackson-core</artifactId>
<version>2.17.0-rc1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-jackson -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
</dependencies>


Expand Down Expand Up @@ -315,8 +328,7 @@
${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
1 change: 1 addition & 0 deletions src/main/environment/ecd_ci.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ [email protected]_API_BASE_URL@/beneficiary/create

##Beneficiary Edit Url
beneficiaryEditUrl [email protected]_API_BASE_URL@/beneficiary/update
[email protected]_SECRET_KEY@
1 change: 1 addition & 0 deletions src/main/environment/ecd_dev.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ registerBeneficiaryUrl=<Enter your socket address here>/commonapi-v1.0/beneficia

##Beneficiary Edit Url
beneficiaryEditUrl =<Enter your socket address here>/commonapi-v1.0/beneficiary/update
jwt.secret=
1 change: 1 addition & 0 deletions src/main/environment/ecd_test.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ registerBeneficiaryUrl=<Enter your socket address here>/commonapi-v1.0/beneficia

##Beneficiary Edit Url
beneficiaryEditUrl =<Enter your socket address here>/commonapi-v1.0/beneficiary/update
jwt.secret=
1 change: 1 addition & 0 deletions src/main/environment/ecd_uat.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ registerBeneficiaryUrl=<Enter your socket address here>/commonapi-v1.0/beneficia

##Beneficiary Edit Url
beneficiaryEditUrl =<Enter your socket address here>/commonapi-v1.0/beneficiary/update
jwt.secret=
22 changes: 22 additions & 0 deletions src/main/java/com/iemr/ecd/EcdApiApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.iemr.ecd.dao.Users;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.info.Contact;
Expand All @@ -37,4 +44,19 @@ public static void main(String[] args) {
SpringApplication.run(EcdApiApplication.class, args);
}

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);

// Use StringRedisSerializer for keys (userId)
template.setKeySerializer(new StringRedisSerializer());

// Use Jackson2JsonRedisSerializer for values (Users objects)
Jackson2JsonRedisSerializer<Users> serializer = new Jackson2JsonRedisSerializer<>(Users.class);
template.setValueSerializer(serializer);

return template;
}
Comment on lines +47 to +60
Copy link

Choose a reason for hiding this comment

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

πŸ› οΈ Refactor suggestion

Move Redis configuration to RedisConfig class

Redis configuration should be centralized in the RedisConfig class rather than split across multiple files. This violates the Single Responsibility Principle.

  1. Remove this RedisTemplate bean
  2. Update the one in RedisConfig to handle both Users and Object types if needed
  3. Use proper qualifiers for different template instances


}
23 changes: 23 additions & 0 deletions src/main/java/com/iemr/ecd/config/RedisConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,19 @@
package com.iemr.ecd.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.iemr.ecd.dao.Users;

@Configuration
@EnableCaching
Copy link

Choose a reason for hiding this comment

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

πŸ’‘ Codebase verification

Redis security configuration needs enhancement

The Redis configuration is currently using default settings with no security measures:

  • Redis is configured to run on localhost:6379 without password protection
  • No SSL/TLS configuration is present
  • No connection timeout settings are defined

This setup is insufficient for a production environment, especially when handling JWT session data. Consider adding the following properties to application.properties:

  • spring.redis.password
  • spring.redis.ssl=true
  • spring.redis.timeout
  • spring.redis.connect-timeout
πŸ”— Analysis chain

Verify Redis security configuration

The Redis configuration lacks security settings. When implementing JWT authentication, it's crucial to secure the Redis instance that might store sensitive session data.

Consider adding:

  • Redis password configuration
  • SSL/TLS settings
  • Connection timeout settings
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if Redis security configuration exists in properties files
rg -l "spring.redis.(password|ssl|timeout)" --type properties

Length of output: 98


Script:

#!/bin/bash
# Search for application properties/yaml files and Redis configuration
fd -e properties -e yml -e yaml | xargs rg -l "spring.redis"

Length of output: 102


Script:

#!/bin/bash
# Search for any Redis-related security configuration in Java files
rg -l "RedisPassword|RedisSsl|RedisTimeout|RedisSecur" --type java

Length of output: 68


Script:

#!/bin/bash
# Look for Redis configuration beans and security settings
ast-grep --pattern 'class $_ {
  $$$
  @Bean
  $$$RedisTemplate$$$
  $$$
}'

Length of output: 77


Script:

#!/bin/bash
# Check for any Redis security configuration in XML files
fd -e xml | xargs rg -l "spring.redis|redis.security"

Length of output: 55


Script:

#!/bin/bash
# Check the content of application.properties for Redis configuration
cat src/main/resources/application.properties

Length of output: 1840


Script:

#!/bin/bash
# Look for Redis connection configuration in Java files
rg "spring.redis" --type java -A 3

Length of output: 765


Script:

#!/bin/bash
# Search for Redis connection properties being set programmatically
ast-grep --pattern 'RedisStandaloneConfiguration $_ = new RedisStandaloneConfiguration();'

Length of output: 92

public class RedisConfig {

private @Value("${spring.redis.host}") String redisHost;
Expand All @@ -37,4 +45,19 @@ LettuceConnectionFactory lettuceConnectionFactory() {
return new LettuceConnectionFactory(redisHost, redisPort);
}

@Bean
public RedisTemplate<String, Users> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Users> template = new RedisTemplate<>();
template.setConnectionFactory(factory);

// Use StringRedisSerializer for keys (userId)
template.setKeySerializer(new StringRedisSerializer());

// Use Jackson2JsonRedisSerializer for values (Users objects)
Jackson2JsonRedisSerializer<Users> serializer = new Jackson2JsonRedisSerializer<>(Users.class);
template.setValueSerializer(serializer);
Comment on lines +56 to +58
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Security consideration: Add type information to Jackson serializer

The current Jackson serializer configuration might be vulnerable to deserialization attacks. Consider enabling default typing for proper polymorphic type handling.

 Jackson2JsonRedisSerializer<Users> serializer = new Jackson2JsonRedisSerializer<>(Users.class);
+ObjectMapper mapper = new ObjectMapper();
+mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
+serializer.setObjectMapper(mapper);
 template.setValueSerializer(serializer);
πŸ“ 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
// Use Jackson2JsonRedisSerializer for values (Users objects)
Jackson2JsonRedisSerializer<Users> serializer = new Jackson2JsonRedisSerializer<>(Users.class);
template.setValueSerializer(serializer);
// Use Jackson2JsonRedisSerializer for values (Users objects)
Jackson2JsonRedisSerializer<Users> serializer = new Jackson2JsonRedisSerializer<>(Users.class);
ObjectMapper mapper = new ObjectMapper();
mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(mapper);
template.setValueSerializer(serializer);


return template;
}
Comment on lines +48 to +61
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Potential bean conflict with EcdApiApplication RedisTemplate

There are two RedisTemplate bean definitions:

  1. RedisTemplate<String, Users> here
  2. RedisTemplate<String, Object> in EcdApiApplication

This could lead to autowiring conflicts and unexpected behavior.

Consider:

  1. Consolidating these beans into a single configuration class
  2. Using distinct bean names if both are needed:
 @Bean
+@Qualifier("usersRedisTemplate")
 public RedisTemplate<String, Users> redisTemplate(RedisConnectionFactory factory) {
πŸ“ 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
@Bean
public RedisTemplate<String, Users> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Users> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// Use StringRedisSerializer for keys (userId)
template.setKeySerializer(new StringRedisSerializer());
// Use Jackson2JsonRedisSerializer for values (Users objects)
Jackson2JsonRedisSerializer<Users> serializer = new Jackson2JsonRedisSerializer<>(Users.class);
template.setValueSerializer(serializer);
return template;
}
@Bean
@Qualifier("usersRedisTemplate")
public RedisTemplate<String, Users> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Users> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// Use StringRedisSerializer for keys (userId)
template.setKeySerializer(new StringRedisSerializer());
// Use Jackson2JsonRedisSerializer for values (Users objects)
Jackson2JsonRedisSerializer<Users> serializer = new Jackson2JsonRedisSerializer<>(Users.class);
template.setValueSerializer(serializer);
return template;
}


}
103 changes: 103 additions & 0 deletions src/main/java/com/iemr/ecd/dao/Users.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package com.iemr.ecd.dao;

import java.io.Serializable;
import java.sql.Timestamp;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.google.gson.annotations.Expose;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;

@Data
@Entity
@Table(name = "m_user")
Comment on lines +17 to +19
Copy link

Choose a reason for hiding this comment

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

πŸ› οΈ Refactor suggestion

Add validation constraints

The entity lacks input validation annotations for critical fields.

Add appropriate validations:

 @Data
 @Entity
 @Table(name = "m_user")
+@EntityListeners(AuditingEntityListener.class)
 public class Users {

Example field validations:

+ @NotBlank(message = "Username is required")
+ @Size(min = 3, max = 50)
  private String userName;

+ @Email(message = "Invalid email format")
  private String emailID;

+ @Pattern(regexp = "^[0-9]{12}$", message = "Invalid Aadhaar format")
  private String aadhaarNo;

Committable suggestion skipped: line range outside the PR's diff.

@JsonIgnoreProperties(ignoreUnknown = true)
public class Users implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Expose
@Column(name = "UserID")
private Long userID;
@Expose
@Column(name = "TitleID") private Short titleID;
@Expose
@Column(name = "FirstName")
private String firstName;
@Expose
@Column(name = "MiddleName")
private String middleName;
@Expose
@Column(name = "lastName")
private String lastName;
@Expose
@Column(name = "GenderID")
private Short genderID;
@Expose
@Column(name = "MaritalStatusID")
private Short maritalStatusID;
@Expose
@Column(name = "AadhaarNo")
private String aadhaarNo;
@Expose
@Column(name = "PAN")
private String pan;
Comment on lines +45 to +49
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: PII data protection required

Aadhaar and PAN are sensitive PII (Personally Identifiable Information) that require special handling:

  1. Should be encrypted at rest
  2. Should not be exposed in JSON responses
  3. Access should be logged

Recommendations:

  1. Implement field-level encryption
  2. Remove @expose annotations
  3. Add audit logging
  4. Consider implementing data masking

@Expose
@Column(name = "DOB")
private Timestamp dob;
@Expose
@Column(name = "DOJ")
private Timestamp doj;
@Expose
@Column(name = "QualificationID")
private Integer qualificationID;
@Expose
@Column(name = "UserName")
private String userName;
@Expose
@Column(name = "Password")
private String password;
Comment on lines +63 to +64
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Critical: Secure password handling required

The password field lacks proper security measures:

  1. No encryption/hashing annotations
  2. @expose annotation could leak passwords in JSON responses

Add password hashing and remove @expose:

- @Expose
  @Column(name = "Password")
  private String password;

Consider using:

  • Spring Security's password encoder
  • @JsonIgnore to prevent serialization

Committable suggestion skipped: line range outside the PR's diff.

@Expose
@Column(name = "AgentID")
private String agentID;
@Expose
@Column(name = "AgentPassword")
private String agentPassword;
@Expose
@Column(name = "EmailID")
private String emailID;
@Expose
@Column(name = "StatusID")
private Short statusID;
@Expose
@Column(name = "EmergencyContactPerson")
private String emergencyContactPerson;
@Expose
@Column(name = "EmergencyContactNo")
private String emergencyContactNo;
@Expose
@Column(name = "IsSupervisor")
private Boolean isSupervisor;
@Expose
@Column(name = "Deleted")
private Boolean deleted;
@Expose
@Column(name = "CreatedBy")
private String createdBy;
@Expose
@Column(name = "CreatedDate")
private Timestamp createdDate;
@Expose
@Column(name = "ModifiedBy")
private String modifiedBy;
@Expose
@Column(name = "LastModDate")
private Timestamp lastModDate;

}

31 changes: 31 additions & 0 deletions src/main/java/com/iemr/ecd/utils/mapper/CookieUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.iemr.ecd.utils.mapper;

import java.util.Arrays;
import java.util.Optional;

import org.springframework.stereotype.Service;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@Service
public class CookieUtil {

public Optional<String> getCookieValue(HttpServletRequest request, String cookieName) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookieName.equals(cookie.getName())) {
return Optional.of(cookie.getValue());
}
}
}
return Optional.empty();
}

public String getJwtTokenFromCookie(HttpServletRequest request) {
return Arrays.stream(request.getCookies()).filter(cookie -> "Jwttoken".equals(cookie.getName()))
.map(Cookie::getValue).findFirst().orElse(null);
}
}
19 changes: 19 additions & 0 deletions src/main/java/com/iemr/ecd/utils/mapper/FilterConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.iemr.ecd.utils.mapper;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {

@Bean
public FilterRegistrationBean<JwtUserIdValidationFilter> jwtUserIdValidationFilter(
JwtAuthenticationUtil jwtAuthenticationUtil) {
FilterRegistrationBean<JwtUserIdValidationFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new JwtUserIdValidationFilter(jwtAuthenticationUtil));
registrationBean.addUrlPatterns("/*"); // Apply filter to all API endpoints
return registrationBean;
}

}
Loading