diff --git a/pom.xml b/pom.xml
index daafa7d..de05254 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,6 +23,7 @@
prescription-service
+ users-service
@@ -42,10 +43,6 @@
org.springframework.boot
spring-boot-starter-data-mongodb
-
- org.springframework.boot
- spring-boot-starter-data-redis
-
org.springframework.boot
spring-boot-starter-web
@@ -54,10 +51,6 @@
io.micrometer
micrometer-tracing-bridge-brave
-
- io.zipkin.reporter2
- zipkin-reporter-brave
-
org.projectlombok
diff --git a/users-service/pom.xml b/users-service/pom.xml
new file mode 100644
index 0000000..1d6b128
--- /dev/null
+++ b/users-service/pom.xml
@@ -0,0 +1,87 @@
+
+
+ 4.0.0
+
+
+ com.stackbytes
+ mediva-backend
+ 0.0.1-SNAPSHOT
+
+
+ org.example
+ users-service
+
+
+ 21
+ 21
+ UTF-8
+
+
+
+
+ de.svenkubiak
+ jBCrypt
+ 0.4
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ javax.servlet
+ javax.servlet-api
+ 4.0.1
+ provided
+
+
+
+ org.springframework.security
+ spring-security-jwt
+ 1.1.1.RELEASE
+
+
+
+
+ io.jsonwebtoken
+ jjwt
+ 0.9.1
+
+
+ javax.xml.bind
+ jaxb-api
+ 2.3.1
+
+
+
+
+ org.glassfish.jaxb
+ jaxb-runtime
+ 2.3.1
+
+
+
+
+ org.bouncycastle
+ bcpg-jdk15on
+ 1.70
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.70
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ 2.18.1
+
+
+
\ No newline at end of file
diff --git a/users-service/src/main/java/com/stackbytes/Main.java b/users-service/src/main/java/com/stackbytes/Main.java
new file mode 100644
index 0000000..050b4aa
--- /dev/null
+++ b/users-service/src/main/java/com/stackbytes/Main.java
@@ -0,0 +1,11 @@
+package com.stackbytes;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Main {
+ public static void main(String[] args) {
+ SpringApplication.run(Main.class, args);
+ }
+}
\ No newline at end of file
diff --git a/users-service/src/main/java/com/stackbytes/controllers/UserController.java b/users-service/src/main/java/com/stackbytes/controllers/UserController.java
new file mode 100644
index 0000000..6a2df21
--- /dev/null
+++ b/users-service/src/main/java/com/stackbytes/controllers/UserController.java
@@ -0,0 +1,35 @@
+package com.stackbytes.controllers;
+
+import com.stackbytes.models.LoginData;
+import com.stackbytes.models.RegisterRequestDto;
+import com.stackbytes.models.ResponseJson;
+import com.stackbytes.services.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import com.stackbytes.models.User;
+
+@RestController()
+@RequestMapping("/users")
+public class UserController {
+ @Autowired
+ private final UserService userService;
+ public UserController(UserService userService) {
+ this.userService = userService;
+ }
+ @CrossOrigin
+ @PostMapping("/login")
+ public ResponseJson loginUser(@RequestBody LoginData loginData) {
+ return userService.loginUser(loginData);
+ }
+ @CrossOrigin
+ @PostMapping("/register")
+ public ResponseJson registerUser(@RequestBody RegisterRequestDto registerRequestDto) throws Exception{
+ return userService.registerUser(registerRequestDto);
+ }
+ @CrossOrigin
+ @GetMapping("/test")
+ public String test() throws Exception{
+ return userService.test();
+ }
+}
diff --git a/users-service/src/main/java/com/stackbytes/models/ContactInfo.java b/users-service/src/main/java/com/stackbytes/models/ContactInfo.java
new file mode 100644
index 0000000..303cc62
--- /dev/null
+++ b/users-service/src/main/java/com/stackbytes/models/ContactInfo.java
@@ -0,0 +1,13 @@
+package com.stackbytes.models;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.Getter;
+
+@Builder
+@Getter
+@Data
+public class ContactInfo {
+ private String phone;
+ private String email;
+}
diff --git a/users-service/src/main/java/com/stackbytes/models/LoginData.java b/users-service/src/main/java/com/stackbytes/models/LoginData.java
new file mode 100644
index 0000000..56e1bd8
--- /dev/null
+++ b/users-service/src/main/java/com/stackbytes/models/LoginData.java
@@ -0,0 +1,14 @@
+package com.stackbytes.models;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.Getter;
+
+@Builder
+@Getter
+@Data
+public class LoginData {
+ private String email;
+ private String password;
+ private boolean isMedic;
+}
diff --git a/users-service/src/main/java/com/stackbytes/models/Medic.java b/users-service/src/main/java/com/stackbytes/models/Medic.java
new file mode 100644
index 0000000..562197b
--- /dev/null
+++ b/users-service/src/main/java/com/stackbytes/models/Medic.java
@@ -0,0 +1,34 @@
+package com.stackbytes.models;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.Getter;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.data.util.Pair;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+@Document(collection = "medics")
+@Data
+@Builder
+@Getter
+public class Medic {
+ @Id
+ private String id;
+ private String medicalId;
+ private String password;
+ private Date activeSince;
+ private String speciality;
+ private String grade;
+ private Pair gpg;
+ private String workPlace;
+ private double ratings;
+ private String bio;
+ private ContactInfo contactInfo;
+ private List userId;
+ private Date createdAt;
+ private Date updatedAt;
+}
diff --git a/users-service/src/main/java/com/stackbytes/models/RegisterRequestDto.java b/users-service/src/main/java/com/stackbytes/models/RegisterRequestDto.java
new file mode 100644
index 0000000..5e42f67
--- /dev/null
+++ b/users-service/src/main/java/com/stackbytes/models/RegisterRequestDto.java
@@ -0,0 +1,17 @@
+package com.stackbytes.models;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.Getter;
+
+@Builder
+@Getter
+@Data
+public class RegisterRequestDto {
+ private User user;
+ private Medic medic;
+ private boolean isMedic;
+ public boolean isMedic() {
+ return isMedic;
+ }
+}
diff --git a/users-service/src/main/java/com/stackbytes/models/ResponseJson.java b/users-service/src/main/java/com/stackbytes/models/ResponseJson.java
new file mode 100644
index 0000000..f8a6b79
--- /dev/null
+++ b/users-service/src/main/java/com/stackbytes/models/ResponseJson.java
@@ -0,0 +1,17 @@
+package com.stackbytes.models;
+
+import lombok.Builder;
+import lombok.Getter;
+import org.springframework.data.util.Pair;
+
+import java.util.HashMap;
+
+@Builder
+@Getter
+public class ResponseJson {
+ private int code;
+ private boolean status;
+ private String message;
+ private String token;
+ private Pair gpg;
+}
diff --git a/users-service/src/main/java/com/stackbytes/models/User.java b/users-service/src/main/java/com/stackbytes/models/User.java
new file mode 100644
index 0000000..1ce90c9
--- /dev/null
+++ b/users-service/src/main/java/com/stackbytes/models/User.java
@@ -0,0 +1,33 @@
+package com.stackbytes.models;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
+import org.springframework.scheduling.support.SimpleTriggerContext;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+
+@Document(collection = "users")
+@Builder
+@Getter
+@Setter
+@Data
+public class User {
+ @Id
+ private String id;
+ private String username;
+ private String email;
+ private String password;
+ private String fullName;
+ private String phone;
+ private String avatar;
+ private Medic medic;
+ private List doctorsId;
+ private Date createdAt;
+ private Date updatedAt;
+}
diff --git a/users-service/src/main/java/com/stackbytes/services/BouncyCastleSetup.java b/users-service/src/main/java/com/stackbytes/services/BouncyCastleSetup.java
new file mode 100644
index 0000000..451b157
--- /dev/null
+++ b/users-service/src/main/java/com/stackbytes/services/BouncyCastleSetup.java
@@ -0,0 +1,11 @@
+package com.stackbytes.services;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.security.Security;
+
+public class BouncyCastleSetup {
+ public static void setup() {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+}
diff --git a/users-service/src/main/java/com/stackbytes/services/GPGKeyGenerator2.java b/users-service/src/main/java/com/stackbytes/services/GPGKeyGenerator2.java
new file mode 100644
index 0000000..a183d31
--- /dev/null
+++ b/users-service/src/main/java/com/stackbytes/services/GPGKeyGenerator2.java
@@ -0,0 +1,65 @@
+package com.stackbytes.services;
+
+import jakarta.annotation.PostConstruct;
+import org.bouncycastle.openpgp.*;
+import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor;
+import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
+import org.bouncycastle.openpgp.operator.PGPDigestCalculator;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
+import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair;
+import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
+
+import java.io.ByteArrayOutputStream;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.util.Date;
+
+@Component
+public class GPGKeyGenerator2 {
+ @Autowired
+ private GetProperties getProperties;
+ @PostConstruct
+ public void init() {
+ BouncyCastleSetup.setup();
+ }
+
+ public PGPKeyRingGenerator generateKey(String id) throws Exception{
+ String passphrase = getProperties.getProperties("gpg.passphrase");
+ KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(2048);
+ KeyPair rsaKeyPair = keyPairGenerator.generateKeyPair();
+ PGPKeyPair pgpKeyPair = new JcaPGPKeyPair(PGPPublicKey.RSA_GENERAL, rsaKeyPair, new Date());
+ PGPDigestCalculator sha256Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get(PGPUtil.SHA1);
+ PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_256, sha256Calc)
+ .setProvider("BC")
+ .build(passphrase.toCharArray());
+ PGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(pgpKeyPair.getPublicKey().getAlgorithm(), PGPUtil.SHA1)
+ .setProvider("BC");
+ return new PGPKeyRingGenerator(
+ PGPSignature.POSITIVE_CERTIFICATION,
+ pgpKeyPair,
+ id,
+ sha256Calc,
+ null,
+ null,
+ contentSignerBuilder,
+ keyEncryptor
+ );
+ }
+ public byte[] getPublicKeyBytes(PGPKeyRingGenerator keyRingGenerator) throws Exception {
+ PGPPublicKeyRing publicKeyRing = keyRingGenerator.generatePublicKeyRing();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ publicKeyRing.encode(out);
+ return out.toByteArray();
+ }
+ public byte[] getPrivateKeyBytes(PGPKeyRingGenerator keyRingGenerator) throws Exception {
+ PGPSecretKeyRing secretKeyRing = keyRingGenerator.generateSecretKeyRing();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ secretKeyRing.encode(out);
+ return out.toByteArray();
+ }
+}
diff --git a/users-service/src/main/java/com/stackbytes/services/GetProperties.java b/users-service/src/main/java/com/stackbytes/services/GetProperties.java
new file mode 100644
index 0000000..4881686
--- /dev/null
+++ b/users-service/src/main/java/com/stackbytes/services/GetProperties.java
@@ -0,0 +1,22 @@
+package com.stackbytes.services;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+import java.io.FileInputStream;
+import java.util.Properties;
+
+@Configuration
+public class GetProperties {
+ public String getProperties(String key) {
+ Properties prop = new Properties();
+ try {
+ prop.load(new FileInputStream("users-service/src/main/resources/application.properties"));
+ String DB_URL = prop.getProperty(key);
+ return DB_URL;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+}
diff --git a/users-service/src/main/java/com/stackbytes/services/JwtUtil.java b/users-service/src/main/java/com/stackbytes/services/JwtUtil.java
new file mode 100644
index 0000000..acb9fe9
--- /dev/null
+++ b/users-service/src/main/java/com/stackbytes/services/JwtUtil.java
@@ -0,0 +1,32 @@
+package com.stackbytes.services;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.SignatureAlgorithm;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+@Configuration
+public class JwtUtil {
+ @Autowired
+ private GetProperties getProperties;
+ public String getToken(String jwtHeader) {
+ String key = getProperties.getProperties("jwt.secret");
+ Map claims = new HashMap<>();
+ try {
+ return Jwts.builder()
+ .setClaims(claims)
+ .setSubject(jwtHeader.toString())
+ .setIssuedAt(new Date(System.currentTimeMillis()))
+ .signWith(SignatureAlgorithm.HS256, key)
+ .compact();
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+}
diff --git a/users-service/src/main/java/com/stackbytes/services/SecurityConfig.java b/users-service/src/main/java/com/stackbytes/services/SecurityConfig.java
new file mode 100644
index 0000000..750bda3
--- /dev/null
+++ b/users-service/src/main/java/com/stackbytes/services/SecurityConfig.java
@@ -0,0 +1,18 @@
+package com.stackbytes.services;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.web.SecurityFilterChain;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig {
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ http
+ .csrf(csrf -> csrf.disable());
+ return http.build();
+ }
+}
diff --git a/users-service/src/main/java/com/stackbytes/services/UserService.java b/users-service/src/main/java/com/stackbytes/services/UserService.java
new file mode 100644
index 0000000..6911b40
--- /dev/null
+++ b/users-service/src/main/java/com/stackbytes/services/UserService.java
@@ -0,0 +1,155 @@
+package com.stackbytes.services;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ser.Serializers;
+import com.stackbytes.models.*;
+import org.bouncycastle.openpgp.PGPKeyRingGenerator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
+import org.springframework.data.util.Pair;
+import org.springframework.stereotype.Service;
+
+import org.mindrot.jbcrypt.BCrypt;
+
+import java.util.Base64;
+import java.util.Date;
+import java.util.HashMap;
+
+@Service
+public class UserService {
+ private final MongoTemplate mongoTemplate;
+ @Autowired
+ private GPGKeyGenerator2 gpgKeyGenerator2;
+ @Autowired
+ private JwtUtil jwtUtil;
+ @Autowired
+ private GetProperties getProperties;
+ private final ObjectMapper objectMapper;
+ public UserService(MongoTemplate mongoTemplate, ObjectMapper objectMapper) {
+ this.mongoTemplate = mongoTemplate;
+ this.objectMapper = objectMapper;
+ }
+ public ResponseJson verifyMedic(Medic medic) {
+ ContactInfo contactInfo = medic.getContactInfo();
+ Medic findMedic = mongoTemplate.findOne(new Query(Criteria.where("email").is(contactInfo.getEmail())), Medic.class);
+ if (findMedic == null) {
+ return ResponseJson.builder().code(404).status(false).message("Medic does not exist").build();
+ }
+ if (findMedic.getGpg() == null) {
+ return ResponseJson.builder().code(404).status(false).message("Medic does not have a GPG key").build();
+ }
+ try {
+ Pair gpg = medic.getGpg();
+ return ResponseJson.builder().code(200).status(true).message("Medic verified").gpg(gpg).build();
+ } catch (Exception e) {
+ return ResponseJson.builder().code(500).status(false).message("Internal server error").build();
+ }
+ }
+ public String test() throws Exception{
+ PGPKeyRingGenerator keyGenerator = gpgKeyGenerator2.generateKey("rares");
+ byte[] publicKey = gpgKeyGenerator2.getPublicKeyBytes(keyGenerator);
+ byte[] privateKey = gpgKeyGenerator2.getPrivateKeyBytes(keyGenerator);
+ return "public key : " + Base64.getEncoder().encodeToString(publicKey) + "\nprivate key : " + Base64.getEncoder().encodeToString(privateKey);
+ }
+ public ResponseJson loginUser(LoginData loginData) {
+ if (loginData.getEmail() == null || loginData.getPassword() == null) {
+ return ResponseJson.builder().code(404).status(false).message("Email or password cannot be null").build();
+ }
+ if(loginData.isMedic() == true) {
+ System.out.println("medic");
+ Medic medic = mongoTemplate.findOne(new Query(Criteria.where("contactInfo.email").is(loginData.getEmail())), Medic.class);
+ if (medic == null) {
+ return ResponseJson.builder().code(404).status(false).message("Medic not found").build();
+ }
+ if (BCrypt.checkpw(loginData.getPassword(), medic.getPassword())) {
+ String jsonStringTokenContent = null;
+ try {
+ jsonStringTokenContent = objectMapper.writeValueAsString(medic);
+ } catch (Exception e) {
+ return ResponseJson.builder().code(500).status(false).message(e.getMessage()).build();
+ }
+ String token = jwtUtil.getToken(jsonStringTokenContent);
+ return ResponseJson.builder().code(200).status(true).message("Medic logged in").token(token).build();
+ } else {
+ return ResponseJson.builder().code(404).status(false).message("Invalid password").build();
+ }
+ } else {
+ try {
+ System.out.println("user");
+ User user = mongoTemplate.findOne(new Query(Criteria.where("email").is(loginData.getEmail())), User.class);
+ System.out.println("here1");
+ if (user == null) {
+ return ResponseJson.builder().code(404).status(false).message("User not found").build();
+ }
+ if (BCrypt.checkpw(loginData.getPassword(), user.getPassword())) {
+ String jsonStringTokenContent = null;
+ try {
+ jsonStringTokenContent = objectMapper.writeValueAsString(user);
+ } catch (Exception e) {
+ return ResponseJson.builder().code(500).status(false).message(e.getMessage()).build();
+ }
+ String token = jwtUtil.getToken(jsonStringTokenContent);
+ return ResponseJson.builder().code(200).status(true).message("User logged in").token(token).build();
+ } else {
+ return ResponseJson.builder().code(404).status(false).message("Invalid password").build();
+ }
+ } catch (Exception e) {
+ return ResponseJson.builder().code(500).status(false).message(e.getMessage()).build();
+ }
+ }
+ }
+ public ResponseJson registerUser(RegisterRequestDto registerRequestDto) throws Exception{
+ if (registerRequestDto == null) {
+ return ResponseJson.builder().code(404).status(false).message("User cannot be null").build();
+ }
+ if (registerRequestDto.isMedic()) {
+ try {
+ Medic medic = registerRequestDto.getMedic();
+ ContactInfo contactInfo = medic.getContactInfo();
+ Medic findMedic = mongoTemplate.findOne(new Query(Criteria.where("contactInfo.email").is(contactInfo.getEmail())), Medic.class);
+ if (findMedic != null) {
+ return ResponseJson.builder().code(404).status(false).message("Medic already exists").build();
+ }
+ medic.setPassword(BCrypt.hashpw(medic.getPassword(), BCrypt.gensalt()));
+ medic.setCreatedAt(new Date());
+ medic.setUpdatedAt(new Date());
+ PGPKeyRingGenerator keyGenerator = gpgKeyGenerator2.generateKey(contactInfo.getEmail());
+ String publicKey = gpgKeyGenerator2.getPublicKeyBytes(keyGenerator).toString();
+ String privateKey = gpgKeyGenerator2.getPrivateKeyBytes(keyGenerator).toString();
+ String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getBytes());
+ String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getBytes());
+ Pair gpg = Pair.of(publicKeyString, privateKeyString);
+ medic.setGpg(gpg);
+ try {
+ mongoTemplate.insert(medic);
+ return ResponseJson.builder().code(200).status(true).message("Medic registered").build();
+ } catch (Exception e) {
+ return ResponseJson.builder().code(500).status(false).message(e.getMessage()).build();
+ }
+ } catch (Exception e) {
+ return ResponseJson.builder().code(500).status(false).message(e.getMessage()).build();
+ }
+ } else {
+ try {
+ User user = registerRequestDto.getUser();
+ User findUser = mongoTemplate.findOne(new Query(Criteria.where("email").is(user.getEmail())), User.class);
+ if (findUser != null) {
+ return ResponseJson.builder().code(404).status(false).message("User already exists").build();
+ }
+ user.setPassword(BCrypt.hashpw(user.getPassword(), BCrypt.gensalt()));
+ user.setCreatedAt(new Date());
+ user.setUpdatedAt(new Date());
+ try {
+ mongoTemplate.insert(user);
+ return ResponseJson.builder().code(200).status(true).message("User registered").build();
+ } catch (Exception e) {
+ return ResponseJson.builder().code(500).status(false).message(e.getMessage()).build();
+ }
+ } catch (Exception e) {
+ return ResponseJson.builder().code(500).status(false).message(e.getMessage()).build();
+ }
+ }
+ }
+}