diff --git a/pom.xml b/pom.xml index d4749c5a..978b3dc3 100644 --- a/pom.xml +++ b/pom.xml @@ -111,6 +111,12 @@ + + + com.google.firebase + firebase-admin + 9.4.3 + org.springframework.boot spring-boot-starter-data-jpa diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties index 2defbace..9eac3447 100644 --- a/src/main/environment/common_ci.properties +++ b/src/main/environment/common_ci.properties @@ -40,6 +40,13 @@ send-message-url=@env.SMS_MESSAGE_URL@ start-sms-scheduler=true cron-scheduler-sms=0 0/1 * * * ? * +# Firebase Configuration +firebase.enabled=@env.FIREBASE_ENABLE@ +# if using file +firebase.credential-file=@env.FIREBASE_CREDENTIAL@ +# for CI/CD +firebase.credential-base64=@env.CREDENTIAL_BASE64@ + #### Email Configuration send-email=@env.SEND_EMAIL@ spring.mail.host=@env.MAIL_HOST@ diff --git a/src/main/environment/common_docker.properties b/src/main/environment/common_docker.properties index 56e2a2fe..7fc32ba3 100644 --- a/src/main/environment/common_docker.properties +++ b/src/main/environment/common_docker.properties @@ -178,4 +178,11 @@ captcha.secret-key=${CAPTCHA_SECRET_KEY} captcha.verify-url=${CAPTCHA_VERIFY_URL} captcha.enable-captcha=${ENABLE_CAPTCHA} -cors.allowed-origins=${CORS_ALLOWED_ORIGINS} \ No newline at end of file +cors.allowed-origins=${CORS_ALLOWED_ORIGINS} + +# Firebase Configuration +firebase.enabled=${FIREBASE_ENABLE} +# if using file +firebase.credential-file=${FIREBASE_CREDENTIAL} +# for CI/CD +firebase.credential-base64=${CREDENTIAL_BASE64} \ No newline at end of file diff --git a/src/main/java/com/iemr/common/CommonApplication.java b/src/main/java/com/iemr/common/CommonApplication.java index 83078018..ea0adb97 100644 --- a/src/main/java/com/iemr/common/CommonApplication.java +++ b/src/main/java/com/iemr/common/CommonApplication.java @@ -21,11 +21,16 @@ */ package com.iemr.common; +import com.google.auth.oauth2.GoogleCredentials; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import com.google.firebase.messaging.FirebaseMessaging; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.context.annotation.Bean; +import org.springframework.core.io.ClassPathResource; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; @@ -36,6 +41,8 @@ import com.iemr.common.data.users.User; import com.iemr.common.utils.IEMRApplBeans; +import java.io.IOException; + @SpringBootApplication @EnableScheduling public class CommonApplication extends SpringBootServletInitializer { @@ -73,4 +80,6 @@ public RedisTemplate redisTemplate(RedisConnectionFactory factor return template; } + } + diff --git a/src/main/java/com/iemr/common/config/firebase/FirebaseMessagingConfig.java b/src/main/java/com/iemr/common/config/firebase/FirebaseMessagingConfig.java new file mode 100644 index 00000000..438ca4f9 --- /dev/null +++ b/src/main/java/com/iemr/common/config/firebase/FirebaseMessagingConfig.java @@ -0,0 +1,57 @@ +package com.iemr.common.config.firebase; + +import com.google.auth.oauth2.GoogleCredentials; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import com.google.firebase.messaging.FirebaseMessaging; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Base64; + +@Configuration +public class FirebaseMessagingConfig { + + @Value("${firebase.enabled:false}") + private boolean firebaseEnabled; + + @Value("${firebase.credential-file:}") + private String firebaseCredentialFile; + + @Value("${firebase.credential-base64:}") + private String firebaseCredentialBase64; + + @Bean + public FirebaseMessaging firebaseMessaging() throws IOException { + if (!firebaseEnabled) { + throw new IllegalStateException("Firebase is disabled"); + } + + GoogleCredentials credentials; + + if (!firebaseCredentialBase64.isBlank()) { + byte[] decoded = Base64.getDecoder().decode(firebaseCredentialBase64); + credentials = GoogleCredentials.fromStream(new ByteArrayInputStream(decoded)); + } else if (!firebaseCredentialFile.isBlank()) { + credentials = GoogleCredentials.fromStream( + new ClassPathResource(firebaseCredentialFile).getInputStream() + ); + } else { + throw new IllegalStateException("No Firebase credentials provided"); + } + + FirebaseOptions options = FirebaseOptions.builder() + .setCredentials(credentials) + .build(); + + FirebaseApp firebaseApp = FirebaseApp.getApps().isEmpty() + ? FirebaseApp.initializeApp(options) + : FirebaseApp.getInstance(); + + return FirebaseMessaging.getInstance(firebaseApp); + } +} diff --git a/src/main/java/com/iemr/common/controller/beneficiaryConsent/BeneficiaryConsentController.java b/src/main/java/com/iemr/common/controller/beneficiaryConsent/BeneficiaryConsentController.java new file mode 100644 index 00000000..e917204c --- /dev/null +++ b/src/main/java/com/iemr/common/controller/beneficiaryConsent/BeneficiaryConsentController.java @@ -0,0 +1,118 @@ +/* + * AMRIT – Accessible Medical Records via Integrated Technology + * Integrated EHR (Electronic Health Records) Solution + * + * Copyright (C) "Piramal Swasthya Management and Research Institute" + * + * This file is part of AMRIT. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ +package com.iemr.common.controller.beneficiaryConsent; + +import com.iemr.common.data.beneficiaryConsent.BeneficiaryConsentRequest; +import com.iemr.common.service.beneficiaryOTPHandler.BeneficiaryOTPHandler; +import com.iemr.common.utils.mapper.InputMapper; +import com.iemr.common.utils.response.OutputResponse; +import io.lettuce.core.dynamic.annotation.Param; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.ws.rs.core.MediaType; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping(value = { "/beneficiaryConsent" },headers ="Authorization" ) +@RestController +public class BeneficiaryConsentController { + final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); + + @Autowired + private BeneficiaryOTPHandler beneficiaryOTPHandler; + + @Operation(summary = "Send Consent") + @RequestMapping(value = "/sendConsent", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON, headers = "Authorization") + public String sendConsent(@Param(value = "{\"mobNo\":\"String\"}") @RequestBody String requestOBJ) { + + OutputResponse response = new OutputResponse(); + + try { + BeneficiaryConsentRequest obj = InputMapper.gson().fromJson(requestOBJ, BeneficiaryConsentRequest.class); + + String success = beneficiaryOTPHandler.sendOTP(obj); // method name unchanged if internal logic still uses 'OTP' + logger.info(success.toString()); + response.setResponse(success); + + + } catch (Exception e) { + response.setError(500, "error : " + e); + } + return response.toString(); + } + + @Operation(summary = "Validate Consent") + @RequestMapping(value = "/validateConsent", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON, headers = "Authorization") + public String validateConsent(@Param(value = "{\"mobNo\":\"String\",\"otp\":\"Integer\"}") @RequestBody String requestOBJ) { + + OutputResponse response = new OutputResponse(); + + try { + BeneficiaryConsentRequest obj = InputMapper.gson().fromJson(requestOBJ, BeneficiaryConsentRequest.class); + + JSONObject responseOBJ = beneficiaryOTPHandler.validateOTP(obj); + if (responseOBJ != null) + response.setResponse(responseOBJ.toString()); + else + response.setError(500, "failure"); + + } catch (Exception e) { + logger.error("error in validating Consent : " + e); + response.setError(500, "error : " + e); + } + return response.toString(); + } + + @Operation(summary = "Resend Consent") + @RequestMapping(value = "/resendConsent", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON, headers = "Authorization") + public String resendConsent(@Param(value = "{\"mobNo\":\"String\"}") @RequestBody String requestOBJ) { + logger.info(requestOBJ.toString()); + + OutputResponse response = new OutputResponse(); + + try { + BeneficiaryConsentRequest obj = InputMapper.gson().fromJson(requestOBJ, BeneficiaryConsentRequest.class); + + String success = beneficiaryOTPHandler.resendOTP(obj); + logger.info(success.toString()); + + if (success.contains("otp")) + response.setResponse(success); + else + response.setError(500, "failure"); + + } catch (Exception e) { + logger.error("error in re-sending Consent : " + e); + response.setError(500, "error : " + e); + } + return response.toString(); + } + + +} + + diff --git a/src/main/java/com/iemr/common/controller/firebaseNotification/FirebaseNotificationController.java b/src/main/java/com/iemr/common/controller/firebaseNotification/FirebaseNotificationController.java new file mode 100644 index 00000000..89fb01a9 --- /dev/null +++ b/src/main/java/com/iemr/common/controller/firebaseNotification/FirebaseNotificationController.java @@ -0,0 +1,61 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +* +/* +* AMRIT – Accessible Medical Records via Integrated Technology +*/ +package com.iemr.common.controller.firebaseNotification; + +import com.iemr.common.model.notification.NotificationMessage; +import com.iemr.common.model.notification.UserToken; +import com.iemr.common.service.firebaseNotification.FirebaseNotificationService; +import com.iemr.common.utils.exception.IEMRException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping(value= "/firebaseNotification",headers = "Authorization") +public class FirebaseNotificationController { + final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); + + @Autowired + FirebaseNotificationService firebaseNotificationService; + + @RequestMapping(value = "sendNotification",method = RequestMethod.POST) + public String sendNotificationByToken(@RequestBody NotificationMessage notificationMessage){ + return firebaseNotificationService.sendNotification(notificationMessage); + } + + @RequestMapping(value = "updateToken",method = RequestMethod.POST) + public String updateToken(@RequestBody UserToken userToken){ + return firebaseNotificationService.updateToken(userToken); + } + + @RequestMapping(value = "getToken",method = RequestMethod.GET,headers = "Authorization") + public String getUserToken() throws IEMRException { + + return firebaseNotificationService.getUserToken(); + } + + +} diff --git a/src/main/java/com/iemr/common/controller/otp/OTPGateway.java b/src/main/java/com/iemr/common/controller/otp/OTPGateway.java index 777503b8..57c1762b 100644 --- a/src/main/java/com/iemr/common/controller/otp/OTPGateway.java +++ b/src/main/java/com/iemr/common/controller/otp/OTPGateway.java @@ -53,6 +53,7 @@ public class OTPGateway { @Operation(summary = "Send OTP") @RequestMapping(value = "/sendOTP", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON, headers = "Authorization") public String sendOTP(@Param(value = "{\"mobNo\":\"String\"}") @RequestBody String requestOBJ) { + logger.info(requestOBJ.toString()); OutputResponse response = new OutputResponse(); @@ -60,7 +61,8 @@ public String sendOTP(@Param(value = "{\"mobNo\":\"String\"}") @RequestBody Stri OTPRequestParsor obj = InputMapper.gson().fromJson(requestOBJ, OTPRequestParsor.class); String success = otpHandler.sendOTP(obj); - if (success.equalsIgnoreCase("success")) + logger.info(success.toString()); + if (success.contains("otp")) response.setResponse(success); else response.setError(5000, "failure"); @@ -98,6 +100,7 @@ public String validateOTP( @Operation(summary = "Resend OTP") @RequestMapping(value = "/resendOTP", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON, headers = "Authorization") public String resendOTP(@Param(value = "{\"mobNo\":\"String\"}") @RequestBody String requestOBJ) { + logger.info(requestOBJ.toString()); OutputResponse response = new OutputResponse(); @@ -105,7 +108,9 @@ public String resendOTP(@Param(value = "{\"mobNo\":\"String\"}") @RequestBody St OTPRequestParsor obj = InputMapper.gson().fromJson(requestOBJ, OTPRequestParsor.class); String success = otpHandler.resendOTP(obj); - if (success.equalsIgnoreCase("success")) + logger.info(success.toString()); + + if (success.contains("otp")) response.setResponse(success); else response.setError(5000, "failure"); diff --git a/src/main/java/com/iemr/common/controller/users/IEMRAdminController.java b/src/main/java/com/iemr/common/controller/users/IEMRAdminController.java index 81acd8ca..4a1af2a2 100644 --- a/src/main/java/com/iemr/common/controller/users/IEMRAdminController.java +++ b/src/main/java/com/iemr/common/controller/users/IEMRAdminController.java @@ -567,7 +567,7 @@ public String getLoginResponse(HttpServletRequest request) { } // Extract user ID from the JWT token - String userId = jwtUtil.getUserIdFromToken(jwtToken); + String userId = jwtUtil.extractUserId(jwtToken); // Get user details and prepare response User user = iemrAdminUserServiceImpl.getUserById(Long.parseLong(userId)); diff --git a/src/main/java/com/iemr/common/data/beneficiaryConsent/BeneficiaryConsentRequest.java b/src/main/java/com/iemr/common/data/beneficiaryConsent/BeneficiaryConsentRequest.java new file mode 100644 index 00000000..ac629a3c --- /dev/null +++ b/src/main/java/com/iemr/common/data/beneficiaryConsent/BeneficiaryConsentRequest.java @@ -0,0 +1,12 @@ +package com.iemr.common.data.beneficiaryConsent; + +import lombok.Data; + +@Data +public class BeneficiaryConsentRequest { + private String mobNo; + private int otp; + private String userName; + private String designation; + +} diff --git a/src/main/java/com/iemr/common/data/userToken/UserTokenData.java b/src/main/java/com/iemr/common/data/userToken/UserTokenData.java new file mode 100644 index 00000000..0646972e --- /dev/null +++ b/src/main/java/com/iemr/common/data/userToken/UserTokenData.java @@ -0,0 +1,46 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +* +/* +* AMRIT – Accessible Medical Records via Integrated Technology +*/ +package com.iemr.common.data.userToken; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Data; + +import java.sql.Timestamp; + +@Entity +@Table(name = "user_tokens", schema = "db_iemr") +@Data +public class UserTokenData { + @Id + @Column(name = "user_id") + Integer userId; + @Column(name = "token") + String token; + @Column(name = "updated_at") + Timestamp updatedAt; +} diff --git a/src/main/java/com/iemr/common/dto/identity/CommonIdentityDTO.java b/src/main/java/com/iemr/common/dto/identity/CommonIdentityDTO.java index 431a267a..8e364eb3 100644 --- a/src/main/java/com/iemr/common/dto/identity/CommonIdentityDTO.java +++ b/src/main/java/com/iemr/common/dto/identity/CommonIdentityDTO.java @@ -54,6 +54,8 @@ public class CommonIdentityDTO { private Integer beneficiaryRegId; private Integer communityId; private String community; + private Boolean isConsent=false; + private Timestamp dob; private Integer ageAtMarriage; private Integer educationId; diff --git a/src/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.java b/src/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.java index aee6b8f7..a6bd9eca 100644 --- a/src/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.java +++ b/src/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.java @@ -77,6 +77,10 @@ public class BeneficiaryModel implements Comparable { // private List outboundCallRequests; // private List beneficiaryCalls; // private List feedbacks; + @Expose + private Boolean isConsent=false; + + @Expose private String beneficiaryID; @Expose @@ -87,6 +91,8 @@ public class BeneficiaryModel implements Comparable { private TitleModel m_title; @Expose private String firstName; + + @Expose private String middleName; @Expose diff --git a/src/main/java/com/iemr/common/model/notification/NotificationMessage.java b/src/main/java/com/iemr/common/model/notification/NotificationMessage.java new file mode 100644 index 00000000..58343034 --- /dev/null +++ b/src/main/java/com/iemr/common/model/notification/NotificationMessage.java @@ -0,0 +1,38 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +* +/* +* AMRIT – Accessible Medical Records via Integrated Technology +*/ +package com.iemr.common.model.notification; + +import lombok.Data; + +import java.util.Map; + +@Data +public class NotificationMessage { + private String appType; + private String token; + private String title; + private String body; + private Map data; +} diff --git a/src/main/java/com/iemr/common/model/notification/UserToken.java b/src/main/java/com/iemr/common/model/notification/UserToken.java new file mode 100644 index 00000000..3ea36588 --- /dev/null +++ b/src/main/java/com/iemr/common/model/notification/UserToken.java @@ -0,0 +1,33 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +* +/* +* AMRIT – Accessible Medical Records via Integrated Technology +*/ +package com.iemr.common.model.notification; + +import lombok.Data; + +@Data +public class UserToken { + Integer userId; + String token; +} diff --git a/src/main/java/com/iemr/common/repo/userToken/UserTokenRepo.java b/src/main/java/com/iemr/common/repo/userToken/UserTokenRepo.java new file mode 100644 index 00000000..817f26f0 --- /dev/null +++ b/src/main/java/com/iemr/common/repo/userToken/UserTokenRepo.java @@ -0,0 +1,31 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +* +/* +* AMRIT – Accessible Medical Records via Integrated Technology +*/ +package com.iemr.common.repo.userToken; + +import com.iemr.common.data.userToken.UserTokenData; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface UserTokenRepo extends JpaRepository { +} diff --git a/src/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.java b/src/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.java index 1d829d17..9e8dcb0d 100644 --- a/src/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.java +++ b/src/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.java @@ -37,7 +37,6 @@ import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.iemr.common.data.beneficiary.Beneficiary; import com.iemr.common.data.mctshistory.MctsOutboundCallDetail; @@ -77,6 +76,8 @@ public class RegisterBenificiaryServiceImpl implements RegisterBenificiaryServic @Autowired Validator validator; + + @Autowired OutboundHistoryRepository outboundHistoryRepository; @@ -163,13 +164,15 @@ private int updateDemographics(BeneficiaryDemographicsModel i_BenDemographics) { @Override public String save(BeneficiaryModel beneficiaryModel, HttpServletRequest servletRequest) throws Exception { - // logger.info("benificiaryDetails: " + beneficiaryModel); + logger.info("benificiaryDetails: " + beneficiaryModel); CommonIdentityDTO identityDTO = identityMapper.beneficiaryModelCommonIdentityDTO(beneficiaryModel); setSaveDemographicDetails(identityDTO,beneficiaryModel); // identityDTO.setOtherFields(beneficiaryModel.getOtherFields()); + identityDTO.setIsConsent(beneficiaryModel.getIsConsent()); identityDTO.setFaceEmbedding(beneficiaryModel.getFaceEmbedding()); identityDTO.setEmergencyRegistration(beneficiaryModel.isEmergencyRegistration()); + identityDTO.setIsConsent(beneficiaryModel.getIsConsent()); identityDTO .setBenFamilyDTOs(identityMapper.benPhoneMapListToBenFamilyDTOList(beneficiaryModel.getBenPhoneMaps())); String request = new Gson().toJson(identityDTO); diff --git a/src/main/java/com/iemr/common/service/beneficiaryOTPHandler/BeneficiaryOTPHandler.java b/src/main/java/com/iemr/common/service/beneficiaryOTPHandler/BeneficiaryOTPHandler.java new file mode 100644 index 00000000..234640a8 --- /dev/null +++ b/src/main/java/com/iemr/common/service/beneficiaryOTPHandler/BeneficiaryOTPHandler.java @@ -0,0 +1,14 @@ +package com.iemr.common.service.beneficiaryOTPHandler; + +import com.iemr.common.data.beneficiaryConsent.BeneficiaryConsentRequest; +import com.iemr.common.data.otp.OTPRequestParsor; +import org.json.JSONObject; + +public interface BeneficiaryOTPHandler { + public String sendOTP(BeneficiaryConsentRequest obj) throws Exception; + + public JSONObject validateOTP(BeneficiaryConsentRequest obj) throws Exception; + + public String resendOTP(BeneficiaryConsentRequest obj) throws Exception; + +} diff --git a/src/main/java/com/iemr/common/service/beneficiaryOTPHandler/BeneficiaryOTPHandlerImpl.java b/src/main/java/com/iemr/common/service/beneficiaryOTPHandler/BeneficiaryOTPHandlerImpl.java new file mode 100644 index 00000000..17027667 --- /dev/null +++ b/src/main/java/com/iemr/common/service/beneficiaryOTPHandler/BeneficiaryOTPHandlerImpl.java @@ -0,0 +1,209 @@ +/* + * AMRIT – Accessible Medical Records via Integrated Technology + * Integrated EHR (Electronic Health Records) Solution + * + * Copyright (C) "Piramal Swasthya Management and Research Institute" + * + * This file is part of AMRIT. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ +package com.iemr.common.service.beneficiaryOTPHandler; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.primitives.Ints; +import com.iemr.common.data.beneficiaryConsent.BeneficiaryConsentRequest; +import com.iemr.common.data.otp.OTPRequestParsor; +import com.iemr.common.data.sms.SMSTemplate; +import com.iemr.common.data.sms.SMSType; +import com.iemr.common.repository.sms.SMSTemplateRepository; +import com.iemr.common.repository.sms.SMSTypeRepository; +import com.iemr.common.service.otp.OTPHandler; +import com.iemr.common.service.users.IEMRAdminUserServiceImpl; +import com.iemr.common.utils.config.ConfigProperties; +import com.iemr.common.utils.http.HttpUtils; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.util.*; +import java.util.concurrent.TimeUnit; + +@Service +public class BeneficiaryOTPHandlerImpl implements BeneficiaryOTPHandler { + + @Autowired + HttpUtils httpUtils; + @Autowired + private IEMRAdminUserServiceImpl iEMRAdminUserServiceImpl; + + final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); + @Autowired + SMSTemplateRepository smsTemplateRepository; + private LoadingCache otpCache; + + @Autowired + SMSTypeRepository smsTypeRepository; + + private static final Integer EXPIRE_MIN = 5; + private static final String SMS_GATEWAY_URL = ConfigProperties.getPropertyByName("sms-gateway-url"); + + // Constructor for new object creation + public BeneficiaryOTPHandlerImpl() { + otpCache = CacheBuilder.newBuilder().expireAfterWrite(EXPIRE_MIN, TimeUnit.MINUTES) + .build(new CacheLoader() { + public String load(String key) { + return "0"; + } + }); + } + + /*** + * @param obj + * @return success if OTP sent successfully + */ + @Override + public String sendOTP(BeneficiaryConsentRequest obj) throws Exception { + int otp = generateOTP(obj.getMobNo()); + return sendSMS(otp, obj); + } + + /*** + * @param obj + * @return OTP verification success or failure + * + */ + @Override + public JSONObject validateOTP(BeneficiaryConsentRequest obj) throws Exception { + String cachedOTP = otpCache.get(obj.getMobNo()); + String inputOTPEncrypted = getEncryptedOTP(obj.getOtp()); + + if (cachedOTP.equalsIgnoreCase(inputOTPEncrypted)) { + JSONObject responseObj = new JSONObject(); + responseObj.put("userName", obj.getMobNo()); + responseObj.put("userID", obj.getMobNo()); + + JSONObject responseOBJ = iEMRAdminUserServiceImpl.generateKeyPostOTPValidation(responseObj); + + return responseOBJ; + } else { + throw new Exception("Please enter valid OTP"); + } + + } + + /*** + * @param obj + * @return success if OTP re-sent successfully + */ + @Override + public String resendOTP(BeneficiaryConsentRequest obj) throws Exception { + int otp = generateOTP(obj.getMobNo()); + return sendSMS(otp, obj); + } + + // generate 6 digit random no # + public int generateOTP(String authKey) throws Exception { + String generatedPassword = null; + Random random = SecureRandom.getInstanceStrong(); + int otp = 100000 + random.nextInt(900000); + + generatedPassword = getEncryptedOTP(otp); + + if (otpCache != null) + otpCache.put(authKey, generatedPassword); + else { + BeneficiaryOTPHandlerImpl obj = new BeneficiaryOTPHandlerImpl(); + obj.otpCache.put(authKey, generatedPassword); + } + return otp; + } + + // SHA-256 encoding logic implemented + private String getEncryptedOTP(int otp) throws Exception { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte[] bytes = md.digest(Ints.toByteArray(otp)); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < bytes.length; i++) { + sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1)); + } + + return sb.toString(); + } + + // send SMS to beneficiary + + + public String sendSMS(int otp, BeneficiaryConsentRequest obj) { + + final RestTemplate restTemplate = new RestTemplate(); + + String dltTemplateId = smsTemplateRepository.findDLTTemplateID(28); + SMSTemplate template = smsTemplateRepository.findBySmsTemplateID(28); + + String sendSMSAPI = BeneficiaryOTPHandlerImpl.SMS_GATEWAY_URL; + + try { + String message = template.getSmsTemplate() + .replace("$$OTP$$",String.valueOf(otp)) + .replace("$$UserName$$", obj.getUserName()) + .replace("$$Designation$$", obj.getDesignation()); + + // Build payload + Map payload = new HashMap<>(); + payload.put("customerId", ConfigProperties.getPropertyByName("sms-username")); + payload.put("destinationAddress", obj.getMobNo()); + payload.put("message", message); + payload.put("sourceAddress", ConfigProperties.getPropertyByName("source-address")); + payload.put("messageType", "SERVICE_IMPLICIT"); + payload.put("dltTemplateId", dltTemplateId); + payload.put("entityId",ConfigProperties.getPropertyByName("sms-entityid") ); + payload.put("otp", true); + // Set headers + HttpHeaders headers = new HttpHeaders(); + String auth = ConfigProperties.getPropertyByName("sms-username") + ":" + ConfigProperties.getPropertyByName("sms-password"); + headers.add("Authorization", + "Basic " + Base64.getEncoder().encodeToString(auth.getBytes())); + + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> request = new HttpEntity<>(payload, headers); + + // Call API + ResponseEntity response = restTemplate.postForEntity(sendSMSAPI, request, String.class); + logger.info("sms-response:"+response.getBody()); + if(response.getStatusCode().value()==200){ + return "OTP sent successfully on register mobile number"; + }else { + return "Fail"; + + } + + } catch (Exception e) { + return "Error sending SMS: " + e.getMessage(); + } + } + +} diff --git a/src/main/java/com/iemr/common/service/firebaseNotification/FirebaseNotificationService.java b/src/main/java/com/iemr/common/service/firebaseNotification/FirebaseNotificationService.java new file mode 100644 index 00000000..ea0cc005 --- /dev/null +++ b/src/main/java/com/iemr/common/service/firebaseNotification/FirebaseNotificationService.java @@ -0,0 +1,112 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +* +/* +* AMRIT – Accessible Medical Records via Integrated Technology +*/ +package com.iemr.common.service.firebaseNotification; + +import com.google.firebase.FirebaseException; +import com.google.firebase.messaging.FirebaseMessaging; +import com.google.firebase.messaging.Message; +import com.google.firebase.messaging.Notification; +import com.iemr.common.data.userToken.UserTokenData; +import com.iemr.common.model.notification.NotificationMessage; +import com.iemr.common.model.notification.UserToken; +import com.iemr.common.repo.userToken.UserTokenRepo; +import com.iemr.common.utils.CookieUtil; +import com.iemr.common.utils.JwtUtil; +import com.iemr.common.utils.exception.IEMRException; +import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.sql.Timestamp; +import java.util.Optional; + +@Service +public class FirebaseNotificationService { + final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); + + @Autowired + FirebaseMessaging firebaseMessaging; + + @Autowired + private UserTokenRepo userTokenRepo; + + @Autowired + private CookieUtil cookieUtil; + + @Autowired + private JwtUtil jwtUtil; + + + public String sendNotification(NotificationMessage notificationMessage) { + + Notification notification = Notification.builder().setTitle(notificationMessage.getTitle()).setBody(notificationMessage.getBody()).build(); + + Message message = Message.builder().setToken(notificationMessage.getToken()).setNotification(notification).putAllData(notificationMessage.getData()).build(); + + + try { + String response = FirebaseMessaging.getInstance().send(message); + + return response; + } catch (FirebaseException e) { + return "Error sending notification"; + + } + } + + public String updateToken(UserToken userToken) { + Optional existingTokenData = userTokenRepo.findById(userToken.getUserId()); + + UserTokenData userTokenData; + + if (existingTokenData.isPresent()) { + userTokenData = existingTokenData.get(); + userTokenData.setToken(userToken.getToken()); + userTokenData.setUpdatedAt(new Timestamp(System.currentTimeMillis())); + } else { + userTokenData = new UserTokenData(); + userTokenData.setUserId(userToken.getUserId()); + userTokenData.setToken(userToken.getToken()); + userTokenData.setUpdatedAt(new Timestamp(System.currentTimeMillis())); + } + + userTokenRepo.save(userTokenData); + return "Save Successfully"; + } + + public String getUserToken() throws IEMRException { + HttpServletRequest requestHeader = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()) + .getRequest(); + String jwtTokenFromCookie = cookieUtil.getJwtTokenFromCookie(requestHeader); + return userTokenRepo.findById(Integer.parseInt(jwtUtil.extractUserId(jwtTokenFromCookie))) // because your userId is Long in DB + .map(UserTokenData::getToken) + .orElse(null); // + } + +} diff --git a/src/main/java/com/iemr/common/service/notification/NotificationService.java b/src/main/java/com/iemr/common/service/notification/NotificationService.java index a3c85387..6fa8ee8d 100644 --- a/src/main/java/com/iemr/common/service/notification/NotificationService.java +++ b/src/main/java/com/iemr/common/service/notification/NotificationService.java @@ -55,4 +55,5 @@ String createEmergencyContacts(String request) String updateEmergencyContacts(String request) throws JSONException, NoSuchAlgorithmException, IOException, IEMRException, Exception; + } diff --git a/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java b/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java index 0b8f6f94..27fbf771 100644 --- a/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java +++ b/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java @@ -59,6 +59,7 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo String contextPath = request.getContextPath(); logger.info("JwtUserIdValidationFilter invoked for path: " + path); + // Log cookies for debugging Cookie[] cookies = request.getCookies(); if (cookies != null) { diff --git a/src/main/java/com/iemr/common/utils/JwtUtil.java b/src/main/java/com/iemr/common/utils/JwtUtil.java index dc29018c..0ea70bda 100644 --- a/src/main/java/com/iemr/common/utils/JwtUtil.java +++ b/src/main/java/com/iemr/common/utils/JwtUtil.java @@ -1,164 +1,106 @@ package com.iemr.common.utils; -import io.jsonwebtoken.*; -import io.jsonwebtoken.security.Keys; +import java.util.Date; +import java.util.UUID; +import java.util.function.Function; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.UnsupportedJwtException; +import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SignatureException; + import javax.crypto.SecretKey; -import java.util.Date; -import java.util.UUID; -import java.util.function.Function; @Component public class JwtUtil { - @Value("${jwt.secret}") - private String SECRET_KEY; - - @Value("${jwt.access.expiration}") - private long ACCESS_EXPIRATION_TIME; - - @Value("${jwt.refresh.expiration}") - private long REFRESH_EXPIRATION_TIME; - - @Autowired - private TokenDenylist tokenDenylist; - - private SecretKey getSigningKey() { - if (SECRET_KEY == null || SECRET_KEY.isEmpty()) { - throw new IllegalStateException("JWT secret key is not set in application.properties"); - } - return Keys.hmacShaKeyFor(SECRET_KEY.getBytes()); - } - - /** - * Generate an access token. - * - * @param username the username of the user - * @param userId the user ID - * @return the generated JWT access token - */ - public String generateToken(String username, String userId) { - return buildToken(username, userId, "access", ACCESS_EXPIRATION_TIME); - } - - /** - * Generate a refresh token. - * - * @param username the username of the user - * @param userId the user ID - * @return the generated JWT refresh token - */ - public String generateRefreshToken(String username, String userId) { - return buildToken(username, userId, "refresh", REFRESH_EXPIRATION_TIME); - } - - /** - * Build a JWT token with the specified parameters. - * - * @param username the username of the user - * @param userId the user ID - * @param tokenType the type of the token (access or refresh) - * @param expiration the expiration time of the token in milliseconds - * @return the generated JWT token - */ - private String buildToken(String username, String userId, String tokenType, long expiration) { - return Jwts.builder() - .subject(username) - .claim("userId", userId) - .claim("token_type", tokenType) - .id(UUID.randomUUID().toString()) - .issuedAt(new Date()) - .expiration(new Date(System.currentTimeMillis() + expiration)) - .signWith(getSigningKey()) - .compact(); - } - - /** - * Validate the JWT token, checking if it is expired and if it's blacklisted - * @param token the JWT token - * @return Claims if valid, null if invalid (expired or denylisted) - */ - public Claims validateToken(String token) { - try { - Claims claims = Jwts.parser().verifyWith(getSigningKey()).build().parseSignedClaims(token).getPayload(); - String jti = claims.getId(); - - // Check if token is denylisted (only if jti exists) - if (jti != null && tokenDenylist.isTokenDenylisted(jti)) { - return null; - } - - return claims; - } catch (ExpiredJwtException ex) { - - return null; // Token is expired, so return null - } catch (UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException ex) { - return null; // Return null for any other JWT-related issue (invalid format, bad signature, etc.) - } - } - - /** - * Extract claims from the token - * @param token the JWT token - * @return all claims from the token - */ - public Claims getAllClaimsFromToken(String token) { - return Jwts.parser() + @Value("${jwt.secret}") + private String SECRET_KEY; + + @Value("${jwt.access.expiration}") + private long ACCESS_EXPIRATION_TIME; + + @Value("${jwt.refresh.expiration}") + private long REFRESH_EXPIRATION_TIME; + + private SecretKey getSigningKey() { + if (SECRET_KEY == null || SECRET_KEY.isEmpty()) { + throw new IllegalStateException("JWT secret key is not set in application.properties"); + } + return Keys.hmacShaKeyFor(SECRET_KEY.getBytes()); + } + + public String generateToken(String username, String userId) { + return buildToken(username, userId, "access", ACCESS_EXPIRATION_TIME); + } + + public String generateRefreshToken(String username, String userId) { + return buildToken(username, userId, "refresh", REFRESH_EXPIRATION_TIME); + } + + private String buildToken(String username, String userId, String tokenType, long expiration) { + return Jwts.builder() + .subject(username) + .claim("userId", userId) + .claim("token_type", tokenType) + .id(UUID.randomUUID().toString()) + .issuedAt(new Date()) + .expiration(new Date(System.currentTimeMillis() + expiration)) + .signWith(getSigningKey()) + .compact(); + } + + public Claims validateToken(String token) { + try { + return Jwts.parser() + .verifyWith(getSigningKey()) + .build() + .parseSignedClaims(token) + .getPayload(); + + } catch (ExpiredJwtException ex) { + // Handle expired token specifically if needed + } catch (UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException ex) { + // Log specific error types + } + return null; + } + + public T getClaimFromToken(String token, Function claimsResolver) { + final Claims claims = getAllClaimsFromToken(token); + return claimsResolver.apply(claims); + } + + public Claims getAllClaimsFromToken(String token) { + return Jwts.parser() .verifyWith(getSigningKey()) .build() .parseSignedClaims(token) .getPayload(); + } + + + public long getRefreshTokenExpiration() { + return REFRESH_EXPIRATION_TIME; + } + + // Additional helper methods + public String getJtiFromToken(String token) { + return getAllClaimsFromToken(token).getId(); + } + + public String getUsernameFromToken(String token) { + return getAllClaimsFromToken(token).getSubject(); + } + + public String extractUserId(String token) { + return getAllClaimsFromToken(token).getId(); - } - - /** - * Extract a specific claim from the token using a function - * @param token the JWT token - * @param claimsResolver the function to extract the claim - * @param the type of the claim - * @return the extracted claim - */ - public T getClaimFromToken(String token, Function claimsResolver) { - final Claims claims = getAllClaimsFromToken(token); - return claimsResolver.apply(claims); - } - - /** - * Get the JWT ID (JTI) from the token - * @param token the JWT token - * @return the JWT ID - */ - public String getJtiFromToken(String token) { - return getAllClaimsFromToken(token).getId(); - } - - /** - * Get the username from the token - * @param token the JWT token - * @return the username - */ - public String getUsernameFromToken(String token) { - return getAllClaimsFromToken(token).getSubject(); - } - - /** - * Get the user ID from the token - * @param token the JWT token - * @return the user ID - */ - public String getUserIdFromToken(String token) { - return getAllClaimsFromToken(token).get("userId", String.class); - } - - /** - * Get the expiration time of the refresh token - * @return the expiration time in milliseconds - */ - public long getRefreshTokenExpiration() { - return REFRESH_EXPIRATION_TIME; - } -} + } +} \ No newline at end of file